[英]Dynamic Property Getter and Setter
有没有办法可以创建具有不同数量属性的 class 并为这些属性创建相应的 getter 和 setter?
我的示例代码如下所示。
class A:
def __init__(self, **options):
self._options = options
for key, value in options.items():
setattr(self, f"_{key.lower()}", value)
setattr(self, f"get_{key.lower()}", lambda : getattr(self, f"_{key.lower()}", None))
a = A(dimension = 2, name = "Unknown Alphabet")
for key, value in a.__dict__.items():
print(f"A[{key.lower()}] = {value}")
print(a.get_name())
print(a.get_dimension())
new_santa_clause = A(location = "North Pole", vehicle = "Teleportation", naughty_kids_list = [])
for key, value in new_santa_clause.__dict__.items():
print(f"SantaClause[{key}] = {value}")
print(new_santa_clause.get_location())
print(new_santa_clause.get_vehicle())
print(new_santa_clause.get_naughty_kids_list())
执行的Output如下图
A[_options] = {'dimension': 2, 'name': 'Unknown Alphabet'}
A[_dimension] = 2
A[get_dimension] = <function A.__init__.<locals>.<lambda> at 0x000002334B4EC678>
A[_name] = Unknown Alphabet
A[get_name] = <function A.__init__.<locals>.<lambda> at 0x000002334B4EC1F8>
Unknown Alphabet
Unknown Alphabet
SantaClause[_options] = {'location': 'North Pole', 'vehicle': 'Teleportation', 'naughty_kids_list': []}
SantaClause[_location] = North Pole
SantaClause[get_location] = <function A.__init__.<locals>.<lambda> at 0x000002334B4ECA68>
SantaClause[_vehicle] = Teleportation
SantaClause[get_vehicle] = <function A.__init__.<locals>.<lambda> at 0x000002334B4EC438>
SantaClause[_naughty_kids_list] = []
SantaClause[get_naughty_kids_list] = <function A.__init__.<locals>.<lambda> at 0x000002334B4ECCA8>
[]
[]
[]
值设置正确,吸气剂也正确创建。 只是在执行 getter 时,没有返回正确的值。
那么,你可以做一些事情有点像你想要的,但在Python,性质查到的基于对象的类,这意味着你需要创建一个单独的类,找你想要的属性的每个组合的。
这就是我的意思:
def property_maker(name):
storage_name = '_' + name.lower()
@property
def prop(self):
return getattr(self, storage_name)
@prop.setter
def prop(self, value):
setattr(self, storage_name, value)
return prop
def make_class(classname, **options):
class Class: pass
Class.__name__ = classname
for key, value in options.items():
storage_name = '_' + key.lower()
setattr(Class, storage_name, value)
setattr(Class, key.lower(), property_maker(key))
return Class
A = make_class('A', dimension=2, name="Unknown Alphabet")
a = A()
print(a.name) # -> Unknown Alphabet
print(a.dimension) # -> 2
SantaClaus = make_class('SantaClaus', location="North Pole", vehicle="Teleportation",
naughty_kids_list=[])
santa_clause = SantaClaus()
print(santa_clause.location) # -> North Pole
print(santa_clause.vehicle) # -> Teleportation
print(santa_clause.naughty_kids_list) # -> []
这不是实现您想要的(使用属性)的pythonic 方式。 但是了解正在发生的事情很有趣。
让我们专注于代码的稍微修改版本:
class A:
def __init__(self, **options):
for key, value in options.items():
setattr(self, f"get_{key}", lambda: value)
a = A(x=1, y=2)
print(a.get_x()) # 2
print(a.get_y()) # 2
lambda: value
是什么意思? 一个返回value
的函数。 是的,但没有任何功能。 这个函数是一个闭包,它可以访问A.__init__
的局部变量value
的A.__init__
。 初始化结束时 value 的value
是多少? 循环的最后一个value
,即2
。
当你调用a.get_x()
解释器所在的属性get_x
的a
:这是一个封闭。 然后定位 value 的value
,即 2,并执行 lambda 函数: return value
,即return 2
。
@wjandrea 提供的链接解释了@property、@attribute.setter 和@attribute.deleter 的使用,但这里并不真正需要它,例如因为您想要“只读”属性或setter 中的附加功能。 这将做得很好:
class Foo:
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
foo = Foo(spam='SPAM', eggs="EGGS")
print(foo.spam)
print(foo.eggs)
# assign new value to foo.spam
foo.spam='bar'
print(foo.spam)
你正在做的不是真正的 pythonic也不是创建property
。 它只是创建具有lambda
“getter”方法的私有实例属性。
问题在于for
循环中的lambda
表达式使用key
变量的方式, for
循环的每次迭代都会为它分配一个不同的值 - 结果在所有单独的“getter”函数的末尾都将引用相同的变量将具有在for
循环中分配给它的最后一个值。 为了避免这种情况并修复您的代码,您可以在创建lambda
“getter”function 时为key
提供一个“默认参数值” ,如下所示:
class A:
def __init__(self, **options):
# self._options = options # NOT NEEDED.
for key, value in options.items():
setattr(self, f"_{key.lower()}", value)
# CHANGED: added default parameter value to lambda in next statement.
setattr(self, f"get_{key.lower()}",
lambda key=key: getattr(self, f"_{key.lower()}", None))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.