值分配Python对象,会发生什么?

时间:2014-04-16 18:18:07

标签: python python-2.7 object variable-assignment

如果我创建一个对象:

class eggs(object):
    """ This wasn't needed """

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

如果我这样做,我能理解:

class eggs(object):
    """ This wasn't needed """

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


if __name__ == "__main__":
    foo = eggs()
    foo.bar = 5
    print foo

我明白了:

bar 5
<__main__.eggs object at 0x8f8bb0c>

然而,当我这样做时:

if __name__ == "__main__":
    foo = eggs()
    foo = 5
    print foo

我明白了:

5

我的问题是&#34;魔法&#34;调用foo = 5时调用方法?

例如我这样做:

class eggs(object):
    """ This wasn't needed """
    def __init__(self):
        self.bacon = False

    def __setattr__(self, name, value):
        if not name == "bacon": 
            raise Exception("No way Hosay")
        if value not in [True, False]:
            raise Exception("It wasn't supposed to be like this")
        super(eggs, self).__setattr__(name, value)
        print "Variable became {0}".format(value)




if __name__ == "__main__":
    foo = eggs()
    foo.bacon = True
    foo.bacon = 5 > 4
    foo.bacon = True or False
    # ETC

返回:

Variable became False
Variable became True
Variable became True
Variable became True

我想在没有培根的情况下这样做。

3 个答案:

答案 0 :(得分:2)

你误解了变量在python中的工作方式 - 它们只是对象的引用,而重新分配只是用另一个引用覆盖引用。

作为一种简化的解释,假设变量赋值的工作方式与分配字典成员相似。如果我们调用该字典variables,我们可以重写

foo = 5

作为

variables["foo"] = 5

现在,您希望variables["foo"]当前值(如果它存在)通知分配,这意味着前一个语句将变得类似于以下内容:

variables["foo"].__somemethod__() #call magic method to notify reassignment
variables["foo"] = 5 # set new value

虽然您实际上可以使用dict子类实现此确切行为,但这并不是CPython中的变量赋值实现的方式。变量引用的对象不会以任何方式通知variables["foo"]不再引用它的事实。所有发生的事情是先前引用的对象(如果有的话)的引用计数减1。

正如Stick的回答所示,有一个__del__方法在对象被垃圾收集时被调用,这可能或者可能不足以满足您的实际用例。但是当这被调用时实际上取决于gc,因此它可以表现出一些时髦的行为;见例如this answer讨论一些怪癖。此外,在python3.4之前,甚至不能保证在调用__del__时任何引用的对象仍然存在,现在已经修复了这个问题:http://legacy.python.org/dev/peps/pep-0442/

如果您确实需要通知被覆盖的引用(并且我怀疑没有更好的方法来解决您的实际问题),请考虑使用子类或monkeypatched dict对于您要跟踪的所有参考。您需要覆盖__setattr__方法以执行覆盖引用时要执行的操作。

答案 1 :(得分:1)

如果你想变得奇怪,你可以覆盖__del__,但这通常是一整套蠕虫。

>>> class A(object):
...     def __del__(self):
...             print "oh noooo!"
... 
>>> Stick = A()
>>> Stick = 5
oh noooo!
>>> Stick
5
>>> 

编辑:听起来你想知道什么时候创建或删除对象?

也许只是一个装饰者就够了?

>>> def talk(func):
...     def inner(*args, **kwargs):
...             print "I'm doing things!"
...             return func(*args, **kwargs)
...     return inner
... 
>>> class A(object):
...     @talk
...     def __init__(self):
...             pass
...     @talk
...     def __del__(self):
...             pass
... 
>>> Stick = A()
I'm doing things!
>>> del Stick
I'm doing things!
>>> Stick
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Stick' is not defined
>>>

这适用于实例化和删除,以及变量的重新分配。

当然 - 如果在调用__init____del__时需要不同的方法,则需要使用不同的装饰器。只需将您的“实际工作”放在嵌套函数中即可。

def wrapper(func):
    def inner(*args, **kwargs):
        #your code here!
        #your code here as well!
        return func(*args, **kwargs)
    return inner

对于使用__del__进行捣乱时,只是对goofballs和诸如此类的东西持怀疑态度,但我认为这是一种相对理智的方式。

答案 2 :(得分:0)

你应该看看Descriptors,它们是你想要的最接近的东西