容器类模板杀手。
项目描述
docs |
|
---|---|
tests |
|
package |
容器类模板杀手。
功能
可读的 __repr__
完整的比较方法集合
关键字和位置参数支持。像正常类一样工作 - 您可以在子类中覆盖几乎所有内容(例如:自定义 __init__)。相比之下,hynek/characteristic 强制不同的调用方案,并以不同的参数调用您的 __init__。
安装
pip install fields
使用示例
一个有两个属性,name 和 size 的类
>>> from fields import Fields
>>> class Pizza(Fields.name.size):
... pass
...
>>> p = Pizza("Pepperoni", "large")
>>> p
Pizza(name='Pepperoni', size='large')
>>> p.size
'large'
>>> p.name
'Pepperoni'
您也可以使用关键字参数
>>> Pizza(size="large", name="Pepperoni")
Pizza(name='Pepperoni', size='large')
您可以有您想要的任何数量的属性
>>> class Pizza(Fields.name.ingredients.crust.size):
... pass
...
>>> Pizza("Funghi", ["mushrooms", "mozarella"], "thin", "large")
Pizza(name='Funghi', ingredients=['mushrooms', 'mozarella'], crust='thin', size='large')
一个有一个必需属性 value 和两个属性(left 和 right)具有默认值 None 的类
>>> class Node(Fields.value.left[None].right[None]):
... pass
...
>>> Node(1, Node(2), Node(3, Node(4)))
Node(value=1, left=Node(value=2, left=None, right=None), right=Node(value=3, left=Node(value=4, left=None, right=None), right=None))
>>> Node(1, right=Node(2))
Node(value=1, left=None, right=Node(value=2, left=None, right=None))
您也可以 内联 使用它
>>> Fields.name.size("Pepperoni", "large")
FieldsBase(name='Pepperoni', size='large')
想要元组吗?
namedtuple 的替代品
>>> from fields import Tuple
>>> class Pair(Tuple.a.b):
... pass
...
>>> issubclass(Pair, tuple)
True
>>> p = Pair(1, 2)
>>> p.a
1
>>> p.b
2
>>> tuple(p)
(1, 2)
>>> a, b = p
>>> a
1
>>> b
2
元组 很快!
benchmark: 9 tests, min 5 rounds (of min 25.00us), 1.00s max time, timer: time.perf_counter Name (time in us) Min Max Mean StdDev Rounds Iterations -------------------------------------------------------------------------------------- test_characteristic 6.0100 1218.4800 11.7102 34.3158 15899 10 test_fields 6.8000 1850.5250 9.8448 33.8487 5535 4 test_slots_fields 6.3500 721.0300 8.6120 14.8090 15198 10 test_super_dumb 7.0111 1289.6667 11.6881 31.6012 15244 9 test_dumb 3.7556 673.8444 5.8010 15.0514 14246 18 test_tuple 3.1750 478.7750 5.1974 9.1878 14642 12 test_namedtuple 3.2778 538.1111 5.0403 9.9177 14105 9 test_attrs_decorated_class 4.2062 540.5125 5.3618 11.6708 14266 16 test_attrs_class 3.7889 316.1056 4.7731 6.0656 14026 18 --------------------------------------------------------------------------------------
文档
开发
要运行所有测试,请在您的shell中运行 tox (如果您还没有,请运行 pip install tox)
tox
常见问题解答
为什么我应该使用这个?
输入更少,为什么要在需要有效符号时使用引号。实际上,这是指定具有字段容器的可能最短形式之一。
但你在滥用一个非常知名的语法。你正在使用属性访问而不是字符串列表。为什么?
符号应该是符号。为什么在可以避免的情况下验证字符串使其成为有效符号?只需使用符号。节省输入和验证代码。
在语义先于传统语法使用的情况下,使用语言结构并不令人惊讶或令人困惑。例如,如果我们有 class Person(Fields.first_name.last_name.height.weight): pass,那么很明显我们正在谈论一个具有 first_name、last_name、height 和 width 字段的 Person 对象:这些单词有明显的意义。
再次提醒,您无论如何都不应该将变量命名为 f1、f2 或任何其他非语义符号。
语义先于语法:就像看到一块像狗的蛋糕,你不会期望蛋糕会吠叫和奔跑。
这是稳定的吗?有测试过吗?
API 是稳定的吗?
当然是的。
为什么不使用 namedtuple?
这很丑陋,重复且不灵活。比较这个
>>> from collections import namedtuple
>>> class MyContainer(namedtuple("MyContainer", ["field1", "field2"])):
... pass
>>> MyContainer(1, 2)
MyContainer(field1=1, field2=2)
与这个
>>> class MyContainer(Tuple.field1.field2):
... pass
>>> MyContainer(1, 2)
MyContainer(field1=1, field2=2)
为什么不使用 characteristic?
丑陋,不一致 - 你不拥有这个类
让我们试试这个
>>> import characteristic >>> @characteristic.attributes(["field1", "field2"]) ... class MyContainer(object): ... def __init__(self, a, b): ... if a > b: ... raise ValueError("Expected %s < %s" % (a, b)) >>> MyContainer(1, 2) Traceback (most recent call last): ... ValueError: Missing keyword value for 'field1'.
WHAT!?好吧,让我们写一些更多的代码
>>> MyContainer(field1=1, field2=2) Traceback (most recent call last): ... TypeError: __init__() ... arguments...
这太疯狂了。你必须围绕这些怪癖来编写你的类
让我们试试这个
>>> class MyContainer(Fields.field1.field2):
... def __init__(self, a, b):
... if a > b:
... raise ValueError("Expected %s < %s" % (a, b))
... super(MyContainer, self).__init__(a, b)
就像一个正常的类,按预期工作
>>> MyContainer(1, 2)
MyContainer(field1=1, field2=2)
为什么不使用 attrs?
这是一个非常困难的问题。
考虑这个典型用例
.. sourcecode:: pycon
>>> import attr >>> @attr.s ... class Point(object): ... x = attr.ib() ... y = attr.ib()
值得注意
attrs 更快,因为它不允许你的类用作混入(它不会为你执行任何 super(cls, self).__init__(...))。
典型用例不允许你自定义 __init__。如果你定义了一个自定义 __init__,它将被 attrs 生成的那个覆盖。
因为它在类上定义了属性,所以它更适合与 IDE 和源代码分析工具一起使用。
总的来说,attrs 是一个快速且最小的容器库,没有子类支持。绝对值得考虑。
这不会让 pylint 混淆吗?
通常会的,但有一个插件可以让 pylint 理解它,就像任何其他类一样: pylint-fields。
客户评价
邪恶的。不能忽视。
我认为这是 Python 代码曾经让我感到最悲伤的一行。
—IRC (#python) 上的某人
不要说你喜欢它。
—PyPy 贡献者
Fields 完全疯狂,但有点酷。
—IRC (#python) 上的某人
WHAT?!?
—2015 年 EuroPython 上的无辜受害者
我不认为它应该工作……
—2015 年 EuroPython 上的无辜受害者
这是某种 Ruby 东西吗?
—2015 年 EuroPython 上的无辜受害者
Python 程序员那么懒惰吗?
—某位 Java 开发者
我将在我的下一个项目中使用这个。你真是个糟糕的人。
抱歉
我在 EuroPython 上尽力了……
变更日志
5.0.0 (2016-04-13)
添加了 fields.InheritableFields 基类。它允许子类化,并用于多继承场景。是的,是的,这会带来很多痛苦和苦难,但有些人就是喜欢这样。
精简了内置封装器的接口(必要的参数已删除,因为它是冗余的,其余参数已交换)。现在它们必须是一个接受两个参数的函数: fields, defaults。
4.0.0 (2016-01-28)
增加了 __all__ 和 factory 方便性。由于它需要一些特殊关注的使用(毕竟它是一个元类),已从公共API中移除了 fields.Factory。
为高级使用增加了 make_init_func 到公共API(与 factory 和 class_sealer 结合使用)。
3.0.0 (2015-10-04)
不允许使用具有“双下划线”名称的字段创建容器。例如: class Foo(Fields.__foo__): 是不允许的。
2.4.0 (2015-06-13)
类似于 fields.Fields,增加了三个新基类
fields.BareFields(实现 __init__)。
fields.ComparableMixin(实现 __eq__、__ne__、__lt__、__gt__、__le__、__ge__ 和 __hash__)。
fields.PrintableMixin(实现 __repr__)。
改进了文档中的参考部分。
增加了 fields.ConvertibleFields 和 fields.ConvertibleMixin。它们有两个便利属性: as_dict 和 as_tuple`。
2.3.0 (2015-01-20)
允许在 SlotsFields 子类中覆盖 __slots__。
2.2.0 (2015-01-19)
将 make_init_func 作为可选参数添加到 class_sealer 中。将 __base__ 选项重命名为 base。
2.1.1 (2015-01-19)
移除了虚假的 console_scripts 入口点。
2.1.0 (2015-01-09)
增加了 SlotsFields(与 Fields 相同,但自动在 CPython 上添加 __slots__ 以提高内存效率)。
为 Tuple 增加了默认参数的支持。
2.0.0 (2014-10-16)
使 FieldsBase 中的 __init__ 更快(用于 fields.Fields)。
将 RegexValidate 移动到 fields.extras 中。
1.0.0 (2014-10-05)
许多内部更改,元类不再在闭包中创建。没有更多的闭包。
增加了 RegexValidate 容器创建器(应将其作为使用 Factory 元类的示例)。
增加了将多个容器作为基类的支持。
增加了一个 super() sink,以便 super().__init__(*args, **kwargs) 总是起作用。所有内容都继承自具有可以接受任何参数的 __init__ 的基类(与 object.__init__ 不同)。这允许更灵活的使用。
增加了验证,以防止在将多个容器作为基类使用时使用冲突的字段布局。
更改了类容器中的 __init__ 函数,使其在位置参数和关键字参数方面像 Python 函数一样工作。例如: class MyContainer(Fields.a.b.c[1].d[2]) 将以与 def func(a, b, c=1, d=2) 相同的方式工作,当传递参数时。现在您可以使用 MyContainer(1, 2, 3, 4)(所有位置参数)或 MyContainer(1, 2, 3, d=4)(混合)。
0.3.0 (2014-07-19)
修正了字符串表示。
0.2.0 (2014-06-28)
许多破坏性更改。从 __call__ 切换到 __getitem__ 以进行默认值赋值。
0.1.0 (2014-06-27)
alpha 版本。
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪一个,请了解有关安装包的更多信息。