Python,如何在字典对象上使用__setattr__,它是重载方法的类的一部分?

时间:2013-04-23 13:57:45

标签: python python-2.7 setattr

如下面的代码所示,为什么我不能使用__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

2 个答案:

答案 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