2025-02-06 python装饰器相关笔记

1.装饰器什么时候执行的,是导入时候,还是调用时候,多个装饰器的调用顺序是怎么样的。
经过查找资料以及自己测试,装饰器的绑定是在导入时候,而内部函数的执行则是在调用时候。
绑定的执行顺序是由近到远,函数执行顺序为由远到近


def decorator_a(func):
    print('Decorator A')

    def wrapper(*args, **kwargs):
        print("Decorator A: Before function call")
        result = func(*args, **kwargs)
        print("Decorator A: After function call")
        return result

    return wrapper

def decorator_b(func):
    print("Decorator B")
    def wrapper_b():
        print("Wrapper B")
        func()
    return wrapper_b

@decorator_a
@decorator_b
def my_function():
    print("My Function")

my_function()

在导入的时候会发现执行了decorator_a 和 decorator_b,当调用my_function的时候发现执行了wrapper_a,wrapper_b
输出为

Decorator B
Decorator A
Decorator A: Before function call
Wrapper B
My Function
Decorator A: After function call

2.类装饰器
(1).类装饰器的基本原理
类装饰器的核心在于实现 call 方法。call 方法使得类的实例可以像函数一样被调用。当一个类被用作装饰器时,它会实例化该类,并调用 call 方法来包装目标函数。
(2).类装饰器的实现步骤
定义类:定义一个类,并在类中实现 initcall 方法。
init 方法用于接收被装饰的函数或类。
call 方法用于实现装饰逻辑。
使用类装饰器:通过在目标函数或类上使用 @ 符号来应用类装饰器。

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.call_count = 0

    def __call__(self, *args, **kwargs):
        self.call_count += 1
        print(f"Function {self.func.__name__} has been called {self.call_count} times")
        return self.func(*args, **kwargs)

# 使用类装饰器
@CountCalls
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")
say_hello("Bob")

这个用来统计函数调用次数。输出为

Function say_hello has been called 1 times
Hello, Alice!
Function say_hello has been called 2 times
Hello, Bob!

3.@classmethod
python代码部分的实现看不出来啥,查找文档之后说这个是在C里面实现的。
主要是用来表示这个函数是属于类的函数,不属于实例的函数。
可以用来创建实例,统计实例个数等,第一个输入参数为cls,也就是类本身,不是普通self,也就是实例本身

class Animal:
    def __init__(self, name):
        self.name = name

    @classmethod
    def create_dog(cls):
        return cls("Dog")

    @classmethod
    def create_cat(cls):
        return cls("Cat")

# 使用工厂方法创建实例
dog = Animal.create_dog()
cat = Animal.create_cat()

print(dog.name)  # 输出:Dog
print(cat.name)  # 输出:Cat

4.django 运行时候会执行两次main函数的问题
今天开发的时候遇到了一个问题,在项目的init.py文件初始化一个多进程的定时任务,然后发现它竟然启动了两个进程(进程ID不一样),
起初以为是不能在初始化文件启动多进程,把它移动到manage.py文件还是出现了一样的情况。
后来经过研究发现,在debug模式(开发模式)下Django会开启两个线程,另一个用来监听代码变化(比如说Ctrl+S会自动重启)

解决
如果不想让它启动执行两次,可以在启动命令加上“–noreload”
python manage.py runserver 8000 --noreload

5.@staticmethod
这个好像是普通函数放到类的命名空间一样,不会带入类或者实例作为参数。
静态方法可以通过类名调用,也可以通过类的实例调用,但不会自动传递类或实例的引用。

class MathUtils:
    @staticmethod
    def add(a, b):
        return a + b

    @staticmethod
    def multiply(a, b):
        return a * b

# 调用静态方法
result1 = MathUtils.add(3, 5)  # 通过类名调用
result2 = MathUtils().add(3, 5)  # 通过实例调用
print(result1)  # 输出 8
print(result2)  # 输出 8

6.@classonlymethod
非内置的,一个只能通过类调用,而不能通过实例调用的方法,按名称定义也很好实现

class classonlymethod:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        if instance is not None:
            raise AttributeError("This method is available only on the class, not on instances.")
        return self.func.__get__(instance, owner)

7.@classproperty 和 @property
在 Python 中,classproperty 是一个非标准的装饰器,用于实现类似 @property 的功能,但作用于类属性而不是实例属性。虽然 Python 标准库中没有内置 classproperty,但可以通过自定义实现。
一个是类的属性,一个是实例的属性

class classproperty:
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, owner):
        return self.f(owner)

使用

class Config:
    _database_url = "localhost"

    @classproperty
    def database_url(cls):
        return cls._database_url

    @database_url.setter
    def database_url(cls, value):
        cls._database_url = value

# 使用示例
print(Config.database_url)  # 输出: localhost
Config.database_url = "https://remote.db"
print(Config.database_url)  # 输出: https://remote.db

8.@wraps
在 Python 中,@wraps 是 functools 模块中的一个装饰器,用于在定义装饰器时保留被装饰函数的元数据(如函数名、文档字符串等)。如果不使用 @wraps,装饰器会改变被装饰函数的元数据,导致调试和文档生成时出现问题

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Before calling the function.")
        result = func(*args, **kwargs)
        print("After calling the function.")
        return result
    return wrapper

@my_decorator
def say_hello2(name):
    """This function greets a person."""
    print(f"Hello, {name}!")

say_hello2("Alice")
print(say_hello2.__name__)  # 输出: say_hello
print(say_hello2.__doc__)   # 输出: This function greets a person.

9.yield
生成器,每调用一次,就会执行到yield位置。

def simple_generator():
    print("yield 1")
    yield 1
    print("yield 2")
    yield 2
    print("yield 3")
    yield 3
    print("yield 4")
gen = simple_generator()
print(next(gen))  # 输出: 1
print(next(gen))  # 输出: 2
print(next(gen))  # 输出: 3
# 再次调用会抛出 StopIteration 异常

输出

yield 1
1
yield 2
2
yield 3
3
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容