【问题标题】:Value Assignment Python Objects, what happens?赋值 Python 对象,会发生什么?
【发布时间】:2014-05-31 18:44:48
【问题描述】:

如果我创建一个对象:

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

我的问题是调用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

我想不吃培根。

【问题讨论】:

  • 你不只是重新分配变量吗?您不再设置foo 的属性,而是赋予foo 一个全新的值,完全不同的类型。在你这样做之后,它甚至不属于eggs。没有什么神奇的方法,只是基本的编程逻辑。
  • 我正在重新分配变量,但我想要一个在您尝试更改它时执行某些操作的对象?如果这有意义吗?我觉得它应该是可行的,但我认为它是错误的。
  • 那是不可能的。赋值运算符重新绑定名称;它不会让以前绑定到该名称的对象做任何事情(嗯,除了间接地,通过减少其引用计数并因此可能调用其__del__ 方法)。
  • @Noelkd 你来自 C++ 背景,对吧?在大多数语言(包括 Python)中,变量和对象是不同的,赋值适用于变量而不是变量引用的对象。 C++ 有点离群 - 对象类型的 C++ 变量实际上“包含”对象而不是引用它,operator= 是对象接口的一部分。
  • “魔法”方法被称为__get____set__,但事情并不能完全按照您的意愿工作。请参阅有关描述符的 Python 文档

标签: python python-2.7 object variable-assignment


【解决方案1】:

您误解了变量在 python 中的工作方式——它们只是对对象的引用,而重新赋值只是用另一个引用覆盖引用。

作为一个简化的解释,假设变量赋值的工作方式类似于分配字典的成员。如果我们称那个字典为variables,我们可以重写

foo = 5

作为

variables["foo"] = 5

现在,您希望 variables["foo"]current 值(如果它甚至存在)被通知分配,这意味着前面的语句将类似于以下语句:

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

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

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

如果您确实需要在引用被覆盖时收到通知(并且我怀疑没有更好的方法来解决您的实际问题),请考虑为您想要的所有引用使用子类或猴子补丁dict追踪。您需要覆盖 __setattr__ 方法来执行您希望在覆盖引用时发生的操作。

【讨论】:

  • 为此,WeakValueDictionary 可能真的很重要
  • +1 用于明确说明“所发生的只是先前引用的对象(如果有的话)的引用计数减一”。我需要对循环进行澄清,在该循环中我正在创建对象并在每次迭代中将它们分配给相同的变量。
【解决方案2】:

如果你想变得古怪,你可以覆盖__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__ 混在一起时,请警惕傻瓜之类的东西,但我认为这是一种相对理智的做法。

【讨论】:

  • 这只会在非常具体的限制下工作,即 1. gc 在未引用时立即丢弃对象,以及 2. 对象仅被引用一次。例如。如果你这样做 a1 = A(); b = a1; a = 0 那么 __del__ 在重新分配 a 后当然不会被调用...
  • 当然,但是......无论如何,这样做完全很奇怪,对吧:/
【解决方案3】:

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

【讨论】:

  • 看不出这有什么关系,你能解释一下吗?
  • 好吧,对于初学者来说,他们确实有一个在分配时调用的“魔术”方法。
  • 关于变量分配?您必须引用官方文档的特定短语才能让我相信这一点。 AFAIK 他们只有一个用于属性分配,其他一切在 CPython 中都是黑魔法。
  • 同意,所以我说:“最接近”
  • 那你误解了这个问题,OP专门问的是变量赋值而不是属性访问...
猜你喜欢
  • 2019-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多