3

今天 Python 咬了我一口。我试图在其__setattr__实现中访问对象的属性 - 我不知道如何。这是我迄今为止尝试过的:

class Test1(object):
    def __init__(self):
        self.blub = 'hi1'

    def __setattr__(self, name, value):
        print self.blub

class Test2(object):
    def __init__(self):
        self.blub = 'hi2'

    def __setattr__(self, name, value):
        print object.__getattr__(self, 'blub')

class Test3(object):
    def __init__(self):
        self.blub = 'hi3'

    def __setattr__(self, name, value):
        print object.__getattribute__(self, 'blub')

class Test4(object):
    def __init__(self):
        self.blub = 'hi4'

    def __setattr__(self, name, value):
        print self.__getattr__('blub')

class Test5(object):
    def __init__(self):
        self.blub = 'hi5'

    def __setattr__(self, name, value):
        print self.__getattribute__('blub')

class Test6(object):
    def __init__(self):
        self.blub = 'hi6'

    def __setattr__(self, name, value):
        print self.__dict__['blub']

测试:

try:
    TestX().bip = 'bap'
except Exception as ex:
    print ex

X1到_6

输出:

'Test1' object has no attribute 'blub'
type object 'object' has no attribute '__getattr__'
'Test3' object has no attribute 'blub'
'Test4' object has no attribute '__getattr__'
'Test5' object has no attribute 'blub'
'blub'

有什么建议么?

4

5 回答 5

4

因为在里面__init__它试图设置blub哪些调用__setattr__;它没有设置任何东西,而是尝试访问(和打印)blub,什么也没找到并引发错误。检查这个:

>>> class Test2(object):
  def __init__(self):
    print "__init__ called"
    self.blub = 'hi2'
    print "blub was set"

  def __setattr__(self, name, value):
    print "__setattr__ called"
    print self.blub


>>> Test2()
__init__ called
__setattr__ called

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    Test2()
  File "<pyshell#9>", line 4, in __init__
    self.blub = 'hi2'
  File "<pyshell#9>", line 9, in __setattr__
    print self.blub
AttributeError: 'Test2' object has no attribute 'blub'
>>> 
于 2013-08-13T08:40:09.247 回答
2

OP,你还没有告诉我们整个故事。你不只是运行这样的代码:

TestX().bip = 'bap'

你运行这样的代码:

try:
    TestX().bip = 'bap'
except Exception as ex:
    print ex

有很大的不同。你为什么问?好吧,乍一看,您的输出似乎表明它Test6有效,并且一些评论和答案认为它确实有效。为什么它看起来有效?阅读代码,它不可能工作。仔细检查源代码会发现,如果它起作用,它应该打印出来hi6,而不是'blub'.

print ex我在in 行放了一个断点pdb来检查异常:

(Pdb) ex
KeyError('blub',)
(Pdb) print ex
'blub'

出于某种原因print ex,不会KeyError: blub像您期望的那样打印,而只是'blub',这就是为什么Test6似乎可以工作的原因。

所以我们已经澄清了这一点。将来,请不要遗漏这样的代码,因为它可能很重要。

所有其他答案都正确指出您尚未设置要打印的属性,这是您的问题。您之前接受的答案,在您接受此答案之前,规定了以下解决方案:

def __setattr__(self, name, value):
    self.__dict__[name] = value
    print self.__dict__[name]

虽然这个解决方案确实有效,但它不是一个好的设计。这是因为您可能希望在某个时候更改基类,并且该基类在设置和/或获取属性时可能会产生重要的副作用,或者它可能根本不存储属性self.__dict__!最好的设计是避免弄乱__dict__.

正确的解决方案是调用父__setattr__方法,这是由至少一个其他答案提出的,尽管 python 2.x 的语法错误。这是我的做法:

def __setattr__(self, name, value):
    super(Test6, self).__setattr__(name, value)
    print getattr(self, name)

如您所见,我使用getattr而不是__dict__动态查找属性。与__dict__直接使用不同,这将酌情调用self.__getattr__or self.__getattribute__

于 2013-08-13T09:14:00.007 回答
1
print self.__dict__['blub']

打印出blub哪个是正确的。您必须先设置新值,因为 python 不会为您执行此操作,如下所示:

def __setattr__(self, name, value):
    self.__dict__[name] = value
    print self.__dict__[name]

然后test6.blub = 'test'应该打印出来test

编辑:

正如@Lauritz 所建议的,您也可以使用

def __setattr__(self, name, value):
    super(Test6, self).__setattr__(name, value)
    print self.__dict__[name]

它调用父函数,所以如果你的超类已经有一个__setattr__函数,它不会被覆盖。

于 2013-08-13T08:37:06.970 回答
1

你从来没有真正设置过你的属性,__setattr__所以对象当然没有它。

def __setattr__(self, name, value):
    self.name = value
        # this doesn't work, since it calls itself (the __setattr__)

def __setattr__(self, name, value):
    super().__setattr__(name, value)
        # call the default implementation directly in Py 3.x

def __setattr__(self, name, value):
    super(TestX, self).__setattr__(name, value) # for Python 2.x

当然,单独这样做没有任何好处,您通常希望在此周围添加一些功能,例如某些条件、调试打印、缓存或任何您需要的东西。

于 2013-08-13T08:37:19.987 回答
1

__setattr__在班级上工作,所以当你超越时 - 你需要让它实际设置属性......否则,它永远不会存在......例如:

object.__setattr__(self, name, value)

因此,在您的__init__, 当您这样做时self.blub = 'hi',实际上是无操作。

于 2013-08-13T08:40:28.903 回答