请问这是正确的习惯用法?
我想定义一个包含属性的对象,这些属性可以(可选)从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 调用它们
答案 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