Python 中装饰器名称的解析

97 阅读2分钟

在 Python 中,有时我们需要给类中的方法添加一些功能,或者改变方法的行为。我们可以通过使用装饰器来实现。

huake_00152_.jpg 装饰器是一种可以修饰函数或方法的函数或类。它允许我们在不改变函数或方法本身的情况下,改变函数或方法的行为。

在下面的代码中,我们定义了一个 Synchronized 类,该类包含一个名为 synchronized 的装饰器。该装饰器可以将任意函数或方法包装在锁中,从而确保该函数或方法只能被一个线程同时访问。

import threading
from functools import wraps

class Synchronized(object):
    def __init__(self):
        self.lock = threading.Lock()

    def synchronized(f):
        @wraps(f)
        def wrapper(self, *args, **kwargs):
            with self.lock:
                print("here")
                return f(self, *args, **kwargs)
        return wrapper

    @synchronized
    def go(self):
        print(1)

class B(Synchronized):
    @synchronized
    def foo(self):
        return 1

然而,当我们运行这段代码时,我们会得到一个错误:

NameError: name 'synchronized' is not defined

这是因为 Python 在解析装饰器名称时,会首先在当前类的作用域中查找。如果找不到,它会继续在父类的作用域中查找。

在上面的代码中,synchronized 装饰器是在 Synchronized 类中定义的。当我们试图在 B 类中使用 synchronized 装饰器时,Python 会首先在 B 类的作用域中查找,但找不到。然后,它会继续在 Synchronized 类的作用域中查找,但仍然找不到。因此,Python 会抛出一个错误。

2、解决方案

要解决这个问题,我们可以使用以下几种方法:

  1. 将 synchronized 装饰器定义在 Synchronized 类的外部。
def synchronized(f):
    @wraps(f)
    def wrapper(self, *args, **kwargs):
        with self.lock:
            print("here")
            return f(self, *args, **kwargs)
    return wrapper

class Synchronized(object):
    def __init__(self):
        self.lock = threading.Lock()

    @synchronized
    def go(self):
        print(1)

class B(Synchronized):
    @synchronized
    def foo(self):
        return 1
  1. 在 B 类中使用 Synchronized.synchronized.im_func 来访问 synchronized 装饰器。
import threading
from functools import wraps

class Synchronized(object):
    def __init__(self):
        self.lock = threading.Lock()

    def synchronized(f):
        @wraps(f)
        def wrapper(self, *args, **kwargs):
            with self.lock:
                print("here")
                return f(self, *args, **kwargs)
        return wrapper

    @synchronized
    def go(self):
        print(1)

class B(Synchronized):
    @Synchronized.synchronized.im_func
    def foo(self):
        return 1
  1. 在 B 类中使用 @staticmethod 来修饰 synchronized 装饰器。
import threading
from functools import wraps

class Synchronized(object):
    def __init__(self):
        self.lock = threading.Lock()

    @staticmethod
    def synchronized(f):
        @wraps(f)
        def wrapper(self, *args, **kwargs):
            with self.lock:
                print("here")
                return f(self, *args, **kwargs)
        return wrapper

    @synchronized
    def go(self):
        print(1)

class B(Synchronized):
    @synchronized
    def foo(self):
        return 1

这些方法都可以解决 Python 中装饰器名称解析的问题。