python库源码分析_python第三方库Faker源码解读
源码背景
Faker是一个Python第三方库,GITHUB开源项目,主要用于创建伪数据创建的数据包含地理信息类、基础信息类、个人账户信息类、网络基础信息类、浏览器信息类、文件信息类、数字类 文本加密类、时间信息类、其他类别等。
源码解读
主源码解读
通过直接点击初始化的类进入类初始化模块
fake = Faker(locale='zh_CN')
核心的源码搬运如下:
proxy.py文件
from __future__ import absolute_import, unicode_literals
from collections import OrderedDict
import random
import re
import six
from faker.config import DEFAULT_LOCALE
from faker.factory import Factory
from faker.generator import Generator
from faker.utils.distribution import choices_distribution
class Faker(object):
"""Proxy class capable of supporting multiple locales"""
cache_pattern = re.compile(r'^_cached_\w*_mapping$')
generator_attrs = [
attr for attr in dir(Generator)
if not attr.startswith('__')
and attr not in ['seed', 'seed_instance', 'random']
]
def __init__(self, locale=None, providers=None,
generator=None, includes=None, **config):
self._factory_map = OrderedDict()
self._weights = None
if isinstance(locale, six.string_types):
locales = [locale.replace('-', '_')]
# This guarantees a FIFO ordering of elements in `locales` based on the final
# locale string while discarding duplicates after processing
elif isinstance(locale, (list, tuple, set)):
assert all(isinstance(l, six.string_types) for l in locale)
locales = []
for l in locale:
final_locale = l.replace('-', '_')
if final_locale not in locales:
locales.append(final_locale)
elif isinstance(locale, OrderedDict):
assert all(isinstance(v, (int, float)) for v in locale.values())
odict = OrderedDict()
for k, v in locale.items():
key = k.replace('-', '_')
odict[key] = v
locales = list(odict.keys())
self._weights = list(odict.values())
else:
locales = [DEFAULT_LOCALE]
for locale in locales:
self._factory_map[locale] = Factory.create(locale, providers, generator, includes, **config)
self._locales = locales
self.AQ_factories = list(self._factory_map.values())
文件主要引入了内部类以及collections.OrderedDict,random,six等外部包。
以下对相应的关键外部包做个说明:
1、collections.OrderedDict实现了对字典对象中元素的排序,由于python的字典是按照hash值进行存储的所以,导致字典是无序状态,OrderedDict实现了对字典对象中元素的排序
2、six这个名字来源于 6 = 2 x 3,其产生主要是为了解决Python2 和 Python3 代码兼容性
初始化的开头按一定规则将Generator类下的属性保存在generator_attrs中,以备后续方法调用。在init的初始化中规定了类的入参有哪些?同时定义了两个名义上的私有变量,来防止外部调用类方法,说他是名义上的私有变量是因为python中没有真正的私有化,不管是方法还是属性,为了编程的需要,约定加了下划线 _的属性和方法不属于API,不应该在类的外面访问,也不会被from M import * 导入。但是注意你想调用也可以调用。self._factory_map中保存的是OrderedDict()的实例化对象;self._weights为了保证其在类中被调用,赋予初始化的None值。接下来的是对入参locale的条件判断,示意图基本如下:
proxy.py文件
if isinstance(locale, six.string_types):
如果入参的locale是字符串,则替换-线为_线,保存在locales中
elif isinstance(locale, (list, tuple, set)):
如果入参的locale是列表,元祖,集合,则遍历入参判断元素为字符串后将元素替换-线为_线保存在locales中
elif isinstance(locale, OrderedDict):
如果入参的locale是有序字典,则遍历入参判断键为字符串后将键替换-线为_线保存在locales中,将键的值保存在之前定义的self._weights中
locales = list(odict.keys())
self._weights = list(odict.values())
else:
以上条件都不满足时,将配置文件中自定义的locale保存到列表中赋值给locales
为什么在locale这个入参要做那么多的校验呢,是因为在初始化是locale做了一件很重要的事,而这件事对locale的要求很高,具体来看源码:
proxy.py文件
for locale in locales:
self._factory_map[locale] = Factory.create(locale, providers, generator, includes, **config)
源码在这里主要做了对每种语言创建了一个map字典,里面涉及到了Factory工厂模式下的创建方法,入参基本为当前类的入参。那么Faker除了对locale入参进行了校验外,有没有做其他的校验呢?答案是肯定的在对关键属性self._weights、self._factories、self._factory_map.items(),通过object下的@property装饰器进行了只读的校验,外部修改。
魔法方法解读
对类的实例化后需要使用实例里面的属性,那么为了增加其扩展性加了getitem的魔法方法使的我们可以对Fake()['pujen']操作,那么在Faker中Fake()['pujen']会返回啥呢,源码中运算结果为KeyError,当然了因为Faker中没有pujen这个语言包。
proxy.py文件
def __getitem__(self, locale):
return self._factory_map[locale.replace('-', '_')]
fake = Faker(locale='zh_CN')
print(fake['zh_CN'])
>>>
接下来看一个实例来更好的去理解什么是getitem魔法方法
class Fake(object):
def __init__(self):
self.name = 'jack'
def __getitem__(self,item):
if item in self.__dict__: # item = key,判断该key是否存在对象的 __dict__ 里,
return self.__dict__[item] # 返回该对象 __dict__ 里key对应的value
def __setitem__(self, key, value):
self.__dict__[key] = value # 在对象 __dict__ 为指定的key设置value
def __delitem__(self, key):
del self.__dict__[key] # 在对象 __dict__ 里删除指定的key
f1 = Fake()
print(f1['name']) # jack
f1['age'] =10
print(f1['age']) # 10
del f1['name']
print(f1.__dict__) # {'age': 10}
接下来看一下getattribute__方法,这个方法出现在这个类中主要是因为防止seed()方法的直接调用而是要形如Faker.seed()这样的调用,在Faker的源码中seed()实际是Generator.seed()一种随机种子函数。假设调用类的方法中不是seed()而是其他非此类方法,那么会执行__getattr方法,这个方法在Faker里面主要是干了什么呢:
proxy.py文件
def __getattr__(self, attr):
"""
Handles cache access and proxying behavior
:param attr: attribute name
:return: the appropriate attribute
"""
条件语句判断异常情况,最后走如下代码
factory = self._select_factory(attr)
return getattr(factory, attr)
工厂模式
在初始化中我们会发现核心的内容最后都是由工厂模式的Factory.create()创建接下来看一下此工厂函数。在Factory中create()是以静态类方法来体现
factory.py文件
@classmethod
def create(
cls,
locale=None,
providers=None,
generator=None,
includes=None,
**config):
if includes is None:
includes = []
# fix locale to package name
locale = locale.replace('-', '_') if locale else DEFAULT_LOCALE
locale = pylocale.normalize(locale).split('.')[0]#返回规范化的语言环境代码
if locale not in AVAILABLE_LOCALES:
msg = 'Invalid configuration for faker locale `{0}`'.format(locale)
raise AttributeError(msg)
config['locale'] = locale
providers = providers or PROVIDERS#排序的集合
providers += includes
faker = generator or Generator(**config)
for prov_name in providers:
if prov_name == 'faker.providers':
continue
prov_cls, lang_found = cls._get_provider_class(prov_name, locale)#prov_cls=faker.providers,lang_found语言包名称
provider = prov_cls(faker)#继承在Generator类中
provider.__provider__ = prov_name
provider.__lang__ = lang_found
faker.add_provider(provider)#增加类的方法和属性
return faker
从上面的源码可以梳理出来,基本就是给类增加方法和规范一下语言包。我们对里面的一些细节代码梳理一下:
factory.py文件
1、
providers += includes
providers是一个空列表
includes是一个集合数据
那么假设providers=[],includes={1,2,3,4}
则providers += includes运行结果,会使的providers=[1,2,3,4],实际这段代码就是将集合的数据放到空列表中。
2、
faker = generator or Generator(**config)
provider = prov_cls(faker)
这里faker是generator类,prov_cls实际上是一个类,那么prov_cls(faker)实际就是继承了Generator类
3、
provider.__provider__ = prov_name
provider.__lang__ = lang_found
faker.add_provider(provider)#增加类的方法和属性
给这些类赋予方法名和语言包,同时通过魔法方法增加类的方法和属性,这里面涉及到Generator.add_provider()方法
Faker隐藏主方法类
以上工厂模式中create()主函数方法基本也介绍完了,类内部的其他方法暂时不过多的研究。接下来看一下在create()中涉及到的Generator.add_provider()方法,方法的源码如下:
generator.py文件
def add_provider(self, provider):
if isinstance(provider, type):
provider = provider(self)
self.providers.insert(0, provider)#将provider插入到0索引位置
for method_name in dir(provider):
# skip 'private' method
if method_name.startswith('_'):
continue
faker_function = getattr(provider, method_name)#动态运行函数
if callable(faker_function):#函数用于检查一个对象是否是可调用的
# add all faker method to generator
self.set_formatter(method_name, faker_function)
针对如下的这个用法做一下基本的说明,后续我们写代码的时候可以作为借鉴
if isinstance(provider, type):
说明:如果对象参数是classinfo参数的实例,或者是它的一个(直接、间接或虚拟)子类的实例,则返回True。如果对象不是给定类型的对象,则该函数始终返回False。如果classinfo是类型对象的元组(或者递归地,其他类似的元组),如果对象是任何类型的实例,则返回True。如果classinfo不是类型的类型或元组,而这些元组又不是类型的元组,则会引发类型错误异常。
for method_name in dir(provider):
dir的用法说明,如果provider类或者模块没有定义dir方法则返回类或者模块的方法属性
接下来看一下这两个方法,主要是用于动态调用函数返回运行对象
faker_function = getattr(provider, method_name)#动态运行函数
if callable(faker_function):#函数用于检查一个对象是否是可调用的
至此,Generator类中的核心方法介绍完成!
Faker里方法运行内部逻辑
当我们在pycharm里面写好方法打算去看一下类函数时,Ctrl+鼠标左击。奇怪的事情发生了,并没有进入到对应的方法里面去,同时pycharm智能提示我们:
fake = Faker(locale='zh_CN')
fake.random_digit_not_null()
通过上面的源码解析也可以很清晰的发现,Faker的方法和属性不像我们往常写的类一样在类的下面,全文解析基本没看到创建伪数据的直接方法和属性。那么下面来看一下方法的基本运行内部逻辑实现方式。
如图,在内部运行逻辑中实际上调用的是generator.py文件内容下的Generator.add_provider方法中有一个需要特别注意就是法,上面我们也提到了,在add_provider方法中有一个需要特别注意就是
for method_name in dir(provider):
通过这个基本的循环将所有的方法和属性加载到对应的语言包中,也就说Faker的属性和方法实际是在另外一个地方存放着,在使用的时候在拿过来,这样做使的Faker的本身类看起来简洁。那么外部是以什么形式来存放的呢?
可以看出在外部有一个provider包,包里面对应很多个方法归类包,在往内部层级就是对应每个语言包下的方法。来看一下具体方法的内部表现形式是如何的
可以发现基本是以元祖的方式存放的原始数据,我们方法运行后最终的结果都是来自于此,那么函数最后是如何运行方法的呢?其实最上面的源码解析已经提及到了,就是使用了init()下的
return getattr(factory, attr)
具体到每个方法或者函数上的实现方式由于太多了就不一一解读了,大范围的是使用random这个基本库来实现的。
文章原创首发于微信公众号 软件测试微课堂
python库源码分析_python第三方库Faker源码解读相关推荐
- 用于安装python第三方库的工具是_Python第三方库安装
Python有一个全球社区:在这里,我们可以搜索Python第三方库的任何话题.PyPI的全称是Python包指数指Python包的指数.它是由PSF (Python软件基金会)和显示全球Python ...
- python扩展库xlwt支持对_python第三方库——xlrd和xlwt操作Excel文件学习
一.xlrd和xlwt的安装 xlrd和xlwt是python的第三方库,所以是需要自己安装的,可以在python的官网https://pypi.python.org/pypi下载该模块来安装,也可以 ...
- 20个必不可少的Python库也是基本的第三方库(转载)
20个必不可少的Python库也是基本的第三方库 读者您好.今天我将介绍20个属于我常用工具的Python库,我相信你看完之后也会觉得离不开它们.他们是: Requests.Kenneth Reitz ...
- qpython3h第三方库安装_Python第三方库安装
Python有一个全球社区:https://pypi.org/,在这里我们可以搜索任何主题的Python第三方库.PyPI全称是Python Package Index,指的是Python包的索引,它 ...
- 不是python中用于开发用户界面的第三方库-模拟试卷C
原标题:模拟试卷C 一.单项选择题 1. 按照"后进先出"原则组织数据的数据结构是____ 队列 栈 双向链表 二叉树 2. 以下选项的叙述中,正确的是 循环队列有队头和队尾两个指 ...
- 不是python中用于开发用户界面的第三方库-python界面 | Tkinter图形界面开发库
0 写在前面 未经允许,不得转载,谢谢~~ 毕设要在现有的基础上做一个可视化的界面,所以趁机也学习一波如何用python实现图形界面的开发. 本文主要学习并整理了: 简要介绍用于python图形界面开 ...
- Python在指定环境下安装第三方库的报错解决办法
Python在指定环境下安装第三方库的报错解决办法 在python安装第三方库时,如果直接打开cmd命令提示符,并输入下列安装命令,则会默认安装在base环境下 但base环境下的包新建的虚拟环境是无 ...
- Android源码分析(十一)-----Android源码中如何引用aar文件
一:aar文件如何引用 系统Settings中引用bidehelper-1.1.12.aar 文件为例 源码地址:packages/apps/Settings/Android.mk LOCAL_PAT ...
- 十年老架构师神级推荐,MyBatis源码分析,再也不用为源码担忧了
十年老架构师神级推荐,MyBatis源码分析,再也不用为源码担忧了 前言 MyBatis是一个优秀的持久层ORM框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL 本身,而不需要花 ...
最新文章
- 制作欧比旺·克诺比逼真的CG角色学习教程
- select2 4.0.8 + , 动态搜索数据
- html head 全局变量,Javascript全局变量的使用方法
- 怎么样自己动手写OS
- Spring Boot-------JPA——EntityManager构建通用DAO
- DB and RAC(11.2.0.3 ) Patch Set Update(11.2.0.3.6 )
- hack (浏览器兼容css hack)
- 软件成分分析(SCA)完全指南
- rep的软件用什么打开_rep文件用什么软件打开
- 调度工具之Azkaban 介绍
- Behavior tree 编程实战
- 烤箱做披萨的做法 教你做火腿肠披萨
- 全球首只AIGC动画短片发行,日漫风格超治愈!
- 我的世界java蜜蜂_在最新的《我的世界》Java版更新中 蜜蜂是所有的热点
- libcurl smtp发送邮件附件大小限制问题
- Excel中文本换行
- 灌篮高手怎么找回原来的服务器,灌篮高手手游异常登陆、封号补偿及领取方式介绍...
- UE4:按键按下触发声音事件,离开位置声音停止
- 大道至简(读后感)第二章 是懒人造就了方法
- Cisco PT最新版下载路径
热门文章
- java basefont_itext 文本域 字体样式设置
- 什么是迭代(die dai)
- 敏捷迭代是什么意思_我认为“敏捷”的方向是第4部分:“敏捷”是什么意思?...
- ExpandableListView 添加分割线
- [转] 宝宝出生第一年妈妈最应关心的问题
- 强大且超实用的论文阅读工具——ReadPaper
- 小程序人脸核验功能实现-边读边录(一)
- 渗透测试php过程,利用骑士cms的一次纠结的渗透测试过程(两个潜在
- 文件服务器查询重复文件,DupScout – 重复文件扫描、删除或移动的免费工具
- Excel操作-多条件筛选