Stu*_*ens 6 python properties decorator
我有一个 python 类,它有几个属性。我想实现一个方法,它将返回一些属性作为字典。我想用装饰器标记属性。这是一个例子:
class Foo:
@export_to_dict # I want to add this property to dict
@property
def bar1(self):
return 1
@property # I don't want to add this propetry to dict
def bar2(self):
return {"smth": 2}
@export_to_dict # I want to add this property to dict
@property
def bar3(self):
return "a"
@property
def bar4(self):
return [2, 3, 4]
def to_dict(self):
return ... # expected result: {"bar1": 1, "bar3": "a"}
Run Code Online (Sandbox Code Playgroud)
实现它的一种方法是使用export_to_dict装饰器为属性设置一个附加属性,如下所示:
def export_to_dict(func):
setattr(func, '_export_to_dict', True)
return func
Run Code Online (Sandbox Code Playgroud)
并通过调用_export_to_dict时的属性来搜索属性。to_dict
还有其他方法可以完成任务吗?
标记每个属性会强制to_dict在每次调用时扫描所有方法/属性,这是缓慢且不优雅的。这是一个独立的替代方案,可以使您的示例用法保持相同。
通过创建export_to_dict一个类,我们可以使用__set_name__(Python 3.6+)来获取 Foo 类的引用并向其添加新属性。现在to_dict确切地知道要提取哪些属性,并且由于该列表是针对每个类的,因此您可以注释不同的类而不会发生冲突。
当我们这样做时,我们可以export_to_dict自动to_dict为任何具有导出属性的类生成函数。
装饰器还会在类创建后恢复原始属性,因此这些属性可以正常工作,不会对性能产生任何影响。
class export_to_dict:
def __init__(self, property):
self.property = property
def __set_name__(self, owner, name):
if not hasattr(owner, '_exported_properties'):
owner._exported_properties = []
assert not hasattr(owner, 'to_dict'), 'Class already has a to_dict method'
owner.to_dict = lambda self: {prop.__name__: prop(self) for prop in owner._exported_properties}
owner._exported_properties.append(self.property.fget)
# We don't need the decorator object anymore, restore the property.
setattr(owner, name, self.property)
class Foo:
@export_to_dict # I want to add this property to dict
@property
def bar1(self):
return 1
@property # I don't want to add this propetry to dict
def bar2(self):
return {"smth": 2}
@export_to_dict # I want to add this property to dict
@property
def bar3(self):
return "a"
@property
def bar4(self):
return [2, 3, 4]
# to_dict is not needed anymore here!
print(Foo().to_dict())
{'bar1': 1, 'bar3': 'a'}
Run Code Online (Sandbox Code Playgroud)
如果您不希望 Foo 类具有额外的属性,您可以将映射存储在静态 dict 中export_to_dict.properties_by_class = {class: [properties]}。
如果您需要支持属性设置者,情况会稍微复杂一些,但仍然可行。传递property.setter是不够的,因为 setter 替换了 getter 并且__set_name__不被调用(毕竟它们具有相同的名称)。
这可以通过拆分注释过程并为property.setter.
class export_to_dict:
# Used to create setter for properties.
class setter_helper:
def __init__(self, setter, export):
self.setter = setter
self.export = export
def __set_name__(self, owner, name):
self.export.annotate_class(owner)
setattr(owner, name, self.setter)
def __init__(self, property):
self.property = property
@property
def setter(self):
return lambda fn: export_to_dict.setter_helper(self.property.setter(fn), self)
def annotate_class(self, owner):
if not hasattr(owner, '_exported_properties'):
owner._exported_properties = []
assert not hasattr(owner, 'to_dict'), 'Class already has a to_dict method'
owner.to_dict = lambda self: {prop.__name__: prop(self) for prop in owner._exported_properties}
owner._exported_properties.append(self.property.fget)
def __set_name__(self, owner, name):
self.annotate_class(owner)
# We don't need the decorator object anymore, restore the property.
setattr(owner, name, self.property)
class Foo:
@export_to_dict # I want to add this property to dict
@property
def writeable_property(self):
return self._writeable_property
@writeable_property.setter
def writeable_property(self, value):
self._writeable_property = value
foo = Foo()
foo.writeable_property = 5
print(foo.to_dict())
{'writeable_property': 5}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
460 次 |
| 最近记录: |