python编程语言L2

Python 继承

  • 继承(Inheritance
    • 复用父类的公开属性和方法
    • 拓展出新的属性和方法

继承的实现

  • 语法: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)
    • 检查类名 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 多态

多态的概念

  • 多态(Polymorphism
    • 同名方法呈现多种行为

运算符的多态表现

  • +
    • 加法:数字+数字
    • 拼接:字符串+字符串
    • 合并:列表+列表
# 加法:数字+数字
print(1 + 1)  # 打印 2

# 拼接:字符串+字符串
print("Hello" + "World")  # 打印 Hello World

# 合并:列表+列表
print([1, 2] + [3])  # 打印 [1, 2, 3]

函数的多态表现

  • len()函数
    • 可以接收字符串
    • 可以接收列表
# 参数是字符串
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

  • dataclass 优势

    • 可读性强
    • 操作灵活
    • 轻量
  • 应用场景

    • 创建对象
    • 完美融合平台开发 ORM 框架

案例

  • 场景:如果创建一只猫,信息包括猫的名字、体重、颜色。同时打印这个对象的时候,希望能打印出一个字符串(包含猫的各种信息)应该如何编写代码
    • 问题:
      • 数据修改不方便
      • 代码冗余
    • 解决方案:
      • 使用自定义类实现数据类
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)

常用的方法

  • asdict() 转化实例对象为字典格式
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 静态方法

普通方法

  • 定义:
    • 第一个参数为self,代表 实例本身
  • 调用:
    • 要有实例化的过程,通过 实例对象.方法名 调用
# 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