2

我刚开始使用attrs非常漂亮的python模块(或者类似地我们可以使用Python 3.7 DataClasses)。我有一个常见的使用模式是让类成为参数值的容器。我喜欢分配参数时的标签,以及更简洁的属性样式引用值,但我也喜欢在将值存储在有序 dict 之类的东西中时有一些很好的特性:

  1. *像 atuple或 a一样解包list以输入函数参数
  2. **当关键字传递是必要或可取的时解包。

我可以通过在类中添加三个方法来实现这一切

@attr.s
class DataParameters:
    A: float = attr.ib()
    alpha: float = attr.ib()
    c: float = attr.ib()
    k: float = attr.ib()
    M_s: float = attr.ib()

    def keys(self):
        return 'A', 'alpha', 'c', 'k', 'M_s'

    def __getitem__(self, key):
        return getattr(self, key)

    def __iter__(self):
        return (getattr(self, x) for x in self.keys())

然后我可以使用这样的类:

params = DataParameters(1, 2, 3, 4, 5)
result1 = function1(100, 200, *params, 300)
result2 = function2(x=1, y=2, **params)

这里的动机是数据类提供方便和清晰。但是,我不使用数据类来编写我要编写的模块是有原因的。希望函数调用应该接受简单的参数,而不是复杂的数据类。

上面的代码很好,但我想知道我是否遗漏了一些可以让我完全跳过编写函数的东西,因为模式很清楚。属性按我希望它们解包的顺序添加,并且可以根据关键字参数的属性名称读取为键值对。

也许这是这样的:

@addtupleanddictunpacking
@attr.s
class DataParameters:
    A: float = attr.ib()
    alpha: float = attr.ib()
    c: float = attr.ib()
    k: float = attr.ib()
    M_s: float = attr.ib()

但我不确定是否有attrs我没有找到的东西本身可以做到这一点。另外,我不确定如何在添加属性时保持它们的顺序并将其转换为 keys 方法。

4

2 回答 2

6

它没有直接集成到类中,但asdict辅助astuple函数旨在执行这种转换。

params = DataParameters(1, 2, 3, 4, 5)
result1 = function1(100, 200, *attr.astuple(params), 300)
result2 = function2(x=1, y=2, **attr.asdict(params))

它们没有集成到类本身中,因为这会使类在任何地方都表现为序列或映射,这可能会在预期TypeError/时导致无声的错误行为。AttributeError在性能方面,这应该没问题;解包无论如何都会转换为tuple/ dict(它不能直接传递不是 atupledictin 的东西,因为 C API 期望能够在其参数上使用特定于类型的 API)。

如果您真的希望该类充当序列或映射,则基本上必须做您所做的事情,尽管您可以使用辅助函数来减少自定义代码和重复的变量名称,例如:

@classmethod
def keys(cls):
    return attr.fields_dict(cls).keys()

def __getitem__(self, key):
    return getattr(self, key)

def __iter__(self):
    return iter(attr.astuple(self, recurse=False))  
于 2018-08-29T16:10:16.887 回答
1

扩展来自@ShadowRanger 的想法,可以制作您自己的包含 attr.s 和 attr.ib 的装饰器,以获得更简洁的解决方案,基本上增加了额外的处理。

import attr
field = attr.ib  # alias because I like it

def parameterset(cls):
    cls = attr.s(cls)

    # we can use a local variable to store the keys in a tuple
    # for a faster keys() method
    _keys = tuple(attr.fields_dict(cls).keys())
    @classmethod
    def keys(cls):
    #     return attr.fields_dict(cls).keys()
        return (key for key in _keys)

    def __getitem__(self, key):
        return getattr(self, key)

    def __iter__(self):
        return iter(attr.astuple(self, recurse=False))

    cls.keys = keys
    cls.__getitem__ = __getitem__
    cls.__iter__ = __iter__

    return cls

@parameterset
class DataParam:
    a: float = field()
    b: float = field()

dat = DataParam(a=1, b=2)
print(dat)
print(tuple(dat))
print(dict(**dat))

给出输出

DataParam(a=1, b=2)
(1, 2)
{'a': 1, 'b': 2}
于 2018-08-30T14:34:41.187 回答