如何使用默认属性描述符并从__init __()成功分配?

smc*_*mci 3 python default properties propertydescriptor

请问这个正确的习惯用法是什么?

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

但是我不想为所有人编写显式描述符.prop1... propn 另外,我想将默认赋值移出__init__()访问者...但是我需要expicit描述符.

什么是最优雅的解决方案?(除了将所有的setter调用移出__init__()一个方法/ classmethod _make()?)

[删除评论]使用默认描述符的badprop代码是由之前的SO用户发表的评论,他给人的印象是它给你一个默认的设置器.但事实并非如此 - 设定者未定义且必然会抛出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'}
Run Code Online (Sandbox Code Playgroud)

如果您使用第5行self.badprop = ...取消注释运行它,它将失败:

self.badprop = 'This throws AttributeError: can\'t set attribute'
Run Code Online (Sandbox Code Playgroud)

AttributeError:无法设置属性

[与以往一样,我阅读描述符,隐式描述符的SO帖子,从init调用它们]

Ben*_*Ben 7

我认为你对属性如何运作有点误解.没有"默认设置器".它抛出一个AttributeErroron设置badprop不是因为它还不知道这badprop是一个属性而不是一个普通的属性(如果是这样的话,它只会设置没有错误的属性,因为现在这是正常的属性),但是因为你没有没有为badprop吸气剂提供装置.

看看这个:

>>> 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
Run Code Online (Sandbox Code Playgroud)

__init__在构造实例之后,您甚至无法在外部设置此类属性.如果您只是使用@property,那么您拥有的是只读属性(实际上是一个看起来像属性读取的方法调用).

如果您在getter和setter中所做的一切都是重定向对同名属性的读/写访问,但前缀是下划线,那么到目前为止最简单的方法是完全删除属性并使用普通属性.Python不是Java(甚至在Java中我也不相信具有明显公共getter/setter的私有字段的优点).外部世界可直接访问的属性是"公共"界面中非常合理的一部分.如果你以后发现每当读取/写入一个属性时你需要运行一些代码,你就可以把它变成一个属性然后不用改变你的界面(这实际上就是那些描述符最初的用途,而不是让我们开始编写Java风格的getter)每个属性的/ setter).

如果您实际上在属性中执行某些操作而不是更改属性的名称,并且您确实希望您的属性是只读的,那么最好的办法是将初始化__init__视为直接使用下划线设置基础数据属性前缀.然后你的类可以直接初始化AttributeErrors,然后属性将在读取属性时执行.

如果你实际做比改变属性的名称等属性的东西,你想你的属性是可读写的,那么你就需要实际指定在获取/设置他们发生了什么.如果每个属性都有独立的自定义行为,那么没有比为每个属性显式提供getter和setter更有效的方法.

如果你在每个getter/setter中运行完全相同(或非常相似)的代码(并且它不只是在真实属性名称中添加下划线),那就是你反对将它们全部写出来的原因(这是正确的!) ,那么你可能会更好地实现一些服务__getattr__,__getattribute____setattr__.这些允许您每次将属性读/写重定向到相同的代码(以属性的名称作为参数),而不是每个属性的两个函数(获取/设置).