4

如下面的代码所示,为什么我不能__setattr__在作为重载方法的类的一部分的 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
4

2 回答 2

3

b.hello打印您的字符串“err”,因为datastore它是类本身的属性,而不是类对象的属性。因此,当你在 中初始化它时ab也可以访问它。

因此,datastore = {}从类中删除。

此外,来自 Python文档

如果__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
于 2013-04-23T14:20:01.707 回答
3

我先解释一下为什么会出现这种情况:

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 
于 2013-04-24T01:11:07.797 回答