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

标签 python properties default propertydescriptor

请问正确的成语是什么?

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

但我不想为所有 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 = ... 未注释的情况下运行它,它将失败:

self.badprop = 'This throws AttributeError: can\'t set attribute'

AttributeError: 无法设置属性

[一如既往,我阅读了有关描述符、隐式描述符、从init 调用它们的 SO 帖子]

最佳答案

我认为您稍微误解了属性的工作原理。没有“默认 setter ”。它在设置 badprop 时抛出 AttributeError 不是因为它还不知道 badprop 是一个属性而不是一个普通属性(如果它是在这种情况下,它只会毫无错误地设置属性,因为这是现在正常的属性行为),但是因为您没有为 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,那么您拥有的是一个只读属性(实际上是一个看起来像属性读取的方法调用)。

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

如果您实际上在属性中做一些事情而不是更改属性的名称,并且您确实希望您的属性是只读的,那么最好的办法可能是在__init__ 直接设置带有下划线前缀的底层数据属性。然后您的类可以直接初始化而不会出现 AttributeErrors,此后属性将在读取属性时执行它们的操作。

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

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

关于python - 如何使用默认属性描述符并从 __init__() 成功分配?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12065570/

相关文章:

Python 编码风格实现

Python 全局变量似乎不能跨模块工作

c# - 如何编写一个动态搜索 TreeView 的树节点的方法?

java - 如何跳转到 Java 属性文件中的某个部分?

C# 传递一个带有 optional 默认值的整数变量

java - 在 Java 中封装私有(private)类的优缺点?

python - 迭代日期时间对象以生成字符串

javascript - 是否可以在 ExtJS 中将存储字段标记为只读?

VIM:为每个新文件设置filetype = txt [无名称]

Python urllib2 返回一个空字符串