当前位置:   article > 正文

Python3-Cookbook(第九章) - 元编程Part2

Python3-Cookbook(第九章) - 元编程Part2

一、为类和静态方法提供装饰器

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # @Time : 2023/8/17 23:23
  4. # @Author : Maple
  5. # @File : 10-为类和静态方法提供装饰器.py
  6. import time
  7. def TimeCount(func):
  8. def wrapper(*args,**kwargs):
  9. start_time = time.perf_counter()
  10. r = func(*args,**kwargs)
  11. print('time takes {}s'.format(time.perf_counter() - start_time))
  12. return r
  13. return wrapper
  14. class A:
  15. # 注意装饰器的顺序不能写错
  16. @staticmethod
  17. @TimeCount
  18. def add(a,b):
  19. return a + b
  20. @classmethod
  21. @TimeCount
  22. def show(cls,n):
  23. return n
  24. if __name__ == '__main__':
  25. # 测试
  26. a = A()
  27. result1 = a.add(1,3)
  28. print(result1)
  29. """
  30. time takes 2.0999577827751637e-06s
  31. 4
  32. """
  33. print('--------------------')
  34. result2 = A.show(10)
  35. print(result2)
  36. """
  37. time takes 7.00005330145359e-07s
  38. 10
  39. """

二、将装饰器定义为类

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # @Time : 2024-04-07 16:23
  4. # @Author : Maple
  5. # @File : 09-将装饰器定义为类.py
  6. import types
  7. from functools import wraps
  8. class Profiled:
  9. def __init__(self, func):
  10. wraps(func)(self)
  11. self.ncalls = 0
  12. def __call__(self, *args, **kwargs):
  13. self.ncalls += 1
  14. # 执行被包装的函数func
  15. return self.__wrapped__(*args, **kwargs)
  16. def __get__(self, instance, cls):
  17. if instance is None:
  18. # print(self)
  19. return self
  20. else:
  21. # self其实就指向被包装的函数-->return self.__wrapper__(args,kwargs)
  22. # 这也是为何要实现__call__,因为能够实现profiled()这种写法-->会执行__call__方法,而__call__方法
  23. ## 返回的是对fun的调用
  24. # 为实例instance绑定fun
  25. return types.MethodType(self, instance)
  26. @Profiled
  27. def add(x,y):
  28. return x + y
  29. class Spam:
  30. @Profiled
  31. def bar(self,x):
  32. print(self,x)
  33. if __name__ == '__main__':
  34. a = add
  35. print(a) # <__main__.Profiled object at 0x0000018200971E50>
  36. print(add.ncalls) # 0
  37. # 本质上调用Profiled类中的call方法,因此ncall会+1
  38. add(12,2)
  39. print(add.ncalls) # 1
  40. s = Spam()
  41. print('s-->', s) # s--> <__main__.Spam object at 0x0000024770602180>
  42. # 会进到Profiled中的__get__方法,然后为s绑定 bar方法(同时这个bar是被Profiled包装过的)
  43. # 因此当执行s.bar时,实际是执行profiled()--> 会执行Profiled中的call--> ncall会被+1
  44. """
  45. s.bar(2)的完整执行流程:
  46. 1. 跳转到Profiled中的__get__方法,为s绑定一个profield实例--name为bar(profiled实例中包装了bar函数)
  47. 2. 因此s.bar(2)会执行Profiled类中的call,将ncall + 1
  48. 3. 然后通过self.__wrapped__(*args, **kwargs) -->执行bar函数本身的逻辑: print(self,x)
  49. 4. 最后return self.__wrapped__(*args, **kwargs)的返回值:此例为null,无返回值
  50. """
  51. s.bar(2)
  52. print(s.bar.ncalls) # 1
  53. s.bar(3)
  54. print(s.bar.ncalls) # 2
  55. print('****************************')
  56. s = Spam.bar(0,1)

三、为类和静态方法提供装饰器

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # @Time : 2023/8/17 23:23
  4. # @Author : Maple
  5. # @File : 10-为类和静态方法提供装饰器.py
  6. import time
  7. def TimeCount(func):
  8. def wrapper(*args,**kwargs):
  9. start_time = time.perf_counter()
  10. r = func(*args,**kwargs)
  11. print('time takes {}s'.format(time.perf_counter() - start_time))
  12. return r
  13. return wrapper
  14. class A:
  15. # 注意装饰器的顺序不能写错
  16. @staticmethod
  17. @TimeCount
  18. def add(a,b):
  19. return a + b
  20. @classmethod
  21. @TimeCount
  22. def show(cls,n):
  23. return n
  24. if __name__ == '__main__':
  25. # 测试
  26. a = A()
  27. result1 = a.add(1,3)
  28. print(result1)
  29. """
  30. time takes 2.0999577827751637e-06s
  31. 4
  32. """
  33. print('--------------------')
  34. result2 = A.show(10)
  35. print(result2)
  36. """
  37. time takes 7.00005330145359e-07s
  38. 10
  39. """

四、装饰器为包装函数增加参数

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # @Time : 2024-04-07 16:23
  4. # @Author : Maple
  5. # @File : 11-装饰器为包装函数增加参数.py
  6. import inspect
  7. from functools import wraps
  8. def optional_debug(func):
  9. @wraps(func)
  10. def wrapper(*args,debug=False,**kwargs):
  11. if debug:
  12. print('calling ' + func.__name__)
  13. return func(*args,**kwargs)
  14. return wrapper
  15. @optional_debug
  16. def add(x,y):
  17. return x + y
  18. """
  19. 通过装饰器来给被包装函数增加参数的做法并不常见。
  20. 尽管如此,有时候它可以避免一些重复代码。例如,如果你有下面这样的代码:
  21. """
  22. def a(x, debug=False):
  23. if debug:
  24. print('Calling a')
  25. def b(x, y, z, debug=False):
  26. if debug:
  27. print('Calling b')
  28. def c(x, y, debug=False):
  29. if debug:
  30. print('Calling c')
  31. def optional_debug2(func):
  32. if 'debug' in inspect.getfullargspec(func).args:
  33. print('inspect.getfullargspec(func).args',inspect.getfullargspec(func).args) # ['x', 'y', 'debug']
  34. raise TypeError('debug argument already defined')
  35. @wraps(func)
  36. def wrapper(*args,debug=False,**kwargs):
  37. if debug:
  38. print('calling', func.__name__)
  39. return func(*args,**kwargs)
  40. return wrapper
  41. """如果函数本身已存在debug参数,会触发装饰器中的raise TypeError
  42. """
  43. # @optional_debug2
  44. # def add2(x,y,debug):
  45. # if debug:
  46. # print('calling add2')
  47. # return x + y
  48. @optional_debug2
  49. def add3(x,y):
  50. return x + y
  51. def optional_debug3(func):
  52. if 'debug' in inspect.getfullargspec(func).args:
  53. print('inspect.getfullargspec(func).args',inspect.getfullargspec(func).args) # ['x', 'y', 'debug']
  54. raise TypeError('debug argument already defined')
  55. @wraps(func)
  56. def wrapper(*args,debug=False,**kwargs):
  57. if debug:
  58. print('calling', func.__name__)
  59. return func(*args,**kwargs)
  60. sig = inspect.signature(func)
  61. # 获取func函数的参数列表
  62. param = list(sig.parameters.values())
  63. # 添加debug参数
  64. param.append(inspect.Parameter('debug',
  65. inspect.Parameter.KEYWORD_ONLY,
  66. default=False))
  67. wrapper.__signature__ = sig.replace(parameters=param)
  68. return wrapper
  69. @optional_debug3
  70. def add4(x,y):
  71. return x + y
  72. if __name__ == '__main__':
  73. # 1.optional_debug测试
  74. r = add(1,2,debug = True) # calling add
  75. print(r) # 3
  76. print('2-1.optional_debug2 -测试1')
  77. # 2-1.optional_debug2 -测试1
  78. # try:
  79. # add2(1,2)
  80. # except TypeError as e:
  81. # print(e) # debug argument already defined
  82. print(' 2-2.optional_debug2 -测试2')
  83. # 2-2.optional_debug2 -测试2
  84. r2 = add3(2,2,debug = True) # calling add3
  85. print(r2) # 4
  86. print('2-3.optional_debug2存在一个问题')
  87. # 2-3.optional_debug2存在一个问题
  88. # 被装饰的add3元数据中并没有debug参数(本意是想通过装饰器添加额外参数)
  89. print(inspect.signature(add3)) # (x, y)
  90. # 3. 修复被装饰函数debug参数丢失问题
  91. print(inspect.signature(add4)) # (x, y, *, debug=False)

五、使用类装饰器扩充类的功能

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # @Time : 2024-04-07 17:24
  4. # @Author : Maple
  5. # @File : 12-使用类装饰器扩充类的功能.py
  6. """通过反省或者重写类定义的某部分来修改它的行为,但是你又不希望使用继承或元类的方式。
  7. """
  8. def log_getattribute(cls):
  9. org_getattribute = cls.__getattribute__
  10. def new_getattribute(self,name):
  11. print('getting ',name)
  12. return org_getattribute(self,name)
  13. cls.__getattribute__ = new_getattribute
  14. return cls
  15. @log_getattribute
  16. class A:
  17. def __init__(self,x):
  18. self.x = x
  19. def spam(self):
  20. pass
  21. if __name__ == '__main__':
  22. # 1.属性测试
  23. a = A('Maple') # getting x
  24. print(a.x) # Maple
  25. # 2.方法测试
  26. a.spam() #getting spam

六、使用元类控制实例的创建

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # @Time : 2024-04-07 17:43
  4. # @Author : Maple
  5. # @File : 13-使用元类控制实例的创建.py
  6. """
  7. 元类是type类的子类
  8. """
  9. class NoInstance(type):
  10. def __call__(self, *args, **kwargs):
  11. raise TypeError("can't instanciate directly")
  12. class Person(metaclass=NoInstance):
  13. def __init__(self,name):
  14. self.name = name
  15. @staticmethod
  16. def run():
  17. print('grok is running')
  18. """利用元类实现单列模式
  19. """
  20. class Singleton(type):
  21. # init方法是完成类(此例中是 Student )的初始化
  22. # self表示Student类, cls.__instance即为类添加了一个类属性
  23. def __init__(cls,*args,**kwargs):
  24. # self代表元类Singleton的实例(注意元类是用来创建类的,因此其实例对象是一个类)
  25. # 因此,此例中self代表Student类
  26. cls.__instance = None
  27. # super()代表type
  28. """
  29. *args 和 **kwargs:这些是位置参数和关键字参数的列表,它们在创建类对象时被传递给 type 的构造函数。通常,这些参数包括:
  30. --下面这3个参数其实都位于args元组中--
  31. name:字符串,代表正在被创建的类的名称。
  32. bases:元组,包含了类的所有基类。
  33. class_dict:字典,包含了类定义中的所有属性和方法。
  34. """
  35. print('__init__方法中的args')
  36. for a in args:
  37. print('a: ',a)
  38. # 调用type的init方法,初始化需要创建的类:此例是Student
  39. super().__init__(*args,**kwargs)
  40. # 当要创建的类(此例中是 Student 类) 实例化时,会调用元类的call方法
  41. def __call__(cls, *args, **kwargs):
  42. #
  43. if cls.__instance is None:
  44. print('__call__方法中的args')
  45. """
  46. a: Maple
  47. a: 19
  48. """
  49. for a in args:
  50. print('a: ', a)
  51. # 调用type的call方法,返回一个实例对象(student),并存放在Student类的__instance属性中
  52. # cls是Student类
  53. cls.__instance = super().__call__(*args,**kwargs)
  54. return cls.__instance
  55. else:
  56. return cls.__instance
  57. class Student(metaclass=Singleton):
  58. def __init__(self,name,age):
  59. self.name = name
  60. self.age = age
  61. if __name__ == '__main__':
  62. # 1. Person不能被实例化,run方法只能通过类调用
  63. print('******** 1. Person不能被实例化,run方法只能通过类调用***************')
  64. Person.run() # grok is running
  65. try:
  66. p = Person('Maple')
  67. except TypeError as e:
  68. print(e) # can't instanciate directly
  69. print('********2. 单例测试****************')
  70. ## 2-1 类属性查看
  71. print(Student.__dict__) # {...Singleton__instance': None}
  72. ## 2-2 初始化实例对象
  73. s1 = Student('Maple',19)
  74. print(Student.__dict__) # {..._Singleton__instance': <__main__.Student object at 0x00000180EB622D80>}}
  75. s2 = Student('Maple', 19)
  76. print(Student.__dict__) # {..._Singleton__instance': <__main__.Student object at 0x00000180EB622D80>}}
  77. print(s1 is s2) # True

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/426594
推荐阅读
相关标签
  

闽ICP备14008679号