Python 继承
继承的实现
- 语法:
class 类名(父类列表)
- 默认父类是
object
- Python 支持多继承
# inheritance_demo.py
class Human:
"""人类"""
# 类属性
message = "这是Human的类属性"
# 构造方法
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age
# 实例方法
def live(self):
print("住在地球上")
class Student(Human):
"""学生类"""
def study(self):
print("正在学习")
# 实例化子类对象
stu = Student("哈利波特", 12)
# 访问类属性(继承)
print(stu.message)
# 访问实例属性(继承)
print(stu.name, stu.age)
# 访问实例方法(继承)
stu.live()
# 访问实例方法(扩展)
stu.study()
类型检查
-
isinstance(实例, 类名)
-
issubclass(类名1, 类名2)
# 人类
class Human:
pass
# 学生类
class Student(Human):
pass
# 老师类
class Teacher(Human):
pass
# 检查实例与类的关系
stu = Student()
#检查对象是否是某个类及其派生类的实例
print(isinstance(stu, Human)) # 将会打印 True
# 检查类与类的关系,检查类名 1 是否是类名 2 的子类
print(issubclass(Student, Human)) # 将会打印 True
print(issubclass(Student, Teacher)) # 将会打印 False
Python 多态
多态的概念
运算符的多态表现
-
+
号
- 加法:数字+数字
- 拼接:字符串+字符串
- 合并:列表+列表
# 加法:数字+数字
print(1 + 1) # 打印 2
# 拼接:字符串+字符串
print("Hello" + "World") # 打印 Hello World
# 合并:列表+列表
print([1, 2] + [3]) # 打印 [1, 2, 3]
函数的多态表现
# 参数是字符串
print(len("Hogwarts")) #8
# 参数是列表
print(len([1, 3, 5])) #3
方法的多态表现
class China:
def speak(self):
print("汉语")
class Usa:
def speak(self):
print("英语")
# 实例化对象
cn = China()
us = Usa()
for x in (cn, us):
# 同一个变量在调用同名方法时,呈现不同的行为
# 具体呈现哪种行为,由该变量所引用的对象决定
x.speak()
#打印结果:
# 汉语
# 英语
多态与继承
- 方法重写(
Override
):子类的方法名称与父类的相同
- 重写构造方法
super().__init__()
父类名.__init__(self)
# override_demo.py
class Human:
"""人类"""
message = "这是Human的类属性"
# 构造方法
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age
# 实例方法
def live(self):
print(f"住在地球上")
class Student(Human):
"""学生类"""
# 重写父类的构造方法
def __init__(self, name, age, school):
# 访问父类的构造方法
super().__init__(name, age)
# super(Student, self).__init__(name, age)
# Human.__init__(self, name, age)
# 子类实例属性(个性)
self.school = school
# 重写父类的实例方法
def live(self):
print(f"住在{self.school}")
# 实例化子类对象
stu = Student("哈利波特", 12, "Hogwarts")
# 访问实例方法
stu.live() # 将会打印 住在Hogwarts
Python 数据类 dataclass
案例
- 场景:如果创建一只猫,信息包括猫的名字、体重、颜色。同时打印这个对象的时候,希望能打印出一个字符串(包含猫的各种信息)应该如何编写代码
class Cat:
name: str
color: str
weight: int
def __init__(self,name,weight,color):
self.name = name
self.weight = weight
self.color = color
def __str__(self):
return f"喵星人姓名:{self.name}, 年龄:{self.weight},颜色:{self.color}"
def __repr__(self):
return f"===>>>>> 喵星人姓名:{self.name}, 年龄:{self.weight},颜色:{self.color}"
数据类更优雅的实现方案
- 使用 dataclass 创建数据类
- 实例化的时候自动生成构造函数
from dataclasses import dataclass
@dataclass
class Cat:
name: str
color: str
weight: int
if __name__ == '__main__':
cat = Cat("菠萝", "橘猫", 9)
print(cat) #打印 Cat(name='菠萝', color='橘猫', weight=9)
field 的使用
# 错误写法,执行报错
from dataclasses import dataclass
@dataclass
class Cat:
name: str
color: str
weight: int
children: list=[1,2,3] #会报错
# 正确写法,可变类型必须使用field
from dataclasses import dataclass, field
@dataclass
class Cat:
name: str
color: str
weight: int = 2
children: list = field(default_factory=list)
if __name__ == '__main__':
cat = Cat("菠萝", "橘猫", 9, [1,2,3])
print(cat) #Cat(name='菠萝', color='橘猫', weight=9, children=[1, 2, 3])
field 常用参数
参数名 |
参数功能 |
default |
字段的默认值 |
default_factory |
定义可变量参数的默认值,default 和 default_factory 不能同时存在 |
init |
如果为 true(默认值),该字段作为参数包含在生成的 init() 方法中。 |
repr |
如果为 true(默认值),该字段包含在生成的 repr() 方法返回的字符串中。 |
field default 参数
import dataclasses
@dataclasses.dataclass
class Cat:
name: str
color: str
weight: str = dataclasses.field(default=5)
children: list = dataclasses.field(default_factory=list)
children1: list = dataclasses.field(default_factory=lambda:[1,2,3])
children2: dict = dataclasses.field(default_factory=lambda: {"name":"喵"})
field init 参数
- 如果为
True
(默认值),该字段作为参数包含在生成的 init()
方法中。
- 如果为
False
,该字段不会包含 init()
方法参数中。
import dataclasses
@dataclasses.dataclass
class Cat:
name: str
color: str
weight: str = dataclasses.field(default=5)
children: list = dataclasses.field(default_factory=list,init=False)
field repr 参数
- 如果为
True
(默认值),该字段包含在生成的 repr()
方法返回的字符串中。
- 如果为
False
,该字段不会包含在生成的 repr()
方法返回的字符串中。
import dataclasses
@dataclasses.dataclass
class Cat:
name: str
color: str
weight: str = dataclasses.field(default=5)
children: list = dataclasses.field(default_factory=list,repr=False)
常用的方法
import dataclasses
@dataclasses.dataclass
class Cat:
name: str
color: str
weight: int = dataclasses.field(default=5)
# 无参的函数
children: list = dataclasses.field(default_factory=lambda:[1,2,3])
children1: dict = dataclasses.field(default_factory=lambda: {"name":"喵"})
cat = Cat("aa","red",10,[1,3],{"name":"喵喵"})
print(cat) #打印:Cat(name='aa', color='red', weight=10, children=[1, 3], children1={'name': '喵喵'})
#打印:{'name': 'aa', 'color': 'red', 'weight': 10, 'children': [1, 3], 'children1': {'name': '喵喵'}}
print(dataclasses.asdict(cat))
Python内置装饰器
内置装饰器 |
含义 |
classmethod |
类方法 |
staticmethod |
静态方法 |
普通方法
# 1. 定义
class MethodsDemo:
param_a = 0 #类变量
def normal_demo(self): # 定义一个类方法,第一个参数必须为self
"""
普通方法
:return:
"""
print("这是一个普通方法", self.param_a)
# 2. 调用
md = MethodsDemo()
md.normal_demo() #这是一个普通方法 0
类方法
- 定义:
- 使用 @classmethod 装饰器,第一个参数为类本身,所以通常使用cls命名做区分(非强制)
- 在类内可以直接使用类方法或类变量,无法直接使用实例变量或方法
- 调用:
- 无需实例化,直接通过 类.方法名 调用,也可以通过 实例.方法名 调用
# 1. 类的定义
class MethodsDemo:
param_a = 0
# 定义类方法必须加 classmethod装饰器
@classmethod
def classmethod_demo(cls):
"""
类方法,第一个参数需要改为cls
:return:
"""
print("类方法", cls.param_a)
# 2. 类的调用
##打印类方法 0
MethodsDemo.classmethod_demo() # 无需实例化,直接调用
#实例的调用
sl = MethodsDemo()
sl.classmethod_demo() #打印类方法 0
静态方法
- 定义:
- 使用 @staticmethod 装饰器,没有和类本身有关的参数
- 无法直接使用任何类变量、类方法或者实例方法、实例变量
- 调用:
- 无需实例化,直接通过 类.方法名 调用,也可以通过 实例.方法名 调用
# 1. 定义
class MethodsDemo:
param_a = 0
@staticmethod
def static_demo():
"""
静态方法
:return:
"""
print("静态方法") # 无法直接调用类变量
# 2. 类的调用
MethodsDemo.static_demo() #打印 静态方法
#3. 实例的调用
sl = MethodsDemo()
sl.static_demo() #打印 静态方法
普通方法、类方法、静态方法
名称 |
定义 |
调用 |
关键字 |
使用场景 |
普通方法 |
至少需要一个参数self |
实例名.方法名() |
无 |
方法内部涉及到实例对象属性的操作 |
类方法 |
至少需要一个cls参数 |
类名.方法名() 或者实例名.方法名() |
@classmethod |
如果需要对类属性,即静态变量进行限制性操作 |
静态方法 |
无默认参数 |
类名.方法名() 或者实例名.方法名() |
@staticmethod |
无需类或实例参与 |
实际案例
- 右边的代码实现的需求是格式化输出时间
- 如果现在需求变更,输入 年、月、日 没法保证格式统一,可能是json,可能是其他格式的字符串,在不修改构造函数的前提下,如何更改代码
class DateFormat:
def __init__(self, year=0, month=0, day=0):
self.year = year
self.month = month
self.day = day
def out_date(self):
return f"输入的时间为{self.year}年,{self.month}月,{self.day}日"
year, month, day = 2017, 7, 1
demo = DateFormat(year, month, day)
print(demo.out_date()) #输入的时间为2017年,7月,1日
静态方法实际案例
- 此方法没有任何和实例、类相关的部分,可以作为一个独立函数使用
- 某些场景下,从业务逻辑来说又属于类的一部分
- 例子:简单工厂方法
# static 使用场景
def EZ():
pass
class Jinx(object):
pass
class Timo(object):
pass
class HeroFactory:
# staticmethod 使用场景,
# 方法所有涉及到的逻辑都没有使用实例方法或者实例变量的时候
# 伪代码
@staticmethod
def create_hero(hero):
if hero == "ez":
return EZ()
elif hero == "jinx":
return Jinx()
elif hero == "timo":
return Timo()
else:
raise Exception("此英雄不在英雄工厂当中")
闭包函数
函数引用
def hogwarts():
print("hogwarts")
harry = hogwarts
harry() #打印hogwarts
闭包函数
- 闭包的内部函数中,对外部作用域的变量进行引用
- 闭包无法修改外部函数的局部变量
- 闭包可以保存当前的运行环境
def output_student(grade):
def inner(name, gender):
print(f"霍格沃兹测试学社开学啦!学生的名称是{name},性别是{gender},年级是{grade}")
return inner
student = output_student(1)
student("罗恩", "男")
student("哈利", "男")
student("赫敏", "女")
# #打印结果为:
# 霍格沃兹测试学社开学啦!学生的名称是罗恩,性别是男,年级是1
# 霍格沃兹测试学社开学啦!学生的名称是哈利,性别是男,年级是1
# 霍格沃兹测试学社开学啦!学生的名称是赫敏,性别是女,年级是1
Python 装饰器
装饰器示例
# 不使用装饰器的代码
def timer(func):
print("计时开始")
func()
print("计时结束")
def hogwarts():
print("霍格沃兹测试学院")
timer(hogwarts)
#打印结果为:
# 计时开始
# 霍格沃兹测试学院
# 计时结束
装饰器
# 使用装饰器的代码
def timer(func):
def inner():
print("计时开始")
func()
print("计时结束")
return inner
@timer
def hogwarts():
print("霍格沃兹测试学院")
hogwarts()
#打印结果:
# 计时开始
# 霍格沃兹测试学院
# 计时结束
装饰器练习
装饰带参数函数
import datetime
def timer(func):
def inner(*args, **kwargs):
# 获取当前时间
start_time = datetime.datetime.now()
func(*args, **kwargs)
end_time = datetime.datetime.now()
print(f"函数的执行时间{end_time-start_time}")
return inner
@timer
def hogwarts(name):
print("霍格沃兹测试学社", name)
hogwarts('张张')
#打印:
#霍格沃兹测试学社 张张
#函数的执行时间0:00:00