面向对象编程的基本实现
通过class关键字来定义一个类:
eg. 定义一个汽车的类
class Cart():
pass
ps: (1)类名的书写规范,建议使用驼峰命名法:
大驼峰:MyCar XiaoMi
小驼峰:myCar xiaoMi
(2)类中需要声明什么内容:
一个类有特征和功能两个内容组成:
特征就是一个描述:颜色:白色,品牌:奥迪,排量:2.4。
功能就是一个能力:拉货,兜风。
特征在编程中就是一个变量,在类中称为:属性。
功能在编程中就是一个函数,在类中称为:方法。
类中属性一般定义在前面,方法定义在后面。
# 定义一个汽车的类
class Cart():
#属性 ==> 特征 ==> 变量
color = '白色' # 表示颜色属性
brand = '奥迪' # 表示品牌属性
pailiang = 2.4 # 表示排列属性
#方法 ==> 功能 ==> 函数
def luohuo(self):
print('小汽车能拉货')
def doufeng(self):
print('小汽车能兜风')
def kuaile(self):
print('能快乐玩耍')
**********************************
如何通过类实例化对象并使用?
aodiobj = Cart()
# print(aodiobj,type(aodiobj))
# <__main__.Cart object at0x106f08550> <class '__main__.Cart'>
# 调用对象的方法
aodiobj.bamei()
# 获取对象的属性
print(aodiobj.brand)
成员属性和成员方法的操作
对象操作成员
1.对象的成员:一个对象通过类实例化之后,那么在类中定义的属性和方法,可以使用实例化的对象进行操作。类中定义的属性也称为成员属性,类中定义的方法,也成为成员方法。
成员属性:
访问: 对象.成员属性名
修改: 对象.成员属性名法 = 新值。(此时等于给这个对象创建了一个自己的属性)
添加: 对象.新成员属性 = 值 (此时是给这个对象自己新建了一个属性)
删除: del 对象.成员属性 (注意:只能删除这个对象自己的属性)
成员方法:
访问: 对象.成员方法名()
修改: 对象.成员方法名 = func(此时等于给这个对象创建了一个自己的方法)
添加: 对象.方法名 = func (此时是给这个对象自己新建了一个方法)
删除: del 对象.方法名 (注意:只能删除这个对象自己的方法)
2.对象的成员的操作
在类的外部,使用对象操作成员属性
(1)访问成员属性:先访问a对象自己的color属性,没有,就去访问这个对象的类的属性res = a.color # 通过对象访问类中的属性
(2)修改对象的属性值: 实际上等于给这个对象创建了一个a对象自己的color属性。a.color = '黑色' # 修改对象的属性print(b.color) # b对象的属性依然原来的值
(3)添加对象的属性: 给当前a对象创建了自己独有的属性a.name = 'A6' # 给对象添加属性 此时的name属性只属于当前a这个对象。 print(b.name) # AttributeError: 'Cart' object has no attribute 'name' 。
(4)删除属性,只能删除这个对象自己的属性,包括给对象添加的和修改的
# del a.brand # AttributeError: brand
del a.color
brand属性不能删除,但是name属性可以删除:因为删除一个对象的属性时,只能删除当前这个对象自己的属性才可以,上面例子中的brand属性并不是a对象自己的属性,而是属性Cart这个类,因此不能删除。而name这个属性,是单独给a对象添加的属性,因为可以删除。
3.在类的外部,操作对象的方法:
访问对象的方法:实际上如果这个对象没有自己独立的方法,那么会访问这个对象的类的方法。
res = a.lahuo() # 通过对象访问类中方法
修改对象的方法:给这个对象的方法重新定义
def func():
print('这是一个新的方法')
a.lahuo = func # 把一个函数赋值给成员
a.lahuo() # 去调用方法
添加新的方法:给这个对象自己新创建的方法
a.func2 = func
a.func2()
删除方法:可以删除当前这个对象自己的方法
del a.lahuo # 可以删除del a.func2 # 可以删除
del a.bamei # 不可以删除这个对象的类的方法
总结:
一个类可以实例化出多个对象,每个对象在内存中都独立存在的。
当通过类实例化对象时,并不会把类中的成员复制一份给对象,而去给对象了一个引用。
访问对象成员的时候,如果对象自己没有这个成员,对象会向实例化它的类去查找。
对象成员的添加和修改,都只会影响当前对象自己,不会影响类和其它对象。
删除对象的成员时,必须是该对象自己具备的成员才可以,不能删除类中引用的成员。
对类的成员操作,会影响通过这个类创建的对象,包括之前创建的。
1.成员方法 self
# 定义人类
class Person():
#成员属性
name = '名字'
age = '年龄'
sex = '性别'
#成员方法
def s**ing(self):
print('会唱。。。')
def dance(self):
print('会跳。。。')
def rap(self):
print(f'我是{self.name}我会rap。。。')
def func(self):
测试,在类的内部是否可以像类的外部一样,去访问和操作成员
print(self)
print(self.name) # 访问对象的属性
self.name = '张三三' # 修改对象的属性
self.sanwei = '80 80 80' #添加对象的属性
print(self.sanwei)
self.rap() # 调用对象的方法
# 只要是对象能干的事,self都可以代表对象去完成
#定义不含self的方法 绑定类方法 (不接受对象参数的方法,只能使用类去访问)
def func2():
print('我是一个没有self的方法')
# 实例化对象
zs = Person()
# zs.name = '张三'
# 通过类实例化的对象,可以在类的外部去访问成员属性和成员方法 对象.成员
# print(zs)
# zs.func()
# zs.rap()
self 在类的方法中代表当前这个对象。
self 代表调用这个方法的对象,谁调用了这个方法,self就代表谁。
self 就可以在类的内部代替对象进行各种操作。
如果在类中定义的方法不含self这个形参时,(self形参,包括其它的代替都不可以)那么这个方法就不能使用对象去调用,不含self形参的方法,只能使用类去调用,这种不接受对象作为形参的方法,叫做绑定类方法。
zs.func2()
Person.func2()
2.魔术方法
初始化方法
魔术方法:
魔术方法也和普通方法一样都是类中定义的成员方法。
魔术方法不需要去手动调用的,魔术方法会在某种情况下,自动触发。(自动执行)
魔术方法还有一个比较特殊的地方:就是多数的魔术方法 前后都有两个连续的下划线。
魔术方法不是我们自己定义的,而是系统定义好的,我们来使用。
init 初始化方法:
触发机制:在通过类实例化对象后,自动触发的一个方法
作用: 可以在对象实例化后完成对象的初始化(属性的赋值,方法的调用。。)
应用场景: 文件的打开,数据的获取,干活前的一些准备功能
class Person():
#成员属性
name = None
age = None
sex = None
#__init__ 初始化方法
def __init__(self,n,a,s):
print('我是一个初始化方法')
# 完成对象属性的初始化
self.name = n
self.age = a
self.sex = s
# 调用方法
self.say()
# 成员方法
def say(self):
print(f'大噶好,唔系{self.name},系兄弟就来砍我。。')
# 实例化对象
# zzh = Person()
zzh = Person('渣渣灰',56,'男')
# print(zzh.name)
# 3.析构方法 del
__del __析构方法
触发机制:析构方法会在对象被销毁时自动触发。
作用:关闭一些开发的资源。
注意:是对象被销毁时触发了析构方法,而不是析构方法销毁了对象。
对象会在哪些情况下被销毁?
1. 当程序执行完毕,内存中所有的资源都会被销毁释放。
2. 使用 del 删除时。
3。.对象没有被引用时,会自动销毁。
定义一个类,完成一个日志的记录
调用这个对象的时候,传递一个日志信息
这个对象会创建一个文件,开始写入,并在最后关闭这个文件
import time
class writeLog():
#成员属性
#文件的路径
fileurl = './'
#日志文件的名称
filename = '2019-09-19'
#初识化 打开文件
def __init__(self):
#完成文件的打开
print('初始化方法触发类。完成文件的打开')
self.fileobj = open(self.fileurl+self.filename,'a+',encoding='utf-8')
#写日志的方法
def log(self,s):
print(f'把日志:{s} 写入文件中')
#析构方法
def __del__(self):
print('析构方法触发了,关闭打开的文件')
# 在对象被销毁时,关闭在初始化方法中打开的文件对象
self.fileobj.close()
实例化对象
l = writeLog()
l.log('今天天气还不错哦。。')
# 使用del手动删除
del l
# 当对象没有被引用时
writeLog()
4.日志类的封装
日志类
class Mylog
功能:能够随时写入一个日志信息
分析:
日志文件在什么地方? 默认在当前目录
日志的文件名是什么? 当前日期 2019-09-20.log
日志的格式是什么样的? 2019-09-20 12:12:12 错误信息
属性:成员属性的作用就是存储信息,供成员方法来使用的。
fileurl 日志文件的地址
filename 日志文件的名称
fileobj 打开的文件对象
方法:具体完成的一个功能的过程。
__init__() ==> 初始化方法,完成对象的初始化,并打开文件
log() ==> 负责接受用给的日志信息,并写入到日志文件中
__del__() ==> 析构方法,在对象被销毁时,关闭打开的文件
import time
class Mylog():
#属性
fileurl = './'
filename = str(time.strftime('%Y-%m-%d'))+'.log'
fileobj = None
#方法
def __init__(self):
# 打开文件
self.fileobj = open(self.fileurl+self.filename,'a+',encoding='utf-8')
def log(self,s):
# 准备数据,开始写入
# 2019-09-20 12:12:12 错误信息
date = time.strftime('%Y-%m-%d %H:%M:%S')
msg = date+' '+s+'\n'
# 写入
self.fileobj.write(msg)
def __del__(self):
# 关闭打开的文件
self.fileobj.close()
# 实例化对象
w = Mylog()
# 调用方法:写日志
w.log('今天天气不太好')
w.log('于是心情不太好')
面向对象的三大特性:封装,继承,多态
1.封装
封装就是使用特殊的语法,对成员属性和成员方法进行包装,达到保护和隐藏的目的。
但是一定注意,不能把成员全部封装死,就失去意义了。
被封装的成员主要是供类的内部使用。
被特殊语法封装的成员,会有不同的访问的权限。
封装的级别
成员 ==> 公有的
_成员 ==> 受保护的 (约定俗成,而python没有具体实现)
__成员 ==> 私有的
公有的 public 受保护的 protected 私有的 private
在类的内部 OK OK OK
在类的外部 OK No(python可以) No
在python中给成员进行私有化,其实就是改了成员的名字
私有化 ==> _类名__成员
class Person():
# 成员属性
name = '名字'
_age = '年龄' # 在成员前面 加一个 _ 受保护的成员
__sanwei = '三维'# 在成员前面 加两个 __ 私有的成员
# 初始化方法
def __init__(self,n,a,s):
self.name = n
self._age = a
self.__sanwei = s
def func(self):
# 在类的内部可以操作任意成员
print(self.__sanwei)
self.__kiss()
# 成员方法
def say(self):
print('聊聊人生和理想。。。')
def _sing(self):
print('高歌一曲,豪放一生。。。')
def __kiss(self):
print('打个kiss 。。。。')
#实例化对象
ym = Person('杨幂',28,'60 55 60')
# print(ym._age) # 在类的外部不能操作 受保护的成员 (但是Python中可以)
# print(ym.__sanwei)# 在类的外部不能操作 私有成员属性
# print(ym._Person__sanwei) # 可以使用特殊的语法获取私有成员
# ym._sing() # ok
# ym.__kiss() # X 在类的外部不能操作 私有成员属性
# ym.func()
# 查看对象的所以成员
print(ym.__dict__) # 可以获取当前对象的所有成员信息
# print(Person.__dict__) # 可以获取当前类的所有成员信息
2. 继承
当子类继承父类后,就可以去使用父类中的成员属性和方法 。(除了私有成员)
子类可以有自己独立的成员,也可以没有。
子类继承父类后,重新定义了父类中的方法,这种情况称为对父类方法的重写。
在子类中可以去直接调用父类中定义的方法 super().父类方法名()。
子类继承父类后,定义类父类中没有的方法,这种情况称为对父类的扩展。
一个父类可以被多个子类继承。
子类调用父类的方法时,如果该方法有参数要求,也需要传递参数。
# 猫科动物
class maoke():
# 属性
maose = '猫纹'
sex = 'm'
# 方法
def pao(self):
print('走猫步')
def pa(self):
print('能上树')
# 定义猫类 去继承 猫科 类
class mao(maoke):
# 继承父类后,重新定义了父类中的方法
def pa(self):
# 在子类中可以使用super直接调用父类的方法
super().pa()
print('很快的就能上树了')
def zhua(self):
print('喜欢抓老鼠')
# 通过猫类 实例化对象
h = mao()
# print(h)
# print(h.__dict__) # {}
# 可以获取对象的属性 猫对象自己的属性 ==> 猫类的属性,===> 继承的父类
# print(h.maose)
# 调用对象的方法
# h.pao()
# h.pa()
# h.zhua()
class bao(maoke):
pass
b = bao()
print(b)
b.pa()
res = 100/3
print(res)
多继承
# 多继承和多继承中的父类方法的调用
# 父亲类
class F():
def eat(self):
print('大口喝酒,大口吃肉。。。')
# 母亲类
class M():
def eat(self):
print('动作优雅,浅尝即止')
# 孩子类
class C(F,M):
def eat(self):
super().eat() # 多继承和多继承中的父类方法的调用
print('吃也哭,不吃也哭。。。')
# 实例化对象
c = C()
c.eat()
菱形继承
'''
HuMan
F M
C
'''
# 祖先类
class HuMan():
num = 444
def eat(self):
print(self.num)
print('顿顿都是小烧烤。。。')
# 父亲类
class F(HuMan):
num = 333
def eat(self):
super().eat()
print(super().num)
print('大口喝酒,大口吃肉。。。')
# 母亲类
class M(HuMan):
num = 222
def eat(self):
super().eat()
print(super().num)
print('动作优雅,浅尝即止')
# 孩子类
class C(F, M):
num = 111
def eat(self):
super().eat()
print(super().num)
print('吃也哭,不吃也哭。。。')
# 实例化对象
c = C()
c.eat()
继承的关系:C->F->M->HuMan
111
顿顿都是小烧烤。。。
444
动作优雅,浅尝即止
222
大口喝酒,大口吃肉。。。
333
吃也哭,不吃也哭。。。
# mro() 获取MRO列表,就是类的继承关系
print(C.mro())
# [<class '__main__.C'>, <class '__main__.F'>, <class '__main__.M'>, <class '__main__.HuMan'>, <class 'object'>]
super()
使用super去调用父级的方法时,实际上是在用super调用MRO列表中的上一级中的方法,
使用super去访问父级的属性时,实际上是在用super访问MRO列表中的上一级中的属性。
super()本身调用父级方法时,传递的self对象,就是这个方法中的那个self对象自己。
继承关系检测
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B,C):
pass
# 获取类的 MRO列表
# print(A.mro())
# 检测一个类是否是另一个类的子类
res = issubclass(D,B) # True 检测D类是不是B类的子类
res = issubclass(D,C) # True 检测D类是不是C类的子类
res = issubclass(D,A) # True 检测D类是不是A类的子类
res = issubclass(A,D) # False 检测A类是不是D类的子类
print(res)
3.多态
多态 普通版本
# 对于同一个方法,由于调用的对象不同(或者传入的对象不同),最终实现了不同的结果
# 定义电脑类
class Computer():
# 在电脑类中定义一个 sub 的规范的接口 方法
def usb(self,obj):
obj.start()
# 定义鼠标类
class Mouse():
def start(self):
print('鼠标启动成功,可以双击单击嗨起来。。。')
# 定义键盘类
class KeyBord():
def start(self):
print('键盘启动成功了,赶紧双击666。。。')
# 定义 U盘 类
class Udisk():
def start(self):
print('U盘启动了,赶紧检查一下我的种子还在不在。。。')
# 实例化对象
c = Computer() # 电脑对象
m = Mouse() # 鼠标对象
k = KeyBord() # 键盘对象
u = Udisk() # u盘对象
# 把不同的设备插入到电脑的usb的接口中
c.usb(m)
c.usb(k)
c.usb(u)
多态 继承版
定义一个接口规范类,其它类都继承这个类,并实现(重写)父类中的方法。
由于每个对象实现父类方法的方式或者过程都不相同,最后的结果是不一样的形态。
# 定义 USB
class USB():
'''
当前类的说明:
这个类是一个接口规范类,需要子类继承并实现start方法
start方法不做任何具体功能的实现
'''
# 在usb类中定义一个规范的接口方法,但是不实现任何功能,
def start(self):
pass
# 定义鼠标类
class Mouse(USB):
def start(self):
print('鼠标启动成功,可以双击单击嗨起来。。。')
# 定义键盘类
class KeyBord(USB):
def start(self):
print('键盘启动成功了,赶紧双击666。。。')
# 定义 U盘 类
class Udisk(USB):
def start(self):
print('U盘启动了,赶紧检查一下我的种子还在不在。。。')
# 实例化对象
m = Mouse() # 鼠标对象
k = KeyBord() # 键盘对象
u = Udisk() # U盘对象
m.start()
k.start()
u.start()
面向对象高阶-描述符与设计模式
1.描述符
当一个类中,包含了三个魔术方法(__get__,__set__,__delete__
)之一,或者全部时,那么这个类就称为描述符类。
作用:
1.描述符的作用就是对一个类中的某个成员进行一个详细的管理操作(获取,赋值,删除)
2.描述符就是代理了一个类中的成员的操作,描述符属于类,只能定义为类的属性
使用格式:
1把当前的描述符类赋值给一个需要代理的类中的成员属性
# 定义描述符类
class PersonName():
__name = 'abc'
def __get__(self, instance, owner):
# print(self,instance,owner)
return self.__name
def __set__(self, instance, value):
# print(self,instance,value)
self.__name = value
def __delete__(self, instance):
# print(self,instance)
# del self.__name
print('不允许删除')
# 定义的普通类
class Person():
# 把类中的一个成员属性交给一个描述符类来实现
# 一个类中的成员的值是另一个描述符类的对象()
# 那么当对这个类中得成员进行操作时,可以理解为就是对另一个对象的操作
name = PersonName()
# 实例化对象
zs = Person()
print(zs.name)
zs.name = '张三'
print(zs.name)
del zs.name
print(zs.name)
2.描述符的应用案例解析
定义一个学生类,需要记录 学员的id,名字,分数
class Student():
def __init__(self,id,name,score):
self.id = id
self.name = name
# self.score = score
# 检测分数范围
if score >= 0 and score <= 100:
self.score = score
else:
print('当前分数不符合要求')
def returnMe(self):
info = f'''
学员编号:{self.id}
学员姓名:{self.name}
学员分数:{self.score}
'''
print(info)
def __setattr__(self, key, value):
# 检测是否是给score进行赋值操作
if key == 'score':
# 检测分数范围
if value >= 0 and value <= 100:
object.__setattr__(self, key, value)
else:
print('当前分数不符合要求')
else:
object.__setattr__(self,key,value)
#定义描述符类 代理分数的管理
class Score():
def __get__(self, instance, owner):
return self.__score
def __set__(self, instance, value):
if value >= 0 and value <= 100:
self.__score = value
else:
print('分数不符合要求')
# 使用描述符类代理score分数属性
class Student():
score = Score()
def __init__(self,id,name,score):
self.id = id
self.name = name
self.score = score
def returnMe(self):
info = f'''
学员编号:{self.id}
学员姓名:{self.name}
学员分数:{self.score}
'''
print(info)
# 实例化对象
zs = Student(1011,'张三疯',99)
zs.returnMe()
zs.score = -20
zs.score = 88
zs.returnMe()
3.描述符的三种定义方式
格式一: 通过定义 描述符类来实现
class ScoreManage():
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
class Student():
score = ScoreManage()
格式二:使用 property 函数 来实现
class Student():
# 在当前需要被管理的类中 直接定义类似下面三个方法
def getscore(self):
print('getscore')
def setscore(self,value):
print('setscore',value)
def delscore(self):
print('delscore')
# 在 property 函数中指定对应的三个方法,对应的方法 1。__get__,2。__set__,3。__delete__
score = property(getscore,setscore,delscore)
zs = Student()
# print(zs.score)
# zs.score = 200
# del zs.score
格式三: 使用 @property 装饰器语法来实现
class Student():
__score = None
@property
def score(self):
print('get')
return self.__score
@score.setter
def score(self,value):
print('set')
self.__score = value
@score.deleter
def score(self):
print('delete')
del self.__score
zs = Student()
# print(zs.score)
zs.score = 199
print(zs.score)
del zs.score
4.单例(单态)设计模式
在当前脚本中,同一个类只能实例化一个对象,去使用。
如何实现Python中的单例设计模式呢?
1.需要有一个方法,可以去控制当前对象的创建过程?
构造方法 __new__
2.需要有一个标示来存储和表示是否有对象
创建一个私有属性 进行存储,默认值为None
3.在创建对象的方法中去检测和判断是否有对象?
如果没有对象,则创建对象,并且把对象存储起来,返回对象
如果存储的是对象,则直接返回对象,就不需要创建新的对象了
class Demo():
# 2.定义私有属性存储对象,默认值为None
__obj = None
# 1.定义构造方法
def __new__(cls, *args, **kwargs):
# 3。在创建对象的过程中,判断是否有对象
if not cls.__obj:
# 判断如果没有对象,则创建对象,并且存储起来
cls.__obj = object.__new__(cls)
# 直接把存储的对象返回
return cls.__obj
# 实例化对象
a = Demo()
b = Demo()
print(a)
print(b)
5.Mixin 混合设计模式
继承需要有一个必要的前提,继承应该是一个 'is-a' 的关系。
interface 接口类 来实现多重继承,python中本身就支持 多继承关系。
# 交通工具
class vehicle():
# 运输货物
def huo(self):
print('运输货物')
# 搭载乘客
def ren(self):
print('搭载乘客')
# 飞行器
class FlyingMixin():
def fly(self):
print('可以起飞了。。。')
# 定义汽车类
class cart(vehicle):
pass
# 定义飞机
class airplane(vehicle, FlyingMixin):
pass
# 定义直升机
class helicopter(vehicle, FlyingMixin):
pass
此时去定义一个飞行器的类 Flying,让需要飞行的交通工具,直接继承这个类。可以解决这个问题。
但是:(1)出现类多继承,违背了'is-a' 。
(2)飞行器这个类很容易被误解。
解决方案也是使用多继承,但是给飞行器这个类,定义成为一个 Mixin 混合类,此时就是等于把飞行器这个类,作为了一个扩展的功能,来扩展其它类。
装饰器 decorator
装饰器定义:在不改变原有函数代码,且保持原函数调用方法不变的情况下,给原函数增加新的功能。(或者给类增加属性和方法)
核心思想:用一个函数(或者类)去装饰一个旧函数(或者类),造出一个新函数(或者新类)。
应用场景:引入日志,函数执行时间的统计,执行函数前的准备工作,执行函数后的处理工作,权限校验,缓存等。
语法规则:在原有的函数上加上 @符,装饰器会把下面的函数当作参数传递到装饰器中,@符又被成为 语法糖。
1.装饰器的原型
利用闭包,把函数当作参数传递,并且在函数内去调用传递进来的函数,并返回一个函数。
# 定义外函数,接收一个函数作为参数
def outer(f):
# 定义内函数,并且在内函数中调用了外函数的参数
def inner():
print('我是外函数中的内函数1')
f()
print('我是外函数中的内函数2')
return inner
# 定义普通函数
# def old():
# print('我是一个普通的函数')
#
# # old() # 作为普通函数直接调用
# old = outer(old) # outer返回了inner函数,赋值给了old
# old() # 此时再调用old函数时,等同于调用了 inner 函数
# 改为装饰器用法
@outer # 此处使用的@outer的语法就是把outer作为了装饰器,等同于 old = outer(old)
def old():
print('我是一个普通的函数')
old() # old函数经过 outer装饰器进行了装饰,代码和调用方法不变,但是函数的功能发送了改变
2.装饰器应用:统计函数的执行时间
import time
# 定义一个统计函数执行时间的 装饰器
def runtime(f):
def inner():
start = time.perf_counter()
f()
end = time.perf_counter() - start
print(f'函数的调用执行时间为:{end}')
return inner
# 定义一个函数
@runtime
def func():
for i in range(5):
print(i,end=" ")
time.sleep(1)
func()
3.装饰器的嵌套语法
# 1.普通装饰器的定义
# 定义装饰器
# 外函数
def outer(func):
#内函数
def inner():
print('找到TA,成功拿到微信3')
func() # 在内函数中调用外函数中的行参-函数
print('约TA,看一场午夜电影4')
# 在外函数中返回内函数
return inner
# 使用一个装饰器
# @outer
# def love():
# print('畅谈人生')
#
# love() # == inner()
# 2.在定义一个装饰器
def kuozhan(f):
def kzinner():
print('扩展1')
f()
print('扩展2')
return kzinner
# 装饰器的嵌套 先执行下面的,再执行上面的。
@kuozhan # 2。再使用上面的 kuozhan 装饰器,装饰 上一次返回的 inner 函数,又返回了 kzinner 函数
@outer # 1。先使用离得近的 outer装饰器 装饰love函数,返回了一个 inner函数
def love():
print('跟TA畅谈人生5')
love()
'''
1 3 5 4 2
1 先使用离得近的 outer装饰器 装饰love函数,返回了一个 inner函数
2 再使用上面的 kuozhan 装饰器,装饰 上一次返回的 inner 函数,又返回了 kzinner 函数
最后在调用love函数的时候是怎么执行的
love() == kzinner()
===> 1
===> inner()
===> 3
===> love() ===> 5
===> 4
===> 2
'''
4.对带有参数的函数进行装饰
# 定义装饰器
def outer(func):
# 如果装饰器带有参数的函数,需要在内函数中定义行参,并传递给调用的函数。因为调用原函数等于调用内函数
def inner(var):
print(f'找到{var}TA,成功拿到微信。。')
func(var)
print(f'约{var}TA,看一场午夜电影。。')
return inner
# 有参数的函数
@outer
def love(name):
print(f'跟{name}畅谈人生。。。')
love('思思') #love() ==> inner() love('思思') ===> inner('思思')
5.对多参数的函数进行装饰
def outer(func):
def inner(who,name,*args,**kwargs):
print('约TA,聊微信。。。')
func(who,name,*args,**kwargs)
print('天色一晚,怎么办?')
return inner
# 定义多参数的 函数
@outer
def love(who,name,*args,**kwargs):
print(f'{who}跟{name}畅谈人生。。。')
print('完事去吃了好多美食',args)
print('看了一场电影',kwargs)
love('三多','思思','火锅','辣条','7块钱的麻辣烫',mov='唐山大地震')
'''
love() ==> inner()
love(...) ==> inner(...)
inner(...) ==> love(...)
'''
6.带有参数的装饰器
# 如果你的装饰器需要有参数,那么给当前的装饰器套一个壳,用于接收装饰器的参数
def kuozhan(var):
def outer(func):
def inner1():
print('TA给了你微信')
func()
def inner2():
print('TA给介绍了TA')
func()
# 装饰器壳的参数,可以用于在函数内去做流程控制
if var == 1:
return inner1
else:
return inner2
return outer
@kuozhan(2) # kuozhan(var) ==> outer() ==> outer(love) ==> inner()
def love():
print('谈谈人生')
love()
7.用类装饰器装饰函数
class Outer():
# 魔术方法:当把该类的对象当作函数调用时,自动触发 obj()
def __call__(self,func):
self.func = func # 把传进来的函数作为对象的成员方法
return self.inner # 返回一个函数
# 在定义的需要返回的新方法中 去进行装饰和处理
def inner(self,who):
print('拿到TA的微信')
self.func(who)
print('看一场午夜电影')
@Outer() # Outer() ==> obj @obj==>obj(love) ==> __call__(love) ==> inner()
def love(who):
print(f'{who}和TA谈谈人生')
love('哥') # inner('哥')
print(love) # 此时的 love就是属于Outer类这个对象中的inner方法
8.用类方法装饰函数
class Outer():
def newinner(func):
Outer.func = func # 把传递进来的函数定义为类方法
return Outer.inner # 同时返回一个新的类方法
def inner():
print('拿到妹子微信')
Outer.func()
print('看一场午夜电影')
@Outer.newinner # Outer.newinner(love) ==> Outer.inner
def love():
print('和妹子谈谈人生喝喝茶。。。')
love() # love() ==> Outer.inner()
9.用函数装饰器装饰类
使用函数装饰器,给类进行装饰,增加新的属性和方法。
# 定义函数,接收一个类。返回修改后的类
def kuozhan(cls):
def func2():
print('我是在装饰器中追加的新方法,func2')
cls.func2 = func2 # 把刚才定义的方法赋值给 类
cls.name = '我是在装饰器中追加的新属性 name'
#返回时,把追加类新成员的 类 返回去
return cls
@kuozhan # kuozhan(Demo) ==> cls ==> Demo
class Demo():
def func():
print('我是Demo类中定义的func方法')
Demo.func() # 此时在调用的Demo类是通过装饰器,更新过的Demo类
Demo.func2()
print(Demo.name)
10.使用类装饰器装饰类
class KuoZhan():
def __call__(self, cls):
# 把接收的类,赋值给当前对象,作为一个属性
self.cls = cls
# 返回一个函数
return self.newfunc
def newfunc(self):
self.cls.name = '我是在类装饰器中追加的新属性 name'
self.cls.func2 = self.func2
# 返回传递进来的类的实例化结果,obj
return self.cls()
def func2(self):
print('我是在类装饰器中追加的新方法 func2')
@KuoZhan() # KuoZhan() ==> obj ==> @obj(Demo) ==> __call__(Demo) ==> newfunc
class Demo():
def func(self):
print('我是Demo类中定义的func方法')
obj = Demo() # Demo() ==> newfunc() ==> obj
obj.func()
obj.func2()
print(obj.name)