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

How to use default property descriptors and successfully assign from __init__()?

请问这个的正确成语是什么?

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

但我不想为所有的prop1... propn编写明确的描述符。另外,我想把默认分配从__init__()移到访问器中…但是我需要一个表达式描述符。

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

[删除的注释使用默认描述符的badprop代码是由以前的so用户的注释引起的,他给人的印象是它给了您一个默认的setter。但事实并非如此——setter是未定义的,它必然会抛出AttributeError]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 = ...未注释的情况下运行此命令,则失败:

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

attributeError:无法设置属性

[和以往一样,我阅读了关于描述符、隐式描述符、从init调用它们的so文章]


我觉得你有点误解了物业是如何运作的。没有"默认设置器"。它在设置badprop时抛出了一个AttributeError,不是因为它还不知道badprop是一个属性而不是一个普通属性(如果是这样的话,它只会毫无错误地设置该属性,因为这现在是普通属性的行为),而是因为您没有为badprop提供一个setter,只有一个getter。

看看这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 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中所做的一切都是将读/写访问重定向到一个同名的属性,但前面加了一个下划线,那么到目前为止,最简单的事情就是完全去掉这些属性,只使用普通属性。Python不是Java(甚至在Java中,我也不相信私有领域和显而易见的公共吸气剂/定位器的优点)。外部世界可以直接访问的属性是"公共"接口的一个非常合理的部分。如果稍后发现在读取/写入属性时需要运行一些代码,则可以在不更改接口的情况下使其成为属性(这实际上是描述符最初的目的,而不是我们可以开始为每一个属性编写Java风格的GETSt/Stter)。

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

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

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


这看起来最简单的方法就是实现__getattr____setattr__,这样它们就可以访问解析后的json dict中的任何键,您应该将其设置为实例成员。或者,您可以使用解析的JSON在self.__dict__上调用update(),但这并不是最好的方法,因为这意味着输入dict可能会践踏实例的成员。

对于setter和getter,只有当它们实际执行了一些特殊的操作,而不是直接设置或检索相关的值时,才应该创建它们。Python不是Java(或C++或其他任何东西),不应该试图模仿那些语言中常见的私有/SET/GET范式。