尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Faker 代理类(Proxy Class)完全指南:多语言支持、种子升级与唯一值生成

Faker 代理类(Proxy Class)完全指南:多语言支持、种子升级与唯一值生成 测试Mock数据脱敏【免费下载链接】fakerFaker is a Python package that generates fake data for you.项目地址https://gitcode.com/gh_mirrors/fak/faker点击查看免费下载导读本文围绕 FakerPython 假数据生成库的Faker代理类展开深入解析它在2.0.4之后的架构演进为什么从Factory.create快捷方式升级为代理类、升级迁移时唯一的破坏性变更Faker.seed()如何处理、多语言模式Multiple Locale Mode下的工厂选择与加权概率机制如何实现以及v4.2.0引入的.unique唯一值属性的使用边界。读完本文你将能够安全地从旧版 Faker 迁移到新代理类掌握单语言/多语言模式下的实例化、种子管理与属性代理细节并正确使用唯一值生成能力避免陷入无限循环。背景从Factory.create快捷方式到代理类在版本2.0.4及更早版本中faker.Faker只是Factory.create类方法的一个快捷别名。调用Faker()实际创建的是一个Generator对象它挂载了来自faker.providers的大量 provider 方法。这种设计的痛点在于如果想做某些定制例如同时支持多个语言环境、为特定 locale 注入自定义 provider就必须绕道Factory与Generator内部实现升级时容易破坏用户代码。解决方案是引入一个新的Faker代理类proxy class实现在 faker/proxy.py。它在绝大多数场景下与旧的Faker快捷方式表现一致但新增了两项关键能力支持多个 locale允许用户直接子类化Faker。为便于行文下文沿用原文档的术语newFaker指新的代理类oldFaker指Factory.create快捷方式。破坏性变更实例上的seed()被禁用新旧Faker的Faker.seed()都指向Generator.seed()即对模块级共享的random.Random实例进行播种。但在 newFaker中从Faker实例上调用seed()已被禁用会直接抛出TypeErrorTypeError: Calling .seed() on instances is deprecated. Use the class method Faker.seed() instead.这一限制实现在Faker.__getattribute__只要属性名是seed就立即抛出TypeError从而杜绝实例方法调用。其动机是处理一个历史遗留行为旧的实现中多个Generator共享同一个模块级random.Random实例这种隐式共享在新代理类加入后会变得更加混乱因此需要强制用户显式通过类方法播种。对应测试见 tests/test_proxy.py它验证了fake.seed(0)抛出TypeError同时Faker.seed(0)会正确地被代理到Generator.seed并恰好调用一次。升级指南三种迁移路径路径一替换为类方法调用推荐假设受影响的旧代码如下from faker import Faker fake Faker() fake.seed(0) # This will raise a TypeError只需把实例方法调用替换为类方法调用from faker import Faker fake Faker() Faker.seed(0)这就完成了迁移。即使后续向Faker()传入额外参数也无需其他改动因为 newFaker与 oldFaker期望的参数完全一致见Faker.__init__的签名locale、providers、generator、includes、use_weighting及**config。路径二保守方案——绕过代理类如果希望完全规避新代理类的行为可以把Faker重新定义为旧的快捷方式from faker.factory import Factory Faker Factory.create fake Faker() fake.seed(0)这样做仍然可以使用后续新增的 provider 方法且不受新代理类潜在 bug 影响但代价是没有多语言支持也不能子类化。路径三进阶使用seed_instance与seed_locale升级后如果需要为实例级随机数独立播种可使用代理类提供的实例方法见 faker/proxy.pyFaker.seed(seed)类方法对所有将来创建的内部 Generator 共享的random.Random对象播种fake.seed_instance(seed)为每个内部 Generator 创建并播种一个独立的random.Random对象fake.seed_locale(locale, seed)仅对指定 locale 对应的内部 Generator 创建并播种独立随机数对象。对应测试见 tests/test_proxy.pyseed_instance会为每个 locale 各调用一次Generator.seed_instance而seed_locale(en_US, 0)只改变en_US工厂的随机数状态其余 locale 的随机数对象保持共享不变。代理类实现细节单语言与多语言模式newFaker实例本质上是一个代理对象内部持有一组Generator引用——每个在实例化时指定的唯一 locale 对应一个。这些Generator对象正是 oldFaker的产物。判断依据faker/proxy.py只有一个内部Generator→单语言模式single locale mode多于一个 →多语言模式multiple locale mode。单语言模式1:1 代理单语言模式下newFaker很容易被伪装成 oldFaker实例它暴露相似的接口并把方法、属性、特性的访问一对一转发给唯一的内部GeneratorFaker.__getattr__中len(self._factories) 1分支。这正是向后兼容得以保持的实现方式除Faker.seed外。random的 getter/setter 在单语言模式下同样被代理到内部 Generatorfaker/proxy.py。多语言模式按需代理多语言模式下不再存在 1:1 映射。调用被代理的方式取决于目标属性是 provider 方法还是Generator上的普通属性seed_instance提供了合理的默认实现遍历所有内部 Generator 逐个播种而add_provider与random的 getter/setter 则依赖具体场景或存在潜在危险因此在多语言模式下直接访问Generator属性会抛出NotImplementedError见 faker/proxy.py。这些情况下更合适的方式是用户自行子类化实现所需行为或直接从内部Generator对象调用这些方法。代理类属性名解析顺序代理类的属性解析逻辑相当精细按以下顺序执行对应 faker/proxy.py属性名为seed→ 抛出TypeError阻止类方法seed被实例调用属性名匹配代理类实例自身属性 → 返回该属性否则若处于单语言模式 → 把调用代理给唯一内部Generator尝试返回匹配属性否则多语言模式若属性名是Generator属性 → 抛出NotImplementedError若属性名匹配缓存模式正则^_cached_\w*_mapping$见 faker/proxy.py→ 抛出AttributeError若存在第 2 步早已处理其余情况 → 假定属性名是 provider 方法执行工厂/生成器选择factory selection并代理到选中的Generator。注意第 2 步依赖__getattribute__优先返回实例属性第 6 步才触发 provider 方法选择。这一设计也解释了为何多语言模式下fake.name()等调用能够自动挑选合适的 locale。语言环境规范化Locale Normalizationlocale参数的取值决定 newFaker运行在哪种模式。合法取值有四种faker/proxy.py任意空值如None→ 自动回退到默认en_US见 faker/config.py 中DEFAULT_LOCALE en_US单个合法 locale 字符串下划线或连字符写法均可包含多个合法 locale 字符串的 list、tuple 或 set键为合法 locale 字符串、值为权重的OrderedDict普通dict也支持但注意普通 dict 无序无法保证概率映射的顺序。前两种是 oldFaker已有的用法newFaker行为一致且总是产生单语言模式。连字符与下划线的归一化多语言模式下最大的坑是 locale 字符串的两种写法en_US下划线与en-US连字符。为避免重复与歧义实例化时 newFaker会把所有 locale 字符串统一归一化为下划线格式并以此存储faker/proxy.py 中code.replace(-, _)en_US与en-US被视为同一个 locale两者同时指定时后处理的视为重复项被丢弃list/tuple/set 分支保证 FIFO 顺序去重通过键索引访问内部Generatorfake[en-US]时执行相同的归一化Faker.__getitem__。例如下面的代码尽管指定了四个 locale最终仍处于单语言模式from faker import Faker fake Faker([en-US, en_US, en_US, en-US]) # Will return [en_US] fake.locales # Get reference to en_US generator us1 fake[en_US] # Get reference to en-US generator us2 fake[en-US] # Will return True us1 us2此外locale 合法性校验同样发生在这里无效 locale如foo_Bar会抛出AttributeErrorfaker/factory.py 会校验 locale 是否在AVAILABLE_LOCALES中测试见 tests/test_proxy.py。Faker([en-US, en_US])这类重复场景下fake.locales返回[en_US]的行为也有对应测试覆盖tests/test_proxy.py。多语言模式Multiple Locale Mode当locale参数是 list、tuple、set 或OrderedDict且归一化后包含多于一个合法 locale时进入多语言模式。例如from collections import OrderedDict from faker import Faker locale_list [en-US, ja-JP, en_US] fake1 Faker(locale_list) # Will return [en_US, ja_JP] fake1.locales locale_odict OrderedDict([ (en-US, 1), (ja-JP, 2), (en_US, 2), ]) fake2 Faker(locale_odict) # Will return [en_US, ja_JP] fake2.locales注意OrderedDict分支中后出现的重复键会覆盖先前的权重(en-US, 1)之后(en_US, 2)归一化后键相同最终权重取2测试 tests/test_proxy.py 验证了fake.weights [4, 1, 1, 2]的覆盖行为。工厂/生成器选择逻辑多语言模式下调用一个潜在的 provider 方法时会依次执行以下逻辑Faker._map_provider_method与Faker._select_factory检查该 provider 方法是否已有缓存映射属性_cached_method_mapping。有则直接使用跳到第 3 步否则检查哪些内部Generator支持该方法把映射结果与实例化时提供的权重一起缓存setattr(self, attr, mapping)若没有任何 Generator 支持该方法 → 抛出AttributeError与 oldFaker行为一致若只有一个Generator 支持 → 直接返回它若多个 Generator 支持且未提供权重→ 使用均匀分布random.choice随机选择若多个 Generator 支持且提供了权重→ 按权重分布choices_distribution来自 faker/utils/distribution.py随机选择。缓存机制保证了性能fake.name()首次调用时构建并缓存_cached_name_mapping后续调用直接命中缓存见 tests/test_proxy.py 对缓存行为与调用次数的验证。除定制 locale 概率与降低性能损耗外选择逻辑还保证了只要至少有一个内部 Generator 支持该方法调用就不会失败。对应测试分别覆盖了无权重选择tests/test_proxy.py、有权重选择tests/test_proxy.py以及不支持方法的AttributeErrortests/test_proxy.py。多语言模式速查表以下示例来自原文档是多语言模式下 newFaker的完整速查表from collections import OrderedDict from faker import Faker locales OrderedDict([ (en-US, 1), (en-PH, 2), (ja_JP, 3), ]) fake Faker(locales) # Get the list of locales specified during instantiation fake.locales # Get the list of internal generators of this Faker instance fake.factories # Get the internal generator for en_US locale fake[en_US] # Get the internal generator for en_PH locale fake[en_PH] # Get the internal generator for ja_JP locale fake[ja_JP] # Will raise a KeyError as en_GB was not included fake[en_GB] # Set the seed value of the shared random.Random object # across all internal generators that will ever be created Faker.seed(0) # Creates and seeds a unique random.Random object for # each internal generator of this Faker instance fake.seed_instance(0) # Creates and seeds a unique random.Random object for # the en_US internal generator of this Faker instance fake.seed_locale(en_US, 0) # Generate a name based on the provided weights # en_US - 16.67% of the time (1 / (1 2 3)) # en_PH - 33.33% of the time (2 / (1 2 3)) # ja_JP - 50.00% of the time (3 / (1 2 3)) fake.name() # Generate a name under the en_US locale fake[en-US].name() # Generate a zipcode based on the provided weights # Note: en_PH does not support the zipcode provider method # en_US - 25% of the time (1 / (1 3)) # ja_JP - 75% of the time (3 / (1 3)) fake.zipcode() # Generate a zipcode under the ja_JP locale fake[ja_JP].zipcode() # Will raise an AttributeError fake[en_PH].zipcode() # Generate a Luzon province name # Note: only en_PH out of the three supports this provider method fake.luzon_province() # Generate a Luzon province name fake[en_PH].luzon_province() # Will raise an AttributeError fake[ja_JP].luzon_province()要点解读fake.locales返回归一化后的 locale 列表fake.factories返回内部 Generator 列表faker/proxy.pyfake.items()可取得(locale, factory)键值对下标访问fake[en_PH]支持连字符写法内部会归一化不存在的 locale 抛出KeyErrorzipcode只有en_US与ja_JP支持因此权重比例从(1,2,3)重新归一化为(1,3)即 25%/75%luzon_province只有en_PH支持直接返回该工厂不触发随机选择注意多语言模式下fake.factories中元素的实际类型每个 locale 会递归构造一个单 locale 的Faker见 faker/proxy.py因此可以继续对这些子代理做下标访问与调用。Unique Values.unique唯一值属性v4.2.0从v4.2.0起Faker代理类新增了.unique属性实现为 UniqueProxy由Faker.unique暴露。通过该属性访问 provider 方法可以保证返回的值在Faker实例生命周期内不重复import faker fake faker.Faker() numbers set(fake.unique.random_int() for i in range(1000)) assert len(numbers) 1000多语言实例的 locale 限定多语言Faker实例可以通过下标指定用于唯一值生成的 localefrom faker import Faker fake Faker([en_US, fr_FR]) names [fake.unique[en_US].first_name() for i in range(500)] assert len(set(names)) 500UniqueProxy.__getitem__faker/proxy.py会取出对应 locale 的子代理并共享同一个_seen已见字典因此跨 locale 的唯一性也是全局维护的测试见 tests/test_unique.py。清除已见值与参数签名隔离调用fake.unique.clear()可清空已见值允许之前生成过的值再次出现import faker fake faker.Faker() numbers set(fake.unique.random_int(min1, max10) for i in range(10)) other_numbers set(fake.unique.random_int(min1, max5) for i in range(5)) assert other_numbers.issubset(numbers)不同参数签名的 provider 方法不共享唯一值池fake.unique.random_int(min1, max10)与fake.unique.random_int(min1, max5)各自独立维护已见集合。这一隔离在_wrap中实现——唯一性键为(name, args, tuple(sorted(kwargs.items())))faker/proxy.py。对应测试见 tests/test_unique.py。值域耗尽与UniquenessException如果 provider 函数可生成的值域很小且使用了.unique经过一定次数迭代后可能找不到合适的新值。为避免无限循环此时会抛出UniquenessExceptionimport faker fake faker.Faker() for i in range(3): fake.unique.boolean() # UniquenessException!内部实现限制为最多尝试_UNIQUE_ATTEMPTS 1000次faker/proxy.py超限即抛出UniquenessException(fGot duplicated values after {_UNIQUE_ATTEMPTS:,} iterations.)faker/proxy.py。测试 tests/test_unique.py 用仅有两个取值的boolean验证了这一点。exclude_types()按类型豁免唯一性检查对于值域极小的类型如只有 True/False 两个取值的布尔可以用exclude_types()豁免特定类型的唯一性检查import faker fake faker.Faker() # Exclude booleans from uniqueness checks proxy fake.unique.exclude_types([bool]) # This works fine - booleans can now repeat for i in range(100): proxy.pybool() # No UniquenessException! # Other types still enforce uniqueness names [proxy.first_name() for i in range(10)] assert len(set(names)) 10 # All uniqueexclude_types()返回一个新的UniqueProxy与原始代理共享同一个已见值字典保证跨代理实例的一致性faker/proxy.pyfrom faker import Faker fake Faker() # Get a unique name name1 fake.unique.first_name() # Create proxy excluding bools proxy fake.unique.exclude_types([bool]) # This shares the same seen dictionary name2 proxy.first_name() assert name1 ! name2 # Still enforces uniqueness for names底层逻辑faker/proxy.py若返回值的类型命中_excluded_types元组则直接返回不查重否则走常规唯一性逻辑。相关行为由 tests/test_unique_exclude.py 全面覆盖包括可链式调用exclude_types([bool])后再exclude_types([int])、可同时排除多类型、跨 locale 生效等。可哈希性限制最后一个重要约束.unique属性只支持可哈希hashable的参数与返回值因为它内部用 set 做快速成员检测。不可哈希的返回值会直接抛出TypeErrorimport faker fake faker.Faker() fake.unique.profile() # TypeError: unhashable type: dict注意_make_hashablefaker/proxy.py已把 dict/list/set 递归转换为可哈希的 tuple/frozenset 表示因此pydict、pylist、pyset这类复杂返回类型实际可用tests/test_unique.py 验证了这一点但profile()这类返回完整 profile 字典的方法仍会报错。另外通过.unique访问非函数属性会抛出TypeError: Accessing non-functions through .unique is not supported.faker/proxy.py。小结升级路径明确把实例上的fake.seed(x)换成类方法Faker.seed(x)即可启用新代理类保守派可用Faker Factory.create绕过。单语言模式完全向后兼容1:1 代理到内部Generator除seed外。多语言模式能力丰富支持 list/tuple/set/OrderedDict带权重四种 locale 形态locale 字符串统一归一化为下划线格式并去重provider 方法调用会按缓存映射 加权/均匀分布选择支持它的 Generator。.unique属性提供实例生命周期内的唯一值保证支持 locale 限定、clear()重置、参数签名隔离、exclude_types()类型豁免但受限于可哈希性与值域大小超 1000 次尝试抛出UniquenessException。更多源码细节可继续阅读 faker/proxy.py、faker/factory.py、faker/generator.py 与测试目录 tests/test_proxy.py、tests/test_unique.py、tests/test_unique_exclude.py。赞分享测试Mock数据脱敏【免费下载链接】fakerFaker is a Python package that generates fake data for you.项目地址https://gitcode.com/gh_mirrors/fak/faker点击查看免费下载相关推荐Faker 唯一值生成实战指南掌握 uniqueArray 与多种去重策略Faker 唯一值生成实战指南掌握 uniqueArray 与多种去重策略 导读 在使用 Faker https://link.gitcode.com/i/4测试Faker Ruby 库实战指南生成真实感假数据、唯一值与确定性随机、多语言本地化Faker Ruby 库实战指南生成真实感假数据、唯一值与确定性随机、多语言本地化 Faker 是一个用于生成几乎真实的假数据的 Ruby 库适用于单元测试开发工具Luban多语言支持详解C、Java、Go等10种语言代码生成指南Luban多语言支持详解C 、Java、Go等10种语言代码生成指南 Luban是一个强大、易用、优雅且稳定的游戏配置解决方案特别擅长多语言代码生成。它能够开发工具代码生成游戏开发CLI数据建模上一篇告别性能瓶颈gperftools全链路分析与优化实战指南下一篇bootstrap-colorpicker扩展开发指南构建属于你的颜色选择插件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表