鱼C论坛

 找回密码
 立即注册
查看: 1855|回复: 5

[知识点备忘] 第077讲:类和对象(XX)

[复制链接]
发表于 2022-10-23 23:53:09 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
0. 本节视频




1. 温馨提示

如果在学习本节课的过程中遇到问题,可以在这个帖子下方提问哦~


小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2022-10-24 19:44:15 | 显示全部楼层
老师,视频呢
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-3 23:17:55 | 显示全部楼层
大概什么时候可以完结啊?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-11 10:29:25 | 显示全部楼层
这节课讲解了一个新的概念——类装饰器。类装饰器就是将装饰器作用在类上面,在类被实例化对象之前对其进行拦截和干预,用法与装饰函数的装饰器非常相似。函数可以用来装饰类(类装饰器),反过来,类也可以用作装饰器来装饰函数,使函数成为类的实例化对象,调用函数便会触发类的__call__()等一系列魔法方法。由此,类与函数的“亲密”关系也得以进一步发展!
类装饰器也有容易踩到的“坑”:当类作装饰器用于装饰另一个类时,被装饰类的多次实例化对象实则为装饰类的同一个对象,不过是将其作为函数调用了若干次,因而会导致属性覆盖问题。解决方法是将装饰器类“套一层外壳”,放在一个函数中,由函数返回该类,再将该函数用作装饰器来装饰被装饰类即可。
话说最近几节课的知识点真的是越来越难了!小古比鱼目前只能理清大致思路,自己编写代码恐怕手足无措……也不知类和对象还有多少节,希望小甲鱼老师早日更新类和对象的课后作业!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2023-1-10 23:36:02 | 显示全部楼层
# 类装饰器
  1. def report(cls):
  2.     def oncall(*args, **kwargs):
  3.         print('开始实例化对象')
  4.         _ = cls(*args, **kwargs)
  5.         print('实例化对象完成')
  6.         return _
  7.     return oncall

  8. @report
  9. class C:
  10.     pass

  11. c = C()
  12. # 开始实例化对象
  13. # 实例化对象完成

  14. @report
  15. class C:
  16.     def __init__(self, x, y, z):
  17.         self.x = x
  18.         self.y = y
  19.         self.z = z
  20.         print('构造函数被调用')

  21. c = C(1, 2, 3)
  22. # 开始实例化对象
  23. # 构造函数被调用
  24. # 实例化对象完成
  25. print(c.__dict__)
  26. # {'x': 1, 'y': 2, 'z': 3}
  27. # 类装饰器的作用就是在类被实例化对象之前对其进行拦截和干预

  28. # 让类做装饰器来装饰函数
  29. class Counter:
  30.     def __init__(self):
  31.         self.count = 0
  32.     def __call__(self, *args, **kwargs):
  33.         self.count += 1
  34.         print(f'已经被调用了{self.count}次')

  35. c = Counter()
  36. c()# 已经被调用了1次
  37. c()# 已经被调用了2次
  38. c()# 已经被调用了3次

  39. class Counter:
  40.     def __init__(self, func):
  41.         self.count = 0
  42.         self.func = func
  43.     def __call__(self, *args, **kwargs):
  44.         self.count += 1
  45.         print(f'已经被调用了{self.count}次')
  46.         return self.func(*args, **kwargs)

  47. @Counter
  48. def say_hi():
  49.     print('hi~')

  50. print(say_hi)# <__main__.Counter object at 0x000001E41134AFD0>
  51. # 函数say_hi已经被掉包成Counter的对象
  52. say_hi()
  53. # 已经被调用了1次
  54. # hi~
  55. say_hi()
  56. # 已经被调用了2次
  57. # hi~
  58. say_hi()
  59. # 已经被调用了3次
  60. # hi~

  61. # 用类做装饰器来装饰类
  62. class Check:
  63.     def __init__(self, cls):
  64.         self.cls = cls
  65.     def __call__(self, *args, **kwargs):
  66.         self.obj = self.cls(*args, **kwargs)
  67.         return self
  68.     def __getattr__(self, item):
  69.         print(f'正在访问{item}....')
  70.         return getattr(self.obj, item)

  71. @Check
  72. class C:
  73.     def say_hi(self):
  74.         print(f'hi~ -> {self.name}')
  75.     def say_hey(self):
  76.         print(f'hey~ -> {self.name}')

  77. # 将类C改写
  78. @Check
  79. class C:
  80.     def __init__(self, name):
  81.         self.name = name
  82.     def say_hi(self):
  83.         print(f'hi~ -> {self.name}')
  84.     def say_hey(self):
  85.         print(f'hey~ -> {self.name}')

  86. c1 = C('c1')
  87. c2 = C('c2')
  88. print(c1.name)
  89. # 正在访问name....
  90. # c2
  91. print(c2.name)
  92. # 正在访问name....
  93. # c2
  94. c1.say_hi()
  95. # 正在访问say_hi....
  96. # hi~ -> c2
  97. c2.say_hey()
  98. # 正在访问say_hey....
  99. # hey~ -> c2
  100. # c1的name属性被c2的name属性覆盖
  101. print(c1)# <__main__.Check object at 0x000001DB1AB5EF70>
  102. print(c2)# <__main__.Check object at 0x000002A74A5DEF70>
  103. # c1和c2其实是Check类的实例对象,其实传递给的是Check类的__call__()魔法方法
  104. # 对象在@Check时就诞生了,class C在__call__()魔法方法中的第一个语句完成实例化,并将实例化对象传递给了self.obj属性,但返回的是self(Check类的对象自身,而非类C的对象)
  105. # c1.name、c2.name实际上访问的是Check类对象的name属性,但Check没有name属性,那么他就会试图去查找__getattr__(self, name)这个魔法方法,Check中定义了此方法,返回的是类C的实例化对象
  106. # Check仅在做装饰器(@Check)时被实例化了一次,那么c1 = C('c1')、c2 = C('c2')只是把对象当函数调用了两次,从而访问了两次相应的__call__()魔法方法,self.obj第一次保存的是name为c1的类C实例化对象,而第二次调用被name为c2的类实例化对象所覆盖

  107. # 解决bug的方法:
  108. def report(cls):
  109.     class Check:
  110.         def __init__(self, *args, **kwargs):
  111.             self.obj = cls(*args, **kwargs)
  112.         def __getattr__(self, item):
  113.             print(f'正在访问{item}....')
  114.             return getattr(self.obj, item)
  115.     return Check

  116. @report
  117. class C:
  118.     def __init__(self, name):
  119.         self.name = name
  120.     def say_hi(self):
  121.         print(f'hi~ -> {self.name}')
  122.     def say_hey(self):
  123.         print(f'hey~ -> {self.name}')
  124. c1 = C('c1')
  125. c2 = C('c2')
  126. print(c1.name)
  127. # 正在访问name....
  128. # c2
  129. print(c2.name)
  130. # 正在访问name....
  131. # c2
  132. c1.say_hi()
  133. # 正在访问say_hi....
  134. # hi~ -> c1
  135. c2.say_hey()
  136. # 正在访问say_hey....
  137. # hey~ -> c2
  138. # 由于report返回的是Check类,被@report装饰过的class C其实是被替换为class Check,执行cx = C('cx')时就是在实例化Check类,调用构造函数__init__(),实例化report装饰器装饰过的类class C,然后把实例化的对象保存在self.obj属性中
  139. # Check被实例化了两次,没有出现属性覆盖的bug
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

发表于 2023-4-10 09:18:41 | 显示全部楼层

您好,请问第148行,为什么不是打印:正在访问say_hi()....
而是打印:正在访问say_hi....
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-5-5 14:53

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表