繁体   English   中英

动态属性 Getter 和 Setter

[英]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__的局部变量valueA.__init__ 初始化结束时 value 的value是多少? 循环的最后一个value ,即2

当你调用a.get_x()解释器所在的属性get_xa :这是一个封闭。 然后定位 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM