鱼C论坛

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

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

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

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

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

x
0. 本节视频




1. 温馨提示

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


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-10-24 19:44:15 | 显示全部楼层
老师,视频呢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-3 23:17:55 | 显示全部楼层
大概什么时候可以完结啊?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

使用道具 举报

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

@report
class C:
    pass

c = C()
# 开始实例化对象
# 实例化对象完成

@report
class C:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
        print('构造函数被调用')

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

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

c = Counter()
c()# 已经被调用了1次
c()# 已经被调用了2次
c()# 已经被调用了3次

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

@Counter
def say_hi():
    print('hi~')

print(say_hi)# <__main__.Counter object at 0x000001E41134AFD0>
# 函数say_hi已经被掉包成Counter的对象
say_hi()
# 已经被调用了1次
# hi~
say_hi()
# 已经被调用了2次
# hi~
say_hi()
# 已经被调用了3次
# hi~

# 用类做装饰器来装饰类
class Check:
    def __init__(self, cls):
        self.cls = cls
    def __call__(self, *args, **kwargs):
        self.obj = self.cls(*args, **kwargs)
        return self
    def __getattr__(self, item):
        print(f'正在访问{item}....')
        return getattr(self.obj, item)

@Check
class C:
    def say_hi(self):
        print(f'hi~ -> {self.name}')
    def say_hey(self):
        print(f'hey~ -> {self.name}')

# 将类C改写
@Check
class C:
    def __init__(self, name):
        self.name = name
    def say_hi(self):
        print(f'hi~ -> {self.name}')
    def say_hey(self):
        print(f'hey~ -> {self.name}')

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

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

@report
class C:
    def __init__(self, name):
        self.name = name
    def say_hi(self):
        print(f'hi~ -> {self.name}')
    def say_hey(self):
        print(f'hey~ -> {self.name}')
c1 = C('c1')
c2 = C('c2')
print(c1.name)
# 正在访问name....
# c2
print(c2.name)
# 正在访问name....
# c2
c1.say_hi()
# 正在访问say_hi....
# hi~ -> c1
c2.say_hey()
# 正在访问say_hey....
# hey~ -> c2
# 由于report返回的是Check类,被@report装饰过的class C其实是被替换为class Check,执行cx = C('cx')时就是在实例化Check类,调用构造函数__init__(),实例化report装饰器装饰过的类class C,然后把实例化的对象保存在self.obj属性中
# Check被实例化了两次,没有出现属性覆盖的bug
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

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

您好,请问第148行,为什么不是打印:正在访问say_hi()....
而是打印:正在访问say_hi....
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-12-22 09:39

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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