如何使用默认的属性描述符并成功地从init_u()赋值?

2024-10-01 09:38:41 发布

您现在位置:Python中文网/ 问答频道 /正文

请问正确的成语是什么?在

我想定义一个包含属性的对象,这些属性可以(可选)从dict初始化(dict来自JSON;它可能不完整)。稍后我可以通过setters修改属性。 实际上有13个以上的属性,我希望能够使用默认的getters和setters,但在这种情况下似乎不起作用:

但我不想为所有prop1... propn编写显式描述符 另外,我想将默认赋值移出__init__()并转移到访问器中。。。但我需要明确的描述。在

最优雅的解决方案是什么?(除了将所有setter调用移出__init__()并转移到方法/类方法_make()中之外?)在

[DELETED COMMENT使用默认描述符的badprop代码是由于前一个SO用户的注释造成的,该用户给您的印象是默认的setter。但是它没有-setter是未定义的,它必然抛出AttributeError。]

class DubiousPropertyExample(object):
    def __init__(self,dct=None):
        self.prop1 = 'some default'
        self.prop2 = 'other default'
        #self.badprop = 'This throws AttributeError: can\'t set attribute'
        if dct is None: dct = dict() # or use defaultdict 
        for prop,val in dct.items():
            self.__setattr__(prop,val)

    # How do I do default property descriptors? this is wrong
    #@property
    #def badprop(self): pass

    # Explicit descriptors for all properties - yukk
    @property
    def prop1(self): return self._prop1
    @prop1.setter
    def prop1(self,value): self._prop1 = value

    @property
    def prop2(self): return self._prop2
    @prop2.setter
    def prop2(self,value): self._prop2 = value

dub = DubiousPropertyExample({'prop2':'crashandburn'})

print dub.__dict__
# {'_prop2': 'crashandburn', '_prop1': 'some default'}

如果在未注释第5行self.badprop = ...的情况下运行此命令,则失败:

^{pr2}$

AttributeError:无法设置属性

[像以前一样,我读过关于描述符的SO帖子,隐式描述符,从init调用它们]


Tags: selfdefault属性initvaluedefpropertydict
2条回答

似乎实现这一点最简单的方法是实现__getattr__和{},这样它们就可以访问解析的JSON dict中的任何键,您应该将其设置为实例成员。或者,可以使用解析后的JSON调用update()上的self.__dict__,但这并不是处理事情的最佳方法,因为这意味着输入dict可能会践踏实例的成员。在

对于你的setter和getter,你应该只在它们确实做了一些特殊的事情,而不是直接设置或检索有问题的值时才创建它们。Python不是java(C++)或其他任何东西,你不应该试图模仿那些语言中常见的私有/SET/GET范式。在

我觉得你有点误解了属性的工作原理。没有“默认设置程序”。它在设置badprop时抛出一个AttributeError,这并不是因为它还不知道{}是一个属性而不是一个普通属性(如果是这样的话,它只会毫无错误地设置属性,因为这现在是正常的属性行为),而是因为您没有为badprop提供setter,而只是一个getter。在

看看这个:

>>> class Foo(object):
    @property
    def foo(self):
        return self._foo
    def __init__(self):
        self._foo = 1


>>> f = Foo()
>>> f.foo = 2

Traceback (most recent call last):
  File "<pyshell#12>", line 1, in <module>
    f.foo = 2
AttributeError: can't set attribute

即使在构造实例之后,__init__之外也不能设置这样的属性。如果您只使用@property,那么您拥有的是一个只读属性(实际上是一个类似于属性read的方法调用)。在

如果您在getter和setter中所做的只是将读/写访问重定向到一个名称相同但带有下划线的属性,那么到far最简单的事情就是完全去掉这些属性,只使用普通属性。Python不是Java(即使在Java中,我也不相信私有字段具有明显的公共getter/setter的优点)。外部世界可以直接访问的属性是“公共”接口中非常合理的一部分。如果您后来发现在读/写属性时需要运行一些代码,那么您可以在不更改接口的情况下使其成为属性然后(这实际上是描述符最初的用途,而不是,这样我们就可以开始为每个属性编写Java风格的getter/setter)。在

如果您实际上在属性中做一些事情而不是更改属性的名称,并且您希望您的属性是只读的,那么您最好的选择可能是将__init__中的初始化视为直接设置带有下划线的底层数据属性。然后,您的类可以直接初始化而不需要AttributeErrors,然后属性将在读取属性时完成它们的工作。在

如果您实际上在属性中做了一些事情,而不是更改属性的名称,并且希望您的属性是可读写的,那么您需要实际指定当您获取/设置它们时会发生什么。如果每个属性都有独立的自定义行为,那么没有比为每个属性显式提供getter和setter更有效的方法了。在

如果您在每个getter/setter中运行完全相同(或非常相似)的代码(这不仅仅是在真正的属性名中添加下划线),这就是为什么您反对将它们都写出来(这是正确的!),那么通过实现__getattr____getattribute__、和{}的一些功能,可能会更好地为您服务。这允许您每次将属性读/写重定向到相同的代码(以属性名称作为参数),而不是每个属性的两个函数(获取/设置)。在

相关问题 更多 >