3

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

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

但我不想为所有内容编写显式描述符prop1... propn 另外,我想将默认分配移出__init__()和移入访问器......但是我需要显式描述符。

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

[删除评论 使用默认描述符的 badprop 的代码是由于以前的 SO 用户的评论,他给人的印象是它给了你一个默认的设置器。但它没有 - 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 帖子]

4

3 回答 3

7

我认为您对属性的工作方式略有误解。没有“默认设置器”。它抛出一个AttributeErroron 设置badprop不是因为它还不知道这badprop是一个属性而不是一个正常的属性(如果是这种情况,它只会设置没有错误的属性,因为现在这是正常的属性行为),而是因为你没有没有为 提供一个 setter badprop,只提供一个 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__。这些允许您每次将属性读取/写入重定向到相同的代码(使用属性名称作为参数),而不是每个属性的两个函数(获取/设置)。

于 2012-08-22T02:44:29.420 回答
4

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

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

于 2012-08-22T02:36:38.653 回答
0

我只是将字典放在本地范围内并在那里获取/设置我的属性。

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
于 2021-03-19T16:53:37.233 回答