【Python】封装、继承、多态、闭包、装饰器【属性、方法】
1. 封装
-
定义:将属性和方法封装到类中,隐藏内部细节,对外只提供访问接口,控制数据读写。
-
【注意】:
- 共有:直接访问
- 私有:双下划线__开头,外部不能直接访问。需要通过get/set方法取值,改值。
class Person:
def __init__(self, name, age):
self.name = name # 公有
self.__age = age # 私有
# 取值
def get_age(self):
return self.__age
# 改值(带校验)
def set_age(self, age):
if 0 < age < 120:
self.__age = age
2. 继承
- 定义:子类复用父类已有的属性和方法,不用重复写代码;子类可扩展新方法、重写父类方法。
# 父类
class Animal:
def eat(self):
print("动物需要吃东西")
# 子类继承父类
class Dog(Animal):
# 扩展自己的方法
def bark(self):
print("狗狗汪汪叫")
3. 多态
- 定义:继承 + 方法重写 前提下,同一个父类方法,不同子类有不同实现;
class Animal:
def talk(self):
pass
class Dog(Animal):
def talk(self):
print("汪汪")
class Cat(Animal):
def talk(self):
print("喵喵")
# 多态调用
def show(ani):
ani.talk()
show(Dog())
show(Cat())
4. 类
-
抽象类:(1)不能实例化(不能创建对象);(2)里面的抽象方法,只声明,不实现;(3)子类必须重写所有抽象方法,否则子类也不能实例化(4)继承ABC(5)用abcstractmethod装饰器装饰函数。
- 注意:加了@abcstractmethod装饰器的抽象类函数,必须重写,而没有加装饰器的对象函数可以不用重写直接调用
from abc import ABC, abstractmethod
# 抽象类:继承 ABC
class Animal(ABC):
# 抽象方法:没有方法体,只有定义
@abstractmethod
def speak(self):
pass
# 抽象类可以有普通方法
def eat(self):
print("动物都要吃东西")
# --------------------不能实例化对象,直接报错
a = Animal() # 直接报错!
# --------------------子类重写抽象类函数
class Dog(Animal):
# 必须实现父类抽象方法
def speak(self):
print("汪汪叫")
# 可以创建子类对象
d = Dog()
d.speak()
# 未加装饰器的对象函数可以直接调用
d.eat()
5. 属性
- 类属性:写在类里面,方法外面;所有实例共享;因此修改类属性值时,所有对象获取都会变;类和对象都能访问。
class Person:
# 类属性
country = "中国"
p1 = Person()
p2 = Person()
# 所有对象共用
print(p1.country)
print(p2.country)
print(Person.country) # 类也可以直接访问
- 对象属性:定义在__init__里。每个对象独有,互不干扰。只能对象访问,类不能直接访问。
class Person:
country = "中国" # 类属性
def __init__(self, name, age):
# 对象属性
self.name = name
self.age = age
p1 = Person("小明", 18)
p2 = Person("小红", 20)
- 私有属性:变量前有"__";外部不能直接访问,只能类内部访问;推荐使用get/set方法来操作
class Person:
def __init__(self, name, age):
self.name = name # 公有
self.__age = age # 私有属性
def get_age(self):
return self.__age
def set_age(self, a):
if 0 < a < 120:
self.__age = a
6. 方法
- 对象方法:默认创建的就是对象方法。
class Person:
# 对象方法
def run(self):
print("人在跑步")
# 调用
p = Person()
p.run()
- 类方法:必须带装饰器'@classmethod';第一参数必须是cls;可以用类调用,也可以用对象调用;默认只能访问类属性,不能直接访问对象属性,如果需要访问对象属性,用类名.对象属性名.。
class Person:
count = 0 # 类属性
def __init__(self, name):
self.name = name
@classmethod
def show_count(cls):
print("人数:", cls.count)
print("姓名", self.name) # 注意:无法调用对象属性,因此这里会报错
print("姓名", Person.name) # 注意:可行
if __name__ == '__main__':
ps = Person("张三")
ps.show_count()
- 私有方法:方法名前面带有"__";外部不能调用,只允许类内部调用;
class Person:
# 私有方法
def __secret(self):
print("这是私有内部逻辑")
# 公有方法可以调用私有方法
def work(self):
self.__secret()
print("正常工作")
if __name__ == '__main__':
p = Person()
p.work()
# p.__secret() 报错,外部访问不了
- 静态方法:既不访问类属性;也不访问对象属性;只是放在类里的普通函数,用@staticmethod装饰;调用: 类名.静态方法() / 对象.静态方法()
class Math:
@staticmethod
def add(a, b):
return a + b
print(Math.add(1,2))
7. 闭包
- 特点:(1) 函数嵌套(外层函数 + 内层函数) (2)内层嵌套使用外层函数的局部变量 (3)外层函数return返回内层函数。
- 作用:(1)保存函数状态 (2)变量私有化,不用全局变量 (3)装饰器底层就是闭包
- 闭包三要素:(1) 外层函数里定义局部变量;(2)内层函数使用外层变量(3)外层函数返回内层函数名
def outer():
# 外层局部变量
num = 10
# 内层函数
def inner():
print(num) # 内层用外层变量
# 返回内层函数
return inner
# 接收内层函数
f = outer()
f() # 输出 10
- 闭包如果想要修改外层变量:需要用nonlocal
def count():
n = 0
def add():
nonlocal n # 声明不是局部变量,用外层的
n += 1
return n
return add
c = count()
print(c()) # 1
print(c()) # 2
print(c()) # 3
8. 装饰器:本质是闭包
-
作用:不修改原函数代码,不改变调用方式,给函数加新功能。
-
三要素:(1)外层函数接收原函数当参数;(2)内层函数写前置 + 后置 扩展逻辑。 (3)外层返回内层函数
-
代码示例
- 语法结构
# 第一层:收装饰器参数 def 外层(装饰器参数): # 第二层:收原函数 def 中间层(func): # 第三层:包装执行 def 内层(*args, **kwargs): # 前置逻辑 res = func(*args, **kwargs) # 后置逻辑 return res return 内层 return 中间层- 无参装饰器
# 1. 定义装饰器 def deco(func): # 内层包装函数 def wrapper(): print("函数开始") # 前置 func() # 执行原函数 print("函数结束") # 后置 return wrapper # 2. 使用装饰器 @语法糖 @deco def hello(): print("我是原函数") # 3. 直接调用 hello()- 装饰带参数的函数
def deco(func): def wrapper(*args, **kwargs): print("开始执行") res = func(*args, **kwargs) # 接收原函数返回值 print("执行完毕") return res return wrapper @deco def add(a,b): return a + b print(add(3,5))- 带开关参数的装饰器
# 第一层:接收装饰器参数 flag def log_switch(flag): # 第二层:接收被装饰函数 func def deco(func): # 第三层:包装函数 def wrapper(*args, **kwargs): if flag: print("【日志开启】开始执行函数") res = func(*args, **kwargs) if flag: print("【日志开启】函数执行结束") return res return wrapper return deco # 使用:给装饰器传参数 True @log_switch(flag=True) def hello(name): print(f"你好:{name}") hello("张三")- 传字符串参数
def tip(msg): def deco(func): def wrapper(): print(f"提示:{msg}") func() return wrapper return deco @tip("正在执行加法函数") def add(): print(10 + 20) add()-
多装饰器
- 装饰器从上往下装饰,从下往上执行
# 装饰器1 def deco1(func): print("【deco1 装饰阶段】") def wrapper(): print("deco1 执行前") func() print("deco1 执行后") return wrapper # 装饰器2 def deco2(func): print("【deco2 装饰阶段】") def wrapper(): print("deco2 执行前") func() print("deco2 执行后") return wrapper # 装饰器3 def deco3(func): print("【deco3 装饰阶段】") def wrapper(): print("deco3 执行前") func() print("deco3 执行后") return wrapper # ------------------------执行结果 【deco3 装饰阶段】 【deco2 装饰阶段】 【deco1 装饰阶段】 deco1 执行前 deco2 执行前 deco3 执行前 ===== 原函数 hello 执行 ===== deco3 执行后 deco2 执行后 deco1 执行后
9. 鸭子类型
- 定义:不看类型,只看方法。只要长得像鸭子、叫起来像鸭子、它就是鸭子。
- 特点:(1) Python不强制继承关系;(2) 只要对象有这个方法,就能调用。(3) 多态的简化版
class Duck:
def speak(self):
print("嘎嘎")
class Dog:
def speak(self):
print("汪汪")
# 函数不关心类型,只关心有没有 speak 方法
def say(animal):
animal.speak()
say(Duck())
say(Dog())

浙公网安备 33010602011771号