如何使用 tuple 和 dict 解包但没有额外的方法来制作 attrs class
how to make attrs class with tuple and dict unpacking but without extra methods
我刚开始使用 python 的 attrs
模块,它非常灵活(或者类似地我们可以使用 Python 3.7 DataClasses)。我的一个常见使用模式是 class 作为参数值的容器。我喜欢分配参数时的标签,以及更清晰的属性样式引用值,但我也喜欢有一些功能,这些功能在将值存储在有序字典中时非常有用:
*
像 tuple
或 list
一样解包以输入函数参数
**
在需要或需要关键字传递时解包。
我可以通过向 class
添加三个方法来实现所有这些
@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())
然后我可以像这样使用 classes:
params = DataParameters(1, 2, 3, 4, 5)
result1 = function1(100, 200, *params, 300)
result2 = function2(x=1, y=2, **params)
这里的动机是数据classes 提供了方便和清晰。但是,有一些原因导致我不要求我正在编写的模块使用数据 class。希望函数调用应该接受简单的参数,而不是复杂的数据classes.
上面的代码很好,但我想知道我是否遗漏了一些可以让我完全跳过编写函数的东西,因为模式非常清晰。属性按照我希望它们解包的顺序添加,并且可以根据关键字参数的属性名称读取为键值对。
也许这是这样的:
@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
本身是否有我没有找到的东西可以做到这一点。此外,我不确定如何在添加属性时保持属性的顺序并将其转换为键方法。
它没有直接集成到 class 中,但 the asdict
and 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))
它们没有集成到 class 本身,因为这会使 class 表现为序列或映射 无处不在 ,这可能会导致无声预期 TypeError
/AttributeError
时的不当行为。在性能方面,这应该没问题;无论如何,解包都会转换为 tuple
/dict
(它不能直接传递不是 tuple
或 dict
的内容,因为 C API 期望能够在其参数上使用特定于类型的 API)。
如果你真的想让 class 充当序列或映射,你基本上必须做你已经做过的事情,尽管你可以使用辅助函数来减少自定义代码和重复的变量名,例如:
@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))
扩展@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}
我刚开始使用 python 的 attrs
模块,它非常灵活(或者类似地我们可以使用 Python 3.7 DataClasses)。我的一个常见使用模式是 class 作为参数值的容器。我喜欢分配参数时的标签,以及更清晰的属性样式引用值,但我也喜欢有一些功能,这些功能在将值存储在有序字典中时非常有用:
*
像tuple
或list
一样解包以输入函数参数**
在需要或需要关键字传递时解包。
我可以通过向 class
添加三个方法来实现所有这些@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())
然后我可以像这样使用 classes:
params = DataParameters(1, 2, 3, 4, 5)
result1 = function1(100, 200, *params, 300)
result2 = function2(x=1, y=2, **params)
这里的动机是数据classes 提供了方便和清晰。但是,有一些原因导致我不要求我正在编写的模块使用数据 class。希望函数调用应该接受简单的参数,而不是复杂的数据classes.
上面的代码很好,但我想知道我是否遗漏了一些可以让我完全跳过编写函数的东西,因为模式非常清晰。属性按照我希望它们解包的顺序添加,并且可以根据关键字参数的属性名称读取为键值对。
也许这是这样的:
@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
本身是否有我没有找到的东西可以做到这一点。此外,我不确定如何在添加属性时保持属性的顺序并将其转换为键方法。
它没有直接集成到 class 中,但 the asdict
and 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))
它们没有集成到 class 本身,因为这会使 class 表现为序列或映射 无处不在 ,这可能会导致无声预期 TypeError
/AttributeError
时的不当行为。在性能方面,这应该没问题;无论如何,解包都会转换为 tuple
/dict
(它不能直接传递不是 tuple
或 dict
的内容,因为 C API 期望能够在其参数上使用特定于类型的 API)。
如果你真的想让 class 充当序列或映射,你基本上必须做你已经做过的事情,尽管你可以使用辅助函数来减少自定义代码和重复的变量名,例如:
@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))
扩展@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}