admin 管理员组

文章数量: 1184232

一、函数式编程

Python的函数式编程(Functional Programming, FP)是一种编程范式。

1. 函数作为参数

  • 将核心逻辑传入方法体,使该方法的适用性更广。

示例1:

def func01():
    print("func01执行")
​
def func02():
    print("func02执行")
​
# 通用
def func03(func):
    print("func03执行")
    func()
​
func03(func02)
func03(func01)

2. 函数作为返回值

我们可以通过闭包思想完成这一特性。

闭包是指引用了此函数外部嵌套函数的变量的函数,并把该函数作为返回值,是一种思想。

2.1 闭包定义

闭包必须满足以下三个条件:

  • 必须有一个内嵌函数

  • 内嵌函数必须引用外部函数中变量

  • 外部函数返回值必须是内嵌函数

2.2 闭包优缺点

优点

  1. 逻辑连续,当闭包作为另一个函数调用参数时,避免脱离当前逻辑而单独编写额外逻辑。

  2. 方便调用上下文的局部变量。

  3. 加强封装性,是第2点的延伸,可以达到对变量的保护作用。

缺点

引用在,空间不灭:闭包使得函数中的变量保存在内存中,内存消耗很大

示例1

def give_yasuiqian(money):
    def child_buy(obj, m):
        nonlocal money
        if money > m:
            money -= m
            print('买', obj, '花了', m, '元, 剩余', money, '元')
        else:
            print("买", obj, '失败')
    return child_buy
cb = give_yashuqian(1000)    
cb('变形金刚', 200)
cb('漫画三国', 100)
cb('手机', 1300)

示例2

# file : closure.py
def make_power(y):
    def fn(x):
        return x ** y
    return fn
pow2 = make_power(2)
print("5的平方是:", pow2(5))
pow3 = make_power(3)
print("6的立方是:", pow3(6))

3. 装饰器

装饰器是Python对闭包思想的具体语法实现,其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能。

  1. 日志记录:可以使用装饰器来记录函数的输入、输出或执行时间。

  2. 认证和授权:装饰器可以用于检查用户是否有权限执行特定操作。

  3. 缓存:装饰器可以缓存函数的结果,从而提高执行效率。

  4. 参数验证:可以使用装饰器来验证函数的输入参数是否符合预期。

  5. 代码注入:装饰器可以在函数的执行前后注入额外的代码。

3.1 基本装饰器

def my_decorator(func):
    print("I am decorator")
​
    def wrapper():
        print("Something is happening before the function is called.")
        func()  # 调用传入的函数
        print("Something is happening after the function is called.")
​
    return wrapper
@my_decorator  # 应用装饰器
def say_hello():
    print("Hello!")
​
print("say_hello is:", say_hello)  # 装饰器装饰之后的函数
say_hello()  # 调用经过装饰器之后的函数
  • my_decorator 是一个接受一个函数 $func$ 作为参数的装饰器。

  • wrapper函数在被装饰函数前后添加了额外的操作。

  • 使用 my_decoratorsay_hello 函数装饰,使其在调用前后执行 wrapper 中的代码。

3.2 带参装饰器

def repeat(num):
    print('...函数名...()...函数执行...')
    def decorator(func):
        print('...装饰器...()...装饰器执行...')
        def wrapper(*args, **kwargs):
            for _ in range(num):
                func(*args, **kwargs)
        return wrapper
    return decorator
@repeat(3)  # 应用装饰器,重复执行下面的函数3次
def greet(name):
    print(f"Hello, {name}!")
greet("Alice")  # 调用被装饰的函数
  • repeat 是一个接受参数的装饰器工厂函数,它返回一个装饰器。

  • decorator 是真正的装饰器,它接受一个函数 $func$ 作为参数。

  • wrapper函数重复执行被装饰的函数 num 次。

3.3 装饰器链

def uppercase(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
​
    return wrapper
​
​
def exclamation(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + "!"
​
    return wrapper
​
​
@exclamation
@uppercase
def say_hello(name):
    return f"Hello, {name}"
​
​
greeting = say_hello("Bob")
print(greeting)  # 输出 "HELLO, BOB!"
  • uppercase和 exclamation 是两个装饰器,分别将文本转换为大写和添加感叹号。

  • 使用 exclamationuppercase 创建装饰器链,它们按照声明的顺序依次应用。

3.4 类装饰器

class MyDecorator:
    def __init__(self, func):
        self.func = func
​
    def __call__(self, *args, **kwargs):
        print("Something is happening before the function is called.")
        result = self.func(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result
​
​
@MyDecorator  # 应用类装饰器
def say_hello(name):
    print(f"Hello, {name}!")
​
​
say_hello("Charlie")  # 调用被装饰的函数
  • MyDecorator 是一个类装饰器,它接受一个函数 func 作为参数并在 __call__ 方法中执行额外操作。

3.5 应用实例

性能计时器

import time
​
​
def performance_timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()  # 记录开始时间
        result = func(*args, **kwargs)  # 执行被装饰的函数
        end_time = time.time()  # 记录结束时间
        print(f"{func.__name__} took {end_time - start_time} seconds to execute.")
        return result
​
    return wrapper
​
​
@performance_timer  # 应用性能计时器装饰器
def time_consuming_function():
    # 模拟一个耗时操作
    time.sleep(2)
​
​
time_consuming_function()  # 调用被装饰的函数并测量执行时间
  • $performance_timer$ 是一个装饰器,它用于测量被装饰函数的执行时间。

  • $wrapper$ 函数 $记录开始时间、执行被装饰函数、记录结束时间$,最后输出执行时间。

二、面向对象编程

面向对象编程(Object-Oriented Programming,简称OOP)是一种通过组织对象来设计程序的编程方法。

Python天生就是面向对象的模块化编程。

1. 初识类和对象

示意图:

       /-------> BYD  E6(京A.88888) 实例,对象
车(类)  
      \-------> BMW  X5(京B.00000) 实例(对象)
   
      /-------> 小京巴(户籍号:000001)
狗(类)  
      \-------> 导盲犬(户籍号:000002)

      /-------> 100 (对象)
int(类)  
      \-------> 200 (对象)

1.1 类→Class

物以类聚

1.1.1 概念

类是对一类对象的抽象,是对象的模板或蓝图。它定义了对象的属性(特征)和方法(功能)。

1.1.2 创建
  • 数据成员:表明事物的特征。 相当于变量

  • 方法成员:表明事物的功能。 相当于函数

  • 通过class关键字定义类。

  • 类的创建语句语法:

    class 类名 (继承列表):
        实例属性(类内的变量) 定义
       实例方法(类内的函数method) 定义
       类变量(class variable) 定义
       类方法(@classmethod) 定义
       静态方法(@staticmethod) 定义

  • 参考代码:

    class Dog:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    ​
        def bark(self):
            print(f"{self.name} says Woof!")
    • 类名就是标识符,建议(有点强烈)首字母大写

    • 类名实质上就是变量,它绑定一个类

    • self代表类实例化的对象本身

1.2 对象→Object

人以群分,每个人就是一个对象。

1.2.1 概念

对象是类的实例化,是类的实际数据存储,具有类所定义的属性和方法。

1.2.2 创建

  • 构造函数调用表达式

    变量 = 类名([参数])

  • 说明

    • 变量存储的是实例化后的对象地址

    • 类参数按照初始化方法的形参传递

      • 对象是类的实例,具有类定义的属性和方法。

      • 通过调用类的构造函数来创建对象。

      • 每个对象有自己的状态,但共享方法。

  • 示例代码

    class Dog:
        pass
    ​
    # 创建第一个实例:
    dog1 = Dog()
    print(id(dog1))  # 打印这个对象的ID
    # 创建第二个实例对象
    dog2 = Dog()  # dog2 绑定一个Dog类型的对象
    print(id(dog2))
    ​
    ​
    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    ​
        def introduce(self):
            print(f"My name is {self.name} and I am {self.age} years old.")
            
    person1 = Person("Alice", 25)
    person2 = Person("Bob", 30)

2. 属性和方法

类的属性和方法是类的核心组成部分,它们用于定义类的状态和行为。

2.1 实例属性

  • 每个实例有自己的变量,称为实例变量(也叫属性)

  • 属性的使用语法

    实例.属性名
  • 属性使用

    class Dog:
        def eat(self, food):
            print(self.color, '的', self.kinds, '正在吃', food)
    ​
    # 创建一个实例:
    dog1 = Dog()
    dog1.kinds = "牧羊犬"  # 添加属性
    dog1.color = "白色"
    dog1.color = "黄色"  # 修改属性
    print(dog1.color, '的', dog1.kinds)
    ​
    dog2 = Dog()
    dog2.kinds = "藏獒"
    dog2.color = "棕色"
    print(dog2.color, '的', dog2.kinds)

  • 实例方法和实例属性(实例变量)结合在一起用

    class Dog:
        def eat(self, food):
            print(self.color, '的', self.kinds, '正在吃', food)
    ​
    # 创建第一个对象
    dog1 = Dog()
    dog1.kinds = '京巴'  # 添加属性kinds
    dog1.color = '白色'  # 添加属性color
    dog1.eat("骨头")
    ​
    dog2 = Dog()
    dog2.kinds = '牧羊犬'
    dog2.color = '灰色'
    dog2.eat('包子')

2.2 实例方法

class 类名(继承列表):
   def 实例方法名(self, 参数1, 参数2, ...):
       "文档字符串"
       语句块

  • 实例方法就是函数,至少有一个指向实例对象的形参self

  • 调用

    实例.实例方法名(调用传参)
    # 或
    类名.实例方法名(实例, 调用传参)
  • 带有实例方法的简单的Dog类

    class Dog:
        """这是一个种小动物的定义
        这种动物是狗(犬)类,用于创建各种各样的小狗
        """
    ​
        def eat(self, food):
            """此方法用来描述小狗吃东西的行为"""
            print("小狗正在吃", food)
    ​
        def sleep(self, hour):
            print("小狗睡了", hour, "小时!")
    ​
        def play(self, obj):
            print("小狗正在玩", obj)
    ​
    dog1 = Dog()
    dog1.eat("骨头")
    dog1.sleep(1)
    dog1.play('球')
    ​
    help(Dog)  # 可以看到Dog类的文档信息

2.3 类属性

  • 类属性是类的属性,此属性属于类,不属于此类的实例

  • 作用:

    • 通常用来存储该类创建的对象的共有属性

  • 类属性说明

    • 类属性,可以通过该类直接访问

    • 类属性,可以通过类的实例直接访问

  • 类属性示例

    class Human:
        total_count = 0  # 创建类属性  self.name = name
        def __init__(self, name):
            self.name = name
    ​
    print(Human.total_count)
    h1 = Human("小张")
    print(h1.total_count)

2.4 类方法

  • 类方法是用于描述类的行为的方法,类方法属于类,不属于该类创建的对象

  • 说明

    • 类方法需要使用classmethod装饰器定义

    • 类方法至少有一个形参用于绑定类,约定为 cls

    • 类和该类的实例都可以调用类方法

    • 类方法不能访问此类创建的对象的实例属性

  • 类方法示例1

    class A:
        v = 0
    ​
        @classmethod
        def set_v(cls, value):
            cls.v = value
    ​
        @classmethod
        def get_v(cls):
            return cls.v
    ​
    ​
    print(A.get_v())
    A.set_v(100)
    print(A.get_v())
    a = A()
    print(a.get_v())

  • 类方法示例2

    class MyClass:
        class_attr = 0  # 类属性
    ​
        def __init__(self, value):
            self.instance_attr = value  # 实例属性
    ​
        @classmethod
        def modify_class_attr(cls, new_value):
            cls.class_attr = new_value
            print(f"类属性已修改为: {cls.class_attr}")
    ​
        @classmethod
        def try_modify_instance_attr(cls):
            try:
                cls.instance_attr = 10  # 事出反常必有妖
            except AttributeError as e:
                print(f"错误: {e}")
    ​
    ​
    # 创建类的实例
    obj = MyClass(5)
    ​
    # 调用类方法修改类属性
    MyClass.modify_class_attr(20)  # 输出: 类属性已修改为: 20
    MyClass.try_modify_instance_attr()  

2.5 静态方法

  • 静态方法定义在类的内部,作用域是类内部

  • 说明

    • 使用@staticmethod装饰器定义

    • 不需要self和cls参数

    • 通过类或类实例调用

    • 可以访问类属性,不能访问实例属性

  • 静态方法示例

    class A:
        class_attr = 42  # 类属性
    ​
        def __init__(self, value):
            self.instance_attr = value  # 实例属性
    ​
        @staticmethod
        def myadd(a, b):
            # 只能访问传递的参数,不能访问实例属性
            return a + b
    ​
    # 创建类实例
    a = A(10)
    ​
    # 调用静态方法
    print(A.myadd(100, 200))  # 输出: 300
    print(a.myadd(300, 400))  # 输出: 700

2.6 构造方法

构造方法__new__()

  • 负责对象的 创建 和内存分配的。

  • 在对象实例化时被调用,负责返回一个新的对象实例。

  • 通常不需要显式地定义 __new__() 方法,Python会调用基类 $object$ 的 __new__() 方法。

class MyClass:
    def __new__(cls, *args, **kwargs):
        print("调用 __new__ 方法,创建对象")
        return super().__new__(cls)
​
    def __init__(self, value):
        print("调用 __init__ 方法,初始化对象")
        self.value = value
​
​
# 创建对象
obj = MyClass(10)

2.7 初始化方法

  • 一旦对象被创建,Python会调用 __init__() 来初始化对象的属性。

  • 语法格式:

    class 类名(继承列表):
        def __init__(self[, 形参列表]):
            语句块
    # [] 代表其中的内容可省略

    接收实参到 __init__方法中

  • 代码示例:

    class MyClass:
        def __new__(cls, *args, **kwargs):
            print("调用 __new__ 方法,创建对象")
            return super().__new__(cls)
    ​
        def __init__(self, value):
            print("调用 __init__ 方法,初始化对象")
            self.value = value
    ​
    # 创建对象
    obj = MyClass(10)

  • 输出:

    调用 __new__ 方法,创建对象
    调用 __init__ 方法,初始化对象

2.8 魔术方法

魔术方法是一种特殊的方法,用双下划线包裹,例如__init____str____add__等。这些方法允许您自定义类的行为,以便与内置Python功能(如+运算符、迭代、字符串表示等)交互。

2.8.1 常用方法
  1. __init__(self, ...): 初始化对象,通常用于设置对象的属性。

  2. __str__(self): 定义对象的字符串表示形式,可通过str(object)print(object)调用。例如,您可以返回一个字符串,描述对象的属性。

  3. __repr__(self): 定义对象的“官方”字符串表示形式,通常用于调试。可通过repr(object)调用。

  4. __len__(self): 定义对象的长度,可通过len(object)调用。通常在自定义容器类中使用。

  5. __getitem__(self, key): 定义对象的索引操作,使对象可被像列表或字典一样索引。例如,object[key]

  6. __setitem__(self, key, value): 定义对象的赋值操作,使对象可像列表或字典一样赋值。例如,object[key] = value

  7. __delitem__(self, key): 定义对象的删除操作,使对象可像列表或字典一样删除元素。例如,del object[key]

  8. __iter__(self): 定义迭代器,使对象可迭代,可用于for循环。

  9. __next__(self): 定义迭代器的下一个元素,通常与__iter__一起使用。

  10. __add__(self, other): 定义对象相加的行为,使对象可以使用+运算符相加。例如,object1 + object2

  11. __sub__(self, other): 定义对象相减的行为,使对象可以使用-运算符相减。

  12. __eq__(self, other): 定义对象相等性的行为,使对象可以使用==运算符比较。

  13. __lt__(self, other): 定义对象小于其他对象的行为,使对象可以使用<运算符比较。

  14. __gt__(self, other): 定义对象大于其他对象的行为,使对象可以使用>运算符比较。

  15. __call__(self, other) 是一个特殊的方法(也称为“魔法方法”),它允许一个对象像函数一样被调用。

2.8.2 案例参考
  1. __str__():定义 print()str() 函数调用时的输出字符串表示。

    • __str__() 方法用来定义当你调用 print()str() 时对象应该返回的字符串。

    • 该方法必须返回一个字符串,通常用于给用户可读的对象描述。

    示例:

    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    ​
        def __str__(self):
            return f"Person: {self.name}, Age: {self.age}"
    ​
    p = Person("Alice", 30)
    print(p)  # 输出: Person: Alice, Age: 30

    作用:

    • 使得你的对象在打印时更加友好和可读。

    • 提供更好的调试输出。

  2. __repr__():定义 repr() 函数的输出,通常用于开发和调试,给出对象的正式字符串表示。

    • __repr__() 方法用于返回一个可以用来重新创建对象的字符串,理想情况下,返回的字符串应当是合法的 Python 表达式。

    • 如果你没有定义 __str__(),那么 __repr__() 会被用来替代 str()print()

    示例:

    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    ​
        def __repr__(self):
            return f"Person('{self.name}', {self.age})"
    ​
    p = Person("Alice", 30)
    print(repr(p))  # 输出: Person('Alice', 30)

    作用:

    • __repr__() 是供开发人员使用的,输出的字符串应该尽可能接近于可以创建该对象的代码。

    • 在调试时使用 repr() 更加方便,它提供了一个清晰的对象表示。

  3. __add__():定义 $+$ 运算符的行为。

    • 该方法允许你重载加法操作符 +,使其在两个对象之间执行加法时能够自定义行为。

    示例:

    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    ​
        def __add__(self, other):
            return Point(self.x + other.x, self.y + other.y)
    ​
        def __repr__(self):
            return f"Point({self.x}, {self.y})"
    ​
    p1 = Point(1, 2)
    p2 = Point(3, 4)
    p3 = p1 + p2
    print(p3)  # 输出: Point(4, 6)

    作用:

    • 允许你自定义 $+$ 运算符的行为。

  4. __len__():定义 len() 函数的行为。

    • __len__() 方法允许你自定义 len() 函数的行为,返回对象的长度。

    示例:

    class MyList:
        def __init__(self, items):
            self.items = items
    ​
        def __len__(self):
            return len(self.items)
    ​
    my_list = MyList([1, 2, 3])
    print(len(my_list))  # 输出: 3

    作用:

    • 使得你的对象能够与 len() 函数兼容,返回对象的长度。

  5. __getitem__():定义对象支持索引([])操作。

    • __getitem__() 方法使得对象能够支持类似列表、元组那样的索引操作。

    示例:

    class MyList:
        def __init__(self, items):
            self.items = items
    ​
        def __getitem__(self, index):
            return self.items[index]
    ​
    my_list = MyList([1, 2, 3, 4])
    print(my_list[2])  # 输出: 3

    作用:

    • 使对象能够支持索引操作,类似于内置的数据结构(如列表、字典)。

  6. __setitem__():定义对象支持索引赋值操作([] =)。

    • __setitem__() 方法允许你定义如何通过索引给对象赋值。

    示例:

    class MyList:
        def __init__(self, items):
            self.items = items
    ​
        def __setitem__(self, index, value):
            self.items[index] = value
    ​
    my_list = MyList([1, 2, 3])
    my_list[1] = 10
    print(my_list.items)  # 输出: [1, 10, 3]

    作用:

    • 使对象能够支持索引赋值操作。

  7. __del__():定义对象的析构方法,通常用于资源清理。

    • 这个方法在对象销毁时调用,通常用于清理资源(如关闭文件、数据库连接等)。

    示例:

    class MyClass:
        def __init__(self, name):
            self.name = name
            print(f"对象 {name} 创建")
    ​
        def __del__(self):
            print(f"对象 {self.name} 被销毁")
    ​
    obj = MyClass("Test")
    del obj  # 输出: 对象 Test 被销毁

    作用:

    • 用于对象销毁时进行资源清理。

  8. __eq__():定义 $==$ 运算符的行为。

    • 该方法允许你自定义对象的相等比较行为,默认情况下,Python 会比较对象的内存地址。

    示例:

    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    ​
        def __eq__(self, other):
            return self.x == other.x and self.y == other.y
    ​
    p1 = Point(1, 2)
    p2 = Point(1, 2)
    print(p1 == p2)  # 输出: True

    作用:

    • 使你能够自定义如何比较对象的相等性。

3. OOP基本特性

OOP的四大基本特性是封装继承多态抽象

3.1 封装

  • 封装是指将对象的属性和方法包装在一起,对外隐藏实现细节,只提供必要的接口给外部访问。

  • 在Python中,通过__init__方法初始化属性,并通过方法来操作这些属性。

  • __开头的属性或方法是私有的,在子类和类外部无法直接使用

  • 可使用“私有”属性和“公有”方法控制外部访问。

class Dog:
    def __init__(self, name, age):
        self.__name = name  # 私有属性
        self.age = age
​
    def get_name(self):
        return self.__name
​
    def set_name(self, name):
        self.__name = name
​
​
​
dog = Dog('泰迪', 20)
print(dog.get_name())
dog.set_name('哈士奇')
print(dog._Dog__name) 

3.2 继承/派生

儿子继承了父亲,父亲派生了儿子~

Python中所有的类最终都继承自内置的object类。

3.2.1 基础概念
  • 继承/派生

    • 继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并能扩展新的能力。

    • 派生类就是从一个已有类中衍生出新类,在新的类上可以添加新的属性和行为

  • 继承/派生的作用

    • 用继承派生机制,可以将一些共有功能加在基类中。实现代码的共享。

    • 在不改变基类的代码的基础上改变原有类的功能

  • 继承/派生名词:

    • 基类(base class)/超类(super class)/父类(father class)

    • 派生类(derived class)/子类(child class)

3.2.2 继承的实现

继承语法:

class DerivedClassName(Base1, Base2, Base3):
   <statement-1>
   .
   .
   .
   <statement-N>

只有一个基类是单继承,有多个基类是多继承。

class Animal:
    def speak(self):
        print("Animal is speaking")
​
class Dog(Animal):
    def speak(self):
        print("Woof!")
​
class Cat(Animal):
    def speak(self):
        print("Meow!")
​
dog = Dog()
dog.speak()  # 输出 "Woof!"
​
cat = Cat()
cat.speak()  # 输出 "Meow!"

3.2.3 覆盖
  • 在子类中实现与基类同名的方法,我们叫覆盖

  • 作用:

    • 实现和父类同名,但功能不同的方法

  • 覆盖示例

    class A:
        def work(self):
            print("A.work 被调用!")
    ​
    class B(A):
        '''B类继承自A类'''
        def work(self):
            print("B.work 被调用!!!")
        pass
    ​
    b = B()
    b.work()  # 请问调用谁?  
    ​
    a = A()
    a.work()  # 请问调用谁?  

写一个类Bicycle类, 有run方法,调用时显示骑行里程km

   class Bicycle:
       def run(self, km):
           print("自行车骑行了", km, "公里")



再写一个类EBicycle,在Bicycle类的基础上,添加电池电量volume属性,有两个方法:
  1. fill_charge(vol) 用来充电, vol 为电量
  2. run(km)方法每骑行10km消耗电量1度,同时显示当前电量,当电量耗尽则,则调用Bicycle的run方法
class EBicyle(Bicycle):
   ...

参考:

class Bicycle:
    def run(self, km):
        print("自行车骑行了", km, "公里")
​
class EBicycle(Bicycle):
    def __init__(self, vol):
        self.cur_volume = vol  # 当前电量
    def run(self, km):
        e_km = min(km, self.cur_volume * 10)  # 求km和 乘余电量能行走的最小里程
        self.cur_volume -= e_km / 10
        if e_km > 0:
            print("电动车骑行了 %d km" % e_km,
                  "剩余电量:", self.cur_volume)
        if km > e_km:
            super().run(km - e_km)
    def fill_charge(self, vol):
        print("电动自行车充电", vol, "度")
        self.cur_volume += vol
​
b = EBicycle(5)  # 新买的电动车内有5度电
b.run(10)  # 电动骑行了10km 还剩 4度电
b.run(100)  # 电动骑行了 40 km ,还剩 0 度电, 用脚登骑行了60km
b.fill_charge(10)  # 电动自行车充电 10 度
b.run(50)  # 骑行了50公里剩余 5度电

3.3 多态

  • 多态是指同一个方法在不同对象上具有不同的行为。

  • 通过多态,可以使得不同类型的对象以相同的接口表现出不同的行为。

  • 多态的实现通常通过继承和方法重写来实现。

class Animal:
    def speak(self):
        print("Animal is speaking")
​
​
class Dog(Animal):
    def speak(self):
        print("Woof!")
​
​
class Cat(Animal):
    def speak(self):
        print("Meow!")
​
​
def animal_speak(animal):
    animal.speak()
​
​
dog = Dog()
cat = Cat()
​
animal_speak(dog)  # 输出 "Woof!"
animal_speak(cat)  # 输出 "Meow!"

3.4 重写

如果父类方法的功能不能满足需求,可以在子类重写父类的方法

3.4.1 对象转字符串重写
  • 对象转字符串函数重写方法

    • str() 函数的重载方法:

      • def __str__(self)

        • 如果没有 __str__(self) 方法,则返回repr(obj)函数结果代替

  • str/repr函数重写示例

    class MyNumber:
        """此类用于定义一个自定义的类,用于演示str/repr函数重写"""
    ​
        def __init__(self, value):
            """构造函数,初始化MyNumber对象"""
            self.data = value
    ​
        def __str__(self):
            """转换为普通字符串"""
            return "%s" % self.data
    ​
    ​
    n1 = MyNumber("一只猫")
    n2 = MyNumber("一只狗")
    print("str(n2) ===>", str(n2))

3.4.2 内建函数重写
  • __abs__ abs(obj) 函数调用

  • __len__ len(obj) 函数调用

  • __reversed__ reversed(obj) 函数调用

  • __round__ round(obj) 函数调用

  • 内建函数 重写示例

    # file : len_overwrite.py
    class MyList:
        def __init__(self, iterable=()):
            self.data = [x for x in iterable]
    ​
        def __repr_(self):
            return "MyList(%s)" % self.data
    ​
        def __len__(self):
            print("__len__(self) 被调用!")
            return len(self.data)
    ​
        def __abs__(self):
            print("__len__(self) 被调用!")
            return MyList((abs(x) for x in self.data))
    ​
    ​
    myl = MyList([1, -2, 3, -4])
    print(myl.data)
    print(len(myl))
    print(abs(myl).data)

3.4.2 运算符重载
  • 运算符重载是指让自定义的类生成的对象(实例)能够使用运算符进行操作

  • 运算符重载的作用

    • 让自定义类的实例像内建对象一样进行运算符操作

    • 让程序简洁易读

    • 对自定义对象将运算符赋予新的运算规则

  • 运算符重载说明:

    • 运算符重载方法的参数已经有固定的含义,不建议改变原有的意义

方法名运算符和表达式说明
__add__(self, rhs)self + rhs加法
__sub__(self, rhs)self - rhs减法
__mul__(self, rhs)self * rhs乘法
__truediv__(self, rhs)self / rhs除法
__floordiv__(self, rhs)self // rhs地板除
__mod__(self, rhs)self % rhs取模(求余)
__pow__(self, rhs)self ** rhs
rhs (right hand side) 右手边
  • 二元运算符重载方法格式:

    def __xxx__(self, other):
       ....

  • 算术运算符重载示例

    class MyNumber:
        """此类用于定义一个自定义的类,用于演示运算符重载"""
    ​
        def __init__(self, value):
            """构造函数,初始化MyNumber对象"""
            self.data = value
    ​
        def __str__(self):
            """转换为表达式字符串"""
            return "MyNumber(%d)" % self.data
    ​
        def __add__(self, rhs):
            """加号运算符重载"""
            print("__add__ is called")
            return MyNumber(self.data + rhs.data)
    ​
        def __sub__(self, rhs):
            """减号运算符重载"""
            print("__sub__ is called")
            return MyNumber(self.data - rhs.data)
    ​
    ​
    n1 = MyNumber(100)
    n2 = MyNumber(200)
    print(n1 + n2)
    n = n1 - n2
    print(n.data)

4. super函数

super() 函数是用于调用父类(超类)的一个方法。

4.1 基本使用

  • 在子类方法中使用 super().add() 调用父类中已被覆盖的方法

  • 使用 super(Child, obj).myMethod() 用于子类对象调用父类已被覆盖的方法

class A:
     def add(self, x):
         y = x+1
         print(y)
class B(A):
    def add(self, x):
        print("子类方法")
        super().add(x)
b = B()
b.add(2)  # 3
class Parent:  # 定义父类
    def myMethod(self):
        print('调用父类方法')
​
​
class Child(Parent):        # 定义子类
    def myMethod(self):
        print('调用子类方法')
​
​
c = Child()                 # 子类实例
c.myMethod()                # 子类调用重写方法
super(Child, c).myMethod()  # 用子类对象调用父类已被覆盖的方法

4.2 super().__init__()

通过 super().__init__() 调用父类构造函数,以确保父类的构造函数被正确调用和初始化。

class Parent:
    def __init__(self):
        print("Parent class constructor called")
        self.parent_attribute = "I am a parent attribute"
​
​
class Child(Parent):
    def __init__(self):
        super().__init__()
        print("Child class constructor called")
        self.child_attribute = "I am a child attribute"
​
​
# 创建一个 Child 类的实例
child_instance = Child()
print(child_instance.parent_attribute)
​
# 输出
# Parent class constructor called
# Child class constructor called

为什么使用 super().__init__()

  • 代码重用:避免在子类中重复父类的初始化代码。

  • 正确初始化:确保父类的初始化逻辑(如设置属性、分配资源等)被执行。

三、迭代器和生成器

1. 迭代器

Iterator

迭代器是一种对象,实现了Python的迭代协议(__iter__()__next__() 方法)。通过迭代器,可以逐个访问容器中的元素。

1.1 特点

  1. 惰性计算:按需生成数据,节省内存。

  2. 实现协议

    • __iter__():返回自身。

    • __next__():返回下一个元素;如果没有更多元素,则抛出 StopIteration 异常。

  3. 可迭代对象(Iterable)与迭代器不同:

    • 可迭代对象实现 __iter__() 方法,返回一个迭代器。

    • 迭代器既实现 __iter__() 又实现 __next__()

1.2 迭代器的创建

  • 迭代器只能往前取值,不会后退

  • 用iter函数可以返回一个可迭代对象的迭代器

# 示例 可迭代对象
L = [1, 3, 5, 7]
it = iter(L)  # 从L对象中获取迭代器
next(it)  # 1  从迭代器中提取一个数据
next(it)  # 3
print(next(it))   # 5
next(it)  # 7
# 示例2 生成器函数
It = iter(range(1, 10, 3))
next(It)  # 1
print(next(It))  # 4
next(It)  # 7

1.3 自定义迭代器

# 自定义迭代器类
class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0
​
    def __iter__(self):
        return self  # 迭代器返回自身
​
    def __next__(self):
        if self.index < len(self.data):
            result = self.data[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration  # 数据迭代结束
​
# 使用自定义迭代器
my_iter = MyIterator([1, 2, 3])
for item in my_iter:
    print(item)

2. 生成器

Generator

生成器是一种特殊的迭代器,通过函数定义,用 yield 语句生成值。生成器可以自动实现迭代协议,无需手动实现 __iter__()__next__()

2.1 特点

  1. 简洁:比手动实现迭代器更易写。

  2. 惰性计算:生成器在每次调用 next() 时生成一个值,而不是一次性生成所有值。

  3. yield:暂停函数执行并返回值,保留函数的状态,以便下一次继续执行。

示例代码:

# 生成器函数
def my_generator():
    print("Start")
    yield 1
    print("Continue")
    yield 2
    print("End")
    yield 3
​
# 使用生成器
gen = my_generator()
for val in gen:
    print(val)
输出:

Start
1
Continue
2
End
3

案例参考:

def Descendorder(n):
    while n > 0:
        yield n
        n -= 1
​
# 创建生成器对象
generator = Descendorder(5)
​
# 通过迭代生成器获取值
print(next(generator))  #5
print(next(generator))  #4
​
# 使用 for 循环迭代生成器
for i in generator:
    print('for循环:', i)  #3  2  1
​

2.2 生成器表达式

生成器可以用表达式形式定义,类似列表推导式,但使用小括号 ()

  • 语法

(表达式 for 变量 in 可迭代对象 [if 真值表达式])

[] 内容代表可以省略

# 生成器表达式
gen_expr = (x**2 for x in range(5))
print(next(gen_expr))  # 0
print(next(gen_expr))  # 1

2.3 课堂练习

代码实现斐波那契数列(最少十个数)

参考代码:

def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield b
        a, b = b, a + b
​
​
fib_seq = fibonacci(10)
​
for i in fib_seq:
    print(i, end=" ")
​
# 运行结果
# 1 1 2 3 5 8 13 21 34 55

2.4 应用场景

  • 数据流处理:处理大文件或流式数据,避免内存耗尽。

  • 无限序列生成:如斐波那契数列、素数序列。

  • 管道化数据处理:与 itertools 模块结合使用。

参考代码:

def read_large_file(file_path):
    with open(file_path, 'r') as file:
        for line in file:
            yield line.strip()
​
for line in read_large_file('large_file.txt'):
    print(line)

3. 区别对比

特性迭代器生成器
实现通过类实现,手动定义方法使用函数和 yield 定义,自动实现迭代协议
代码简洁性代码较复杂代码简单
状态管理手动管理状态自动保存函数的运行状态
惰性计算支持支持
示例应用自定义复杂的迭代逻辑简单的数据流生成

四、异常处理

用作信号通知,通知上层调用者有错误产生需要处理

1. try 语句

  • 语法

try:
   可能发生异常的语句块
except 错误类型1 [as 变量名1]:
   异常处理语句块1
except 错误类型2 [as 变量名2]:
   异常处理语句块2
...
except 错误类型n [as 变量名n]:
   异常处理语句块n
except:
   异常处理语句块other
else:
   未发生异常的语句
finally:
   最终的处理语句

  • 作用

    尝试捕获异常,得到异常通知,将程序由异常状态变为正常状态

  • 说明

    • except 子句可以有 1个或多个

  • except: 不给错误类型,可以匹配全部的错误类型

  • else 子句在没有错误发生时执行,当处于异常时不执行

  • finally 子句里的语句,无论何时都执行

  • 示例

try:
    x = int(input("请输入一个整数:"))
    print('x=', x)
except ValueError:
    print('您的输入不能转成整数')
​
print("程序结束")

2. raise 语句

  • 问题

    # 写一个函数, get_score 函数,读取用户输入的整数成绩, 
    # 成绩的正常值是0~100 之间, 要求, 如果不在0~100 之间
    # 报 ValueError类型的错误
    def get_score():
        x = int(input('请输入成绩:'))
        if 0 <= x <= 100:
            return x
        raise ValueError
        
  • 语法

    raise 异常类型
    或
    raise 异常对象
  • 作用

    • 抛出一个错误,让程序进入异常状态

    • 发送错误通知给调用者

  • 示例:

# 写一个函数, get_score 函数,读取用户输入的整数成绩,
# 成绩的正常值是0~100 之间, 
# 如果不在0~100之间报ValueError类型的错误
def get_score():
    x = int(input('请输入成绩:'))
    if 0 <= x <= 100:
        return x
    # raise ValueError
    raise ValueError('用户输入的成绩不在 0~100 之间')
​
​
try:
    score = get_score()
    print(score)
except ValueError as err:
    print("成绩输入有误 err=", err)

err的作用就是接收raise ValueError('用户输入的成绩不在 0~100 之间')给出的提示信息

  • 异常类型的可选种类

    写一个猜拳游戏: 石头,剪刀,布, 让电脑随机生成一个, 你的输入如下:   (0)  石头     (1)  剪刀     (2)  布     (q)  退出    请选择: 0   电脑出的是 布 ,你输了 循环输入,知道输入q 为止

  • 参考答案1

    import random
    import time
    ​
    signal = ['石头', '剪刀', '布']
    ​
    def show_menu():
        print(" 0)  石头 ")
        print(" 1)  剪刀 ")
        print(" 2)  布  ")
        print(" q)  退出 ")
    ​
    def begin_compare(computer, your):
        comp_s = signal[computer]  # 电脑的字符串
        your_s = signal[your]
        print('电脑出的是', comp_s, '你出的是', your_s)
        if comp_s == your_s:
            print('平局!')
        elif comp_s == '石头':
            if your_s == '剪刀':
                print('你输了!')
            elif your_s == '布':
                print('你赢了!')
        elif comp_s == '剪刀':
            if your_s == '布':
                print('你输了!')
            elif your_s == '石头':
                print('你赢了!')
        elif comp_s == '布':
            if your_s == '石头':
                print('你输了!')
            elif your_s == '剪刀':
                print('你赢了!')
        # time.sleep(5)
        input('请输入回车键,继续下一次猜拳:')
    ​
    def run():
        '''开始猜拳游戏'''
        while True:
            show_menu()
            s = input('请选择:')
            if s == 'q':
                break
            your = int(s)  # 你的选项
            computer = random.randint(0, 2)
            begin_compare(computer, your)
    ​
    ​
    if __name__ == '__main__':
        run()

    参考答案2

    import random
    import time
    ​
    signal = ['石头', '剪刀', '布']
    result = ['平局!', '你赢了!', '你输了!']
            #   0         1          -1
    ​
    # 定义一个二维列表, 行代表 电脑的选择, 列代表我的选择
    map = [
      # 用户: 0  1  2
        [ 0, -1,  1],  # 电脑出的是0---> 石头
        [ 1,  0, -1],  # 电脑出的是1---> 剪刀
        [-1,  1,  0],  # 电脑出的是2---> 布
    ]
    ​
    def show_menu():
        print(" 0)  石头 ")
        print(" 1)  剪刀 ")
        print(" 2)  布  ")
        print(" q)  退出 ")
    ​
    def begin_compare(computer, your):
        comp_s = signal[computer]  # 电脑的字符串
        your_s = signal[your]
        print('电脑出的是', comp_s, '你出的是', your_s)
        result_index = map[computer][your]
        r = result[result_index]
        print(r)
        # time.sleep(5)
        input('请输入回车键,继续下一次猜拳:')
    ​
    def run():
        '''开始猜拳游戏'''
        while True:
            show_menu()
            s = input('请选择:')
            if s == 'q':
                break
            your = int(s)  # 你的选项
            computer = random.randint(0, 2)
            begin_compare(computer, your)
    ​
    ​
    if __name__ == '__main__':
        run()

  • 课后练习2

    写一个猜数字游戏 让电脑随机生成一个 0 ~ 100 的整数让用来猜
    如果 您输入的数大于电脑生产的数,提示:“您猜大了”, 继续猜
    如果 您输入的数小于电脑生产的数,提示:“您猜小了”, 继续猜
    当 您输入的数等于电脑生产的数,提示:"恭喜您猜对了" 打印猜的次数后退出程序

    参考答案

    import random
    ​
    def run():
        # 1. 让电脑生成一个整数,用 x 变量绑定
        x = random.randint(0, 100)
        count = 0  # 记次数
        while True:
            y = int(input('请输入: '))
            count += 1
            if y > x:
                print('您猜大了!')
            elif y < x:
                print('您猜小了!')
            else:
                print('恭喜您猜对了!')
                break
        print('您共猜了', count, '次')
    ​
    if __name__ == '__main__':
        run()

3. 错误类型

错误类型说明
ZeroDivisionError除(或取模)零 (所有数据类型)
ValueError传入无效的参数
AssertionError断言语句失败
StopIteration迭代器没有更多的值
IndexError序列中没有此索引(index)
IndentationError缩进错误
OSError输入/输出操作失败
ImportError导入模块/对象失败
NameError未声明/初始化对象 (没有属性)
AttributeError对象没有这个属性
<!-- 以下不常用 -->
GeneratorExit生成器(generator)发生异常来通知退出
TypeError对类型无效的操作
KeyboardInterrupt用户中断执行(通常是输入^C)
OverflowError数值运算超出最大限制
FloatingPointError浮点计算错误
BaseException所有异常的基类
SystemExit解释器请求退出
Exception常规错误的基类
StandardError所有的内建标准异常的基类
ArithmeticError所有数值计算错误的基类
EOFError没有内建输入,到达EOF 标记
EnvironmentError操作系统错误的基类
WindowsError系统调用失败
LookupError无效数据查询的基类
KeyError映射中没有这个键
MemoryError内存溢出错误(对于Python 解释器不是致命的)
UnboundLocalError访问未初始化的本地变量
ReferenceError弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError一般的运行时错误
NotImplementedError尚未实现的方法
SyntaxError Python语法错误
TabErrorTab 和空格混用
SystemError一般的解释器系统错误
UnicodeErrorUnicode 相关的错误
UnicodeDecodeErrorUnicode 解码时的错误
UnicodeEncodeErrorUnicode 编码时错误
UnicodeTranslateErrorUnicode 转换时错误
以下为警告类型
Warning警告的基类
DeprecationWarning关于被弃用的特征的警告
FutureWarning关于构造将来语义会有改变的警告
OverflowWarning旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning关于特性将会被废弃的警告
RuntimeWarning可疑的运行时行为(runtime behavior)的警告
SyntaxWarning可疑的语法的警告
UserWarning用户代码生成的警告

本文标签: 学习笔记 python