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

时间:2012-08-22 02:22:05

标签: python properties default 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'}

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

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

AttributeError:无法设置属性

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

3 个答案:

答案 0 :(得分:6)

我认为你对属性如何运作有些误解。没有“默认设置器”。它会在设置AttributeError时抛出badprop,因为它还不知道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中所做的一切都是将读/写访问重定向到同名属性但前缀为下划线,那么最简单的方法就是获取完全摆脱属性,只使用普通属性。 Python不是Java(甚至在Java中我也不相信具有明显公共getter / setter的私有字段的优点)。外部世界可直接访问的属性是“公共”界面中非常合理的一部分。如果您以后发现只要读取/写入某个属性就需要运行一些代码,那么您可以将其设置为然后,而无需更改您的界面(这实际上是最初的描述符,,以便我们可以开始为每个属性编写Java样式的getter / setter。)

如果您实际上在属性中执行某些操作而不是更改属性的名称,并且您执行希望您的属性是只读的,那么您最好的选择可能是在处理初始化__init__直接设置基础数据属性,前缀为下划线。然后,您的类可以在没有AttributeErrors的情况下直接初始化,然后属性将在读取属性时执行。

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

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

答案 1 :(得分:2)

似乎最简单的方法就是实现__getattr____setattr__,这样他们就可以访问解析后的JSON dict中的任何键,您应将其设置为实例成员。或者,您可以使用已解析的JSON在update()上调用self.__dict__,但这并不是解决问题的最佳方式,因为这意味着您的输入字典可能会破坏您实例的成员。

对于你的setter和getter,除了直接设置或检索有问题的值之外,你应该只创建它们。 Python不是Java(或C ++或其他任何东西),你不应该试图模仿这些语言中常见的private / set / get范例。

答案 2 :(得分:0)

我只是将 dict 放在本地范围内,然后在那里获取/设置我的属性。

class test(object):
    def __init__(self,**kwargs):
        self.kwargs = kwargs
        #self.value = 20 asign from init is possible
    @property
    def value(self):
        if self.kwargs.get('value') == None:
            self.kwargs.update(value=0)#default
        return self.kwargs.get('value')
    @value.setter
    def value(self,v):
        print(v) #do something with v
        self.kwargs.update(value=v)

x = test()
print(x.value)
x.value = 10
x.value = 5

输出

0
10
5