如何使用元组和字典解压缩但没有额外方法的情况下使attrs类

时间:2018-08-29 14:24:02

标签: python python-attrs python-dataclasses

我刚刚开始将attrs模块用于非常漂亮的python模块(或者类似地,我们可以使用Python 3.7 DataClasses)。我的一种常见用法是将类用作参数值的容器。我喜欢在分配参数时使用标签,并且更喜欢使用值的更清洁的属性样式引用,但是我还喜欢在将值存储在有序的dict中时具有一些不错的功能:

  1. *tuplelist一样解包以输入函数参数
  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方法。

2 个答案:

答案 0 :(得分:3)

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

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 / {{1} }。性能方面,这应该很好;无论如何,解压缩都会转换为AttributeError / tuple(因为C API希望能够将非dicttuple的内容直接传递给dict / @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)) 在其参数上使用特定于类型的API)。

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

library(purrr) 
storage <-c() 
d = NULL 
x1 <- rdunif(1, 3, a=1) 

while ( x1 != 1) { 
  x1<- rdunif(1, 3, a=1) 
  storage <-c(storage, x1) 
} 

storage

答案 1 :(得分:0)

扩展@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}