Python 3.x 有人能解释一下为什么Python解释器没有';在这个使用装饰器的例子中,你不会感到困惑吗?

Python 3.x 有人能解释一下为什么Python解释器没有';在这个使用装饰器的例子中,你不会感到困惑吗?,python-3.x,python-decorators,Python 3.x,Python Decorators,我试图从中了解装饰师 我有两个简单的文件a.py和b.py a、 py具有以下代码: def f1(input_func): def inner_func(): input_func() input_func() return inner_func import a def saying(): print('Hello') saying = a.f1(saying) saying() b、 py具有以下代码: def f1(input_

我试图从中了解装饰师

我有两个简单的文件a.py和b.py

a、 py具有以下代码:

def f1(input_func):
    def inner_func():
        input_func()
        input_func()
    return inner_func
import a
def saying():
    print('Hello')
saying = a.f1(saying)
saying()
b、 py具有以下代码:

def f1(input_func):
    def inner_func():
        input_func()
        input_func()
    return inner_func
import a
def saying():
    print('Hello')
saying = a.f1(saying)
saying()

我的问题是,对于前面提到的例子,当您进行赋值时,解释器如何知道我调用的是指向a.f1(saying)的
saying
对象,而不是指向saying(
print('Hello')
)定义的对象?

saying = a.f1(saying)
从这一点开始,
saying
指出由
a.f1(saying)
评估的内容,在本例中,这是修饰的
saying
功能


如果要交换py.b的最后两行,则
saying()
仍将调用未修饰的
saying
函数。

当您进行赋值时

saying = a.f1(saying)
从这一点开始,
saying
指出由
a.f1(saying)
评估的内容,在本例中,这是修饰的
saying
功能


如果要交换py.b的最后两行,那么
saying()
仍将调用未修饰的
saying
函数。

TLDR:您正在覆盖变量名,因此解释器将只使用最新的定义。运行
saying=a.f1(saying)
后,除非已将原始
saying
保存在另一个变量中,否则无法直接调用它

如果你想要一个更长的解释

垃圾收集 通常,当您覆盖这样的变量时,内存中的旧值/位置会被垃圾收集器完全擦除。但是,在您的情况下,原始的
语句将保存在内存中,因为它在调用
f1(saying)
时使用。您无法访问它,除非在重新分配它之前已将其保存在另一个变量中

让我们使用
id
函数查看内存引用,以便更好地了解发生了什么

如果运行此代码:

def f1(input_func):
    def inner_func():
        input_func()
        input_func()
    return inner_func

def saying():
    print('Hello')

print("saying address:", hex(id(saying)))
saying()
print("---")

saying = f1(saying)
print("new saying address = f1(saying):", hex(id(saying)))
saying()
你会得到这样的结果:

saying address: 0x7fa960295820
Hello
---
new saying address = f1(saying): 0x7fa9602958b0
Hello
Hello
您可以看到这两个函数保存在不同的内存地址中,并且
saying
现在指向新地址

如果我们提前保存第一个地址,我们可以使用恢复原始功能。在实践中,您永远不会希望这样做(简单地将原始的
语句保存在另一个变量中会非常容易),但仅用于教学目的:

import gc

# ... define f1 and saying as before

def object_by_id(id_):
    for obj in gc.get_objects():
        if id(obj) == id_:
            return obj
    raise Exception("Not found")

origId = id(saying)
print("saying address:", hex(origId))
saying()
print("---")

saying = f1(saying)
print("new saying address = f1(saying):", hex(id(saying)))
saying()
print("---")

saying = object_by_id(origId)
print("saying address after recovery: ", hex(id(saying)))
if id(saying) == origId:
  print( "Matches original!")
saying()
输出:

saying address: 0x7f0c2db4a820
Hello
---
new saying address = f1(saying): 0x7f0c2db4a8b0
Hello
Hello
---
saying address after recovery:  0x7f0c2db4a820
Matches original!
Hello
因此,您可以看到,原来的函数确实已恢复。同样,这只会起作用,因为新值saying=f1(saying)
在其定义中包含对原始函数的调用

但是,如果对
f1
的调用不包含
语句,而是调用另一个函数,会发生什么情况?然后原来的
语句
将永久丢失。您可以通过将此代码段添加到前面代码的末尾来看到这一点:

def otherFunc():
  print(1)

saying = f1(otherFunc)
print("new saying address = f1(otherFunc):", hex(id(saying)))
saying()
print("---")

saying = object_by_id(origId) # Exception: Not found
不再有变量引用原始的
语句
,因此垃圾回收器将其删除,并且它不再可恢复


TLDR:您正在覆盖变量名,因此解释器将只使用最新的定义。运行
saying=a.f1(saying)
后,除非已将原始
saying
保存在另一个变量中,否则无法直接调用它

如果你想要一个更长的解释

垃圾收集 通常,当您覆盖这样的变量时,内存中的旧值/位置会被垃圾收集器完全擦除。但是,在您的情况下,原始的
语句将保存在内存中,因为它在调用
f1(saying)
时使用。您无法访问它,除非在重新分配它之前已将其保存在另一个变量中

让我们使用
id
函数查看内存引用,以便更好地了解发生了什么

如果运行此代码:

def f1(input_func):
    def inner_func():
        input_func()
        input_func()
    return inner_func

def saying():
    print('Hello')

print("saying address:", hex(id(saying)))
saying()
print("---")

saying = f1(saying)
print("new saying address = f1(saying):", hex(id(saying)))
saying()
你会得到这样的结果:

saying address: 0x7fa960295820
Hello
---
new saying address = f1(saying): 0x7fa9602958b0
Hello
Hello
您可以看到这两个函数保存在不同的内存地址中,并且
saying
现在指向新地址

如果我们提前保存第一个地址,我们可以使用恢复原始功能。在实践中,您永远不会希望这样做(简单地将原始的
语句保存在另一个变量中会非常容易),但仅用于教学目的:

import gc

# ... define f1 and saying as before

def object_by_id(id_):
    for obj in gc.get_objects():
        if id(obj) == id_:
            return obj
    raise Exception("Not found")

origId = id(saying)
print("saying address:", hex(origId))
saying()
print("---")

saying = f1(saying)
print("new saying address = f1(saying):", hex(id(saying)))
saying()
print("---")

saying = object_by_id(origId)
print("saying address after recovery: ", hex(id(saying)))
if id(saying) == origId:
  print( "Matches original!")
saying()
输出:

saying address: 0x7f0c2db4a820
Hello
---
new saying address = f1(saying): 0x7f0c2db4a8b0
Hello
Hello
---
saying address after recovery:  0x7f0c2db4a820
Matches original!
Hello
因此,您可以看到,原来的函数确实已恢复。同样,这只会起作用,因为新值saying=f1(saying)
在其定义中包含对原始函数的调用

但是,如果对
f1
的调用不包含
语句,而是调用另一个函数,会发生什么情况?然后原来的
语句
将永久丢失。您可以通过将此代码段添加到前面代码的末尾来看到这一点:

def otherFunc():
  print(1)

saying = f1(otherFunc)
print("new saying address = f1(otherFunc):", hex(id(saying)))
saying()
print("---")

saying = object_by_id(origId) # Exception: Not found
不再有变量引用原始的
语句
,因此垃圾回收器将其删除,并且它不再可恢复


谢谢。这是非常详细和有用的。谢谢。这是非常详细和有用的。