一、为类和静态方法提供装饰器
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/8/17 23:23
# @Author : Maple
# @File : 10-为类和静态方法提供装饰器.py
import time
def TimeCount(func):
def wrapper(*args,**kwargs):
start_time = time.perf_counter()
r = func(*args,**kwargs)
print('time takes {}s'.format(time.perf_counter() - start_time))
return r
return wrapper
class A:
# 注意装饰器的顺序不能写错
@staticmethod
@TimeCount
def add(a,b):
return a + b
@classmethod
@TimeCount
def show(cls,n):
return n
if __name__ == '__main__':
# 测试
a = A()
result1 = a.add(1,3)
print(result1)
"""
time takes 2.0999577827751637e-06s
4
"""
print('--------------------')
result2 = A.show(10)
print(result2)
"""
time takes 7.00005330145359e-07s
10
"""
二、将装饰器定义为类
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024-04-07 16:23
# @Author : Maple
# @File : 09-将装饰器定义为类.py
import types
from functools import wraps
class Profiled:
def __init__(self, func):
wraps(func)(self)
self.ncalls = 0
def __call__(self, *args, **kwargs):
self.ncalls += 1
# 执行被包装的函数func
return self.__wrapped__(*args, **kwargs)
def __get__(self, instance, cls):
if instance is None:
# print(self)
return self
else:
# self其实就指向被包装的函数-->return self.__wrapper__(args,kwargs)
# 这也是为何要实现__call__,因为能够实现profiled()这种写法-->会执行__call__方法,而__call__方法
## 返回的是对fun的调用
# 为实例instance绑定fun
return types.MethodType(self, instance)
@Profiled
def add(x,y):
return x + y
class Spam:
@Profiled
def bar(self,x):
print(self,x)
if __name__ == '__main__':
a = add
print(a) # <__main__.Profiled object at 0x0000018200971E50>
print(add.ncalls) # 0
# 本质上调用Profiled类中的call方法,因此ncall会+1
add(12,2)
print(add.ncalls) # 1
s = Spam()
print('s-->', s) # s--> <__main__.Spam object at 0x0000024770602180>
# 会进到Profiled中的__get__方法,然后为s绑定 bar方法(同时这个bar是被Profiled包装过的)
# 因此当执行s.bar时,实际是执行profiled()--> 会执行Profiled中的call--> ncall会被+1
"""
s.bar(2)的完整执行流程:
1. 跳转到Profiled中的__get__方法,为s绑定一个profield实例--name为bar(profiled实例中包装了bar函数)
2. 因此s.bar(2)会执行Profiled类中的call,将ncall + 1
3. 然后通过self.__wrapped__(*args, **kwargs) -->执行bar函数本身的逻辑: print(self,x)
4. 最后return self.__wrapped__(*args, **kwargs)的返回值:此例为null,无返回值
"""
s.bar(2)
print(s.bar.ncalls) # 1
s.bar(3)
print(s.bar.ncalls) # 2
print('****************************')
s = Spam.bar(0,1)
三、装饰器为包装函数增加参数
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024-04-07 16:23
# @Author : Maple
# @File : 11-装饰器为包装函数增加参数.py
import inspect
from functools import wraps
def optional_debug(func):
@wraps(func)
def wrapper(*args,debug=False,**kwargs):
if debug:
print('calling ' + func.__name__)
return func(*args,**kwargs)
return wrapper
@optional_debug
def add(x,y):
return x + y
"""
通过装饰器来给被包装函数增加参数的做法并不常见。
尽管如此,有时候它可以避免一些重复代码。例如,如果你有下面这样的代码:
"""
def a(x, debug=False):
if debug:
print('Calling a')
def b(x, y, z, debug=False):
if debug:
print('Calling b')
def c(x, y, debug=False):
if debug:
print('Calling c')
def optional_debug2(func):
if 'debug' in inspect.getfullargspec(func).args:
print('inspect.getfullargspec(func).args',inspect.getfullargspec(func).args) # ['x', 'y', 'debug']
raise TypeError('debug argument already defined')
@wraps(func)
def wrapper(*args,debug=False,**kwargs):
if debug:
print('calling', func.__name__)
return func(*args,**kwargs)
return wrapper
"""如果函数本身已存在debug参数,会触发装饰器中的raise TypeError
"""
# @optional_debug2
# def add2(x,y,debug):
# if debug:
# print('calling add2')
# return x + y
@optional_debug2
def add3(x,y):
return x + y
def optional_debug3(func):
if 'debug' in inspect.getfullargspec(func).args:
print('inspect.getfullargspec(func).args',inspect.getfullargspec(func).args) # ['x', 'y', 'debug']
raise TypeError('debug argument already defined')
@wraps(func)
def wrapper(*args,debug=False,**kwargs):
if debug:
print('calling', func.__name__)
return func(*args,**kwargs)
sig = inspect.signature(func)
# 获取func函数的参数列表
param = list(sig.parameters.values())
# 添加debug参数
param.append(inspect.Parameter('debug',
inspect.Parameter.KEYWORD_ONLY,
default=False))
wrapper.__signature__ = sig.replace(parameters=param)
return wrapper
@optional_debug3
def add4(x,y):
return x + y
if __name__ == '__main__':
# 1.optional_debug测试
r = add(1,2,debug = True) # calling add
print(r) # 3
print('2-1.optional_debug2 -测试1')
# 2-1.optional_debug2 -测试1
# try:
# add2(1,2)
# except TypeError as e:
# print(e) # debug argument already defined
print(' 2-2.optional_debug2 -测试2')
# 2-2.optional_debug2 -测试2
r2 = add3(2,2,debug = True) # calling add3
print(r2) # 4
print('2-3.optional_debug2存在一个问题')
# 2-3.optional_debug2存在一个问题
# 被装饰的add3元数据中并没有debug参数(本意是想通过装饰器添加额外参数)
print(inspect.signature(add3)) # (x, y)
# 3. 修复被装饰函数debug参数丢失问题
print(inspect.signature(add4)) # (x, y, *, debug=False)
四、使用类装饰器扩充类的功能
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024-04-07 17:24
# @Author : Maple
# @File : 12-使用类装饰器扩充类的功能.py
"""通过反省或者重写类定义的某部分来修改它的行为,但是你又不希望使用继承或元类的方式。
"""
def log_getattribute(cls):
org_getattribute = cls.__getattribute__
def new_getattribute(self,name):
print('getting ',name)
return org_getattribute(self,name)
cls.__getattribute__ = new_getattribute
return cls
@log_getattribute
class A:
def __init__(self,x):
self.x = x
def spam(self):
pass
if __name__ == '__main__':
# 1.属性测试
a = A('Maple') # getting x
print(a.x) # Maple
# 2.方法测试
a.spam() #getting spam
五、使用元类控制实例的创建
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024-04-07 17:43
# @Author : Maple
# @File : 13-使用元类控制实例的创建.py
"""
元类是type类的子类
"""
class NoInstance(type):
def __call__(self, *args, **kwargs):
raise TypeError("can't instanciate directly")
class Person(metaclass=NoInstance):
def __init__(self,name):
self.name = name
@staticmethod
def run():
print('grok is running')
"""利用元类实现单列模式
"""
class Singleton(type):
# init方法是完成类(此例中是 Student )的初始化
# self表示Student类, cls.__instance即为类添加了一个类属性
def __init__(cls,*args,**kwargs):
# self代表元类Singleton的实例(注意元类是用来创建类的,因此其实例对象是一个类)
# 因此,此例中self代表Student类
cls.__instance = None
# super()代表type
"""
*args 和 **kwargs:这些是位置参数和关键字参数的列表,它们在创建类对象时被传递给 type 的构造函数。通常,这些参数包括:
--下面这3个参数其实都位于args元组中--
name:字符串,代表正在被创建的类的名称。
bases:元组,包含了类的所有基类。
class_dict:字典,包含了类定义中的所有属性和方法。
"""
print('__init__方法中的args')
for a in args:
print('a: ',a)
# 调用type的init方法,初始化需要创建的类:此例是Student
super().__init__(*args,**kwargs)
# 当要创建的类(此例中是 Student 类) 实例化时,会调用元类的call方法
def __call__(cls, *args, **kwargs):
#
if cls.__instance is None:
print('__call__方法中的args')
"""
a: Maple
a: 19
"""
for a in args:
print('a: ', a)
# 调用type的call方法,返回一个实例对象(student),并存放在Student类的__instance属性中
# cls是Student类
cls.__instance = super().__call__(*args,**kwargs)
return cls.__instance
else:
return cls.__instance
class Student(metaclass=Singleton):
def __init__(self,name,age):
self.name = name
self.age = age
if __name__ == '__main__':
# 1. Person不能被实例化,run方法只能通过类调用
print('******** 1. Person不能被实例化,run方法只能通过类调用***************')
Person.run() # grok is running
try:
p = Person('Maple')
except TypeError as e:
print(e) # can't instanciate directly
print('********2. 单例测试****************')
## 2-1 类属性查看
print(Student.__dict__) # {...Singleton__instance': None}
## 2-2 初始化实例对象
s1 = Student('Maple',19)
print(Student.__dict__) # {..._Singleton__instance': <__main__.Student object at 0x00000180EB622D80>}}
s2 = Student('Maple', 19)
print(Student.__dict__) # {..._Singleton__instance': <__main__.Student object at 0x00000180EB622D80>}}
print(s1 is s2) # True