跳转到主要内容

容器类模板杀手。

项目描述

docs

Documentation Status

tests

Travis-CI Build Status AppVeyor Build Status Requirements Status
Coverage Status Coverage Status
Code Quality Status Scrutinizer Status Codacy Code Quality Status CodeClimate Quality Status

package

PyPI Package latest release PyPI Package monthly downloads PyPI Wheel Supported versions Supported implementations

容器类模板杀手。

功能

  • 可读的 __repr__

  • 完整的比较方法集合

  • 关键字和位置参数支持。像正常类一样工作 - 您可以在子类中覆盖几乎所有内容(例如:自定义 __init__)。相比之下,hynek/characteristic 强制不同的调用方案,并以不同的参数调用您的 __init__

安装

pip install fields

使用示例

一个有两个属性,namesize 的类

>>> 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 和两个属性(leftright)具有默认值 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
--------------------------------------------------------------------------------------

文档

https://python-fields.readthedocs.org/

开发

要运行所有测试,请在您的shell中运行 tox (如果您还没有,请运行 pip install tox

tox

常见问题解答

为什么我应该使用这个?

输入更少,为什么要在需要有效符号时使用引号。实际上,这是指定具有字段容器的可能最短形式之一。

但你在滥用一个非常知名的语法。你正在使用属性访问而不是字符串列表。为什么?

符号应该是符号。为什么在可以避免的情况下验证字符串使其成为有效符号?只需使用符号。节省输入和验证代码。

在语义先于传统语法使用的情况下,使用语言结构并不令人惊讶或令人困惑。例如,如果我们有 class Person(Fields.first_name.last_name.height.weight): pass,那么很明显我们正在谈论一个具有 first_namelast_nameheightwidth 字段的 Person 对象:这些单词有明显的意义。

再次提醒,您无论如何都不应该将变量命名为 f1f2 或任何其他非语义符号。

语义先于语法:就像看到一块像狗的蛋糕,你不会期望蛋糕会吠叫和奔跑。

这是稳定的吗?有测试过吗?

是的。无情地在对 TravisAppVeyor 进行测试。

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

客户评价

邪恶的。不能忽视。

David Beazley

我认为这是 Python 代码曾经让我感到最悲伤的一行。

—IRC (#python) 上的某人

不要说你喜欢它。

—PyPy 贡献者

Fields 完全疯狂,但有点酷。

—IRC (#python) 上的某人

WHAT?!?

—2015 年 EuroPython 上的无辜受害者

我不认为它应该工作……

—2015 年 EuroPython 上的无辜受害者

这是某种 Ruby 东西吗?

—2015 年 EuroPython 上的无辜受害者

Python 程序员那么懒惰吗?

—某位 Java 开发者

我将在我的下一个项目中使用这个。你真是个糟糕的人。

Isaac Dickinson

抱歉

我在 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(与 factoryclass_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.ConvertibleFieldsfields.ConvertibleMixin。它们有两个便利属性: as_dictas_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 版本。

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪一个,请了解有关安装包的更多信息。

源代码分发

fields-5.0.0.tar.gz (36.2 kB 查看哈希值)

上传时间 源代码

构建分发

fields-5.0.0-py2.py3-none-any.whl (19.8 kB 查看哈希值)

上传时间 Python 2 Python 3

由以下支持