如下面的代码所示,为什么我不能使用__setattr__
在dict上设置值,该dict是重载方法的类的一部分?我希望b.hello
不存在。
class MyClass():
datastore = {}
def __init__(self):
self.datastore = {}
def __getattr__(self, key):
return self.datastore[key]
def __setattr__(self, key, value):
self.datastore[key] = value
a = MyClass()
b = MyClass()
a.hello = "err"
print a.hello # err
print b.hello # err
答案 0 :(得分:2)
b.hello
打印字符串“err”,因为datastore
是类本身的属性,而不是类的对象。因此,当您在a
初始化时,b
也可以访问它。
因此,请从班级中删除datastore = {}
。
此外,来自Python docs:
如果
__setattr__()
想要分配给实例属性,它应该 不是简单地执行self.name = value
- 这会导致递归 呼唤自己。相反,它应该在字典中插入值 实例属性,例如self.__dict__[name] = value
。对于 它不是新的类,而是访问实例字典 应该调用具有相同名称的基类方法,例如,object.__setattr__(self, name, value)
。
因此,请将您的代码更改为:
class MyClass(object): # Use new style classes
def __init__(self):
object.__setattr__(self, 'datastore', {}) # This prevents infinite recursion when setting attributes
def __getattr__(self, key):
return self.datastore[key]
def __setattr__(self, key, value):
self.datastore[key] = value
a = MyClass()
b = MyClass()
a.hello = "err"
print a.hello # Works
print b.hello # Gives an error
答案 1 :(得分:1)
首先让我解释一下为什么会这样:
class MyClass():
datastore = {}
def __init__(self):
self.datastore = {} # calls __setattr__
如您所见,__init__
中的第一个变量定义最终会调用__setattr__
def __setattr__(self, key, value):
self.datastore[key] = value
该实例尚未具有datastore
属性,因为它仍处于定义过程中。因此,此行self.datastore[key] = value
首先尝试在实例的datastore
中查找__dict__
但无法找到它!然后它在类树中查找一个级别,它确实找到它,作为类属性!
记住这一点:
class MyClass():
datastore = {}
这一开始非常令人困惑,因为你同时拥有一个实例变量和一个具有相同名称的类变量,你应该不同时拥有它们。
因此,您可以将__init__
更改为:
object.__setattr__(self, 'datastore', {})
像@Dhara建议的那样,或者你可以使用我推荐的更通用的方法:
super(MyClass, self).__setattr__('datastore', {})
后一个选项仅适用于您应该使用的新式类(在各方面都更好!)!
只需将object
添加为超类
class MyClass(object): # In Py3k you don't need to since all classes are newstyle
有一点需要注意:
def __setattr__(self, key, value):
self.datastore[key] = value
仅适用,因为您正在设置字典的键而不是实例的属性。小心不要做
之类的事情 def __setattr__(self, key, value):
self.datastore = {} # for example
因为这会导致无限递归,如果你想在__setattr__
中做类似的事情,请使用之前的相同技巧:
def __setattr__(self, key, value):
super(MyClass, self).__setattr__('datastore', {})
最终结果如下:
class MyClass(object):
def __init__(self):
super(MyClass, self).__setattr__('datastore', {})
def __getattr__(self, key):
return self.datastore[key]
def __setattr__(self, key, value):
self.datastore[key] = value
a = MyClass()
b = MyClass()
a.hello = "err"
print a.hello
print b.hello