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).类装饰器的实现步骤
定义类:定义一个类,并在类中实现 init 和 call 方法。
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