Python3 高级编程

作者:user 发布日期: 浏览量:102

一、一切皆对象

1、python中一切皆对象

  • python的面向对象相比于其它语言更彻底
  • 函数和类也是对象

可以赋值给一个变量
可以添加到集合对象中
可以作为参数传递给函数
可以当作函数的返回值

2、type、object 和 class 的关系

  • type 有两种用法,一种是用来生成类,另一种是用来返回一个对象的类型
  • 类本身也是一个对象
  • type是用来生成类对象的,平常使用的实例就是平常定义的类或者内置的类生成的实例
a = 1

type(1)  # class 'int'
type(int)  # class 'type'
  • object 是所有类都要继承的最顶层的基类
  • type 也是一个类,同时也是一个对象
class Student():
    pass

class MyStudent(Student):
    pass

Student.__bases__  # (<class 'object'>,)
MyStudent.__bases__  # (<class '__main__.Student'>,)

type.__bases__  # (<class 'object'>,)

object.__bases__  # ()

3、python中的内置类型

对象的三个特征:

  • 身份:对象在内存之中的地址,id(对象)都有一个id值
  • 类型:int、str、float、bool等

3-1、None 类型

  • 表示空值或者缺失值
  • Python解释器在启动的时候会用 None 类型生成一个 None 对象,全局只有一个
a = None
b = None

print(a == b)  # True

3-2、数值类型

  • 整数型 int:表示整数值,支持任意精度。示例:12、-100
  • 浮点型 float:表示带有小数点的数值,遵循 IEEE 754 标准。示例:3.14、-0.001
  • 复数型 complex:表示复数,包含实部和虚部。示例:3 + 4j、-1j
  • 布尔型 bool:表示逻辑值 True 或 False,是 int 的子类,True 等价于 1,False 等价于 0

3-3、迭代类型

  • 可以用 for 循环进行遍历

3-4、序列类型

  • 列表 list:可变的有序集合,元素可以是任意类型。示例:[1, 2, 3]、[‘a’, ‘b’, ‘c’]
  • 元组 tuple:不可变的有序集合,元素可以是任意类型。示例:(1, 2, 3)、(‘a’, ‘b’, ‘c’)
  • 字符串 str:不可变的字符序列。示例:’hello’、”“”多行字符串”“
  • 范围 range:表示不可变的数字序列,常用于循环。示例:range(5) 表示 [0, 1, 2, 3, 4]

3-5、映射类型

  • 字典 dict:可变的无须集合,键必须是不可变类型(如字符串、数字、元组)。示例:{‘name’: ‘zhan’, ‘age’: 12}

3-6、集合类型

  • 集合 set:可变的无序集合,元素唯一且不可重复。示例:{1, 2, 3}、set([1, 2, 3])
  • 冻结集合 frozenset:不可变的无序集合,元素唯一且不可重复。示例:frozenset([1, 2, 3])

3-7、二进制数据类型

  • 字节 bytes:不可变的字节序列。示例:b’hello’、bytes([65, 66, 67])
  • 字节数组 bytearray:可变的字节序列。示例:bytearray(b’hello’)

3-8、上下文管理类型(with)

3-9、其他

  • 模块 module:表示一个模块对象。示例:import math; type(math)
  • 函数 function:表示用户定义的函数。示例:def func(): pass; type(func)
  • 类 type:表示类对象本身。示例:class MyClass: pass; type(MyClass)
  • 实例方法 method:表示绑定到类实例的方法。示例:class C: def foo(self): pass; c = C(); type(c.foo)

二、魔法函数

1、什么是魔法函数

  • 在 Python 中,一些以双下划线(__)开头和结尾,允许开发者定义类的行为,使其与Python的内置操作符或功能进行交互的特殊方法,被称为魔法函数(Magic
    Methods 或 Dunder Methods)。通过这些魔法函数,可以自定义类的行为。
  • Python 的魔法函数是 Python 提供好的,我们不可以随便定义我们的魔法函数。

2、魔法函数一览

2-1、对象的基本行为

  • new(cls, args, *kwargs):用于创建实例对象。它是静态方法,返回一个新的实例。
class MyClass:
    def __new__(cls, *args, **kwargs):
        print("Creating instance")
        return super().__new__()
  • init(self, args, *kwargs):用于初始化实例对象。
class MyClass:
    def __init__(self, value):
        self.value = value
  • del(self):在对象被销毁时调用(垃圾回收时)。
class MyClass:
    def __del__(self):
        print("Object deleted")
  • str(self):返回对象的“非正式”字符串表示,通常用于用户友好的输出。
class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __str__(self):
        return ', '.join(self.employee)


company = Company(['zhangsan', 'lisi', 'wangwu'])
print(company)  # <__main__.Company object at 0x0000026421401F10>  # 使用__str__: zhangsan, lisi, wangwu
  • repr(self):返回对象的“官方”字符串表示,通常用于调式。
class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __str__(self):
        return ', '.join(self.employee)

    def __repr__(self):
        return ', '.join(self.employee)


company = Company(['zhangsan', 'lisi', 'wangwu'])
# print(company)  # <__main__.Company object at 0x0000026421401F10>  # 使用__str__: zhangsan, lisi, wangwu
company  #  在开发环境下会使用__repr__: zhangsan, lisi, wangwu

2-2、比较操作

  • eq(self, other):定义 == 操作符的行为。
class Point:
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
  • ne(self, other):定义 != 操作符的行为。
class Point:
    def __ne__(self, other):
        return not self.__eq__(other)
  • lt(self, other) , le(self, other) , gt(self, other) , ge(self, other):分别定义 <, <=, >, >= 操作符的行为。

2-3、数学运算

  • add(self, other):定义 + 操作符的行为。
class Point:
    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)
  • sub(self, other) , mul(self, other) , truediv(self, other) , floordiv(self, other) , mod(self,
    other) , pow(self, other):分别定义 -, *, /, //, %, ** 操作符的行为。
  • neg(self) , pos(self) , abs(self):分别定义一元操作符 -, +, abs() 的行为。

2-4、属性访问

  • getattr(self, name):当访问不存在的属性时调用。
class MyClass:
    def __getattr__(self, name):
        return f"{name} does not exist"
  • setattr(self, name, value):当设置属性值时调用。
class MyClass:
    def __setattr__(self, name, value):
        print(f"Setting {name} to {value}")
        super().__setattr__(name, value)
  • delattr(self, name):当删除属性时调用。
class MyClass:
    def __delattr__(self, name):
        print(f"Deleting {name}")
        super().__delattr__(name)

2-5、容器行为

  • len(self):定义 len() 函数的行为。
class MyList:
    def __len__(self):
        return 10
  • getitem(self, key):定义通过索引或键访问元素的行为。
class MyList:
    def __getitem__(self, index):
        return index * 2
  • setitem(self, key, value):定义通过索引或键设置元素的行为。
class MyList:
    def __setitem__(self, index, value):
        print(f"Setting {index} to {value}")
  • delitem(self, key):定义通过索引或键删除元素的行为。
class MyList:
    def __delitem__(self, index):
        print(f"Deleting {index}")
  • contains(self, item):定义 in 操作符的行为。
class MyList:
    def __contains__(self, item):
        return item % 2 == 0

2-6、可调用对象

  • call(self, args, *kwargs):使对象像函数一样可调用。
class Adder:
    def __call__(self, x, y):
        return x + y

2-7、上下文管理

  • enter(self):定义进入上下文时的行为。
class MyContext:
    def __enter__(self):
        print("Entering context")
        return self
  • exit(self, exc_type, exc_value, traceback):定义退出上下文时的行为。
class MyContext:
    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting context")

2-8、迭代器协议

  • iter(self):返回一个迭代器对象。
class MyRange:
    def __init__(self, start, end):
        self.start = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.start >= self.end:
            raise StopIteration
        current = self.start
        self.start += 1
        return current

2-9、其他常用魔法函数

  • hash(self):定义对象的哈希值,用于集合和字典键。
class Point:
    def __hash__(self):
        return hash((self.x, self.y))
  • bool(self):定义对象在布尔上下文中的行为。
class MyClass:
    def __bool__(self):
        return False
  • dir(self):定义 dir() 函数返回的属性列表。
class MyClass:
    def __dir__(self):
        return ["x", "y"]

3、魔法函数的重要性——len函数举例

  • 在使用 len() 时,默认会隐式调用对象的 len(self) 方法
  • 使用 Python 的内置类型如 list、dict、set等类型进行长度计算时,len() 不会和自定义类型一样调用 len 去遍历获取长度,而是使用
    cpython 里的某个字段或属性直接获取长度值,这样效率会更高。所以尽量使用python自带的方法而不是自己使用 len 写。其他如
    list()、dict()也是类似的。

三、深入类和对象

1、鸭子类型和多态

  • 鸭子类型(Duck Typing)是Python中的一种编程风格,它关注对象的行为而不是对象的类型。这个概念源自一句话:“如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子。”
    在Python中,这意味着一个对象的适用性不是由它的类或类型决定的,而是由它是否具有特定的方法或属性决定的
  • 多态(Polymorphism)是面向对象编程(OOP)的核心概念之一,指的是同一个接口或操作可以作用于不同的对象,并根据对象的类型表现出不同的行为
    。Python
    作为一门动态类型语言,天然支持多态,主要通过鸭子类型(Duck Typing)和继承机制来实现。
  • 鸭子类型的应用:魔法函数、多态
# 多态实现方式1:Python 的多态主要依赖于鸭子类型。只要对象具有所需的方法或属性,就可以在特定的上下文中使用,而不需要显式地继承某个类或实现某个接口。
class Dog:
    def speak(self):
        return "Woof!"

class Cat:
    def speak(self):
        return "Meow!"

def animal_sound(animal):
    print(animal.speak())

dog = Dog()
cat = Cat()

animal_sound(dog)  # 输出: Woof!
animal_sound(cat)  # 输出: Meow!

# 多态实现方式2:Python 也支持通过继承实现多态。子类可以重写父类的方法,从而表现出不同的行为。
class Animal:
    def speak(self):
        raise NotImplementedError("Subclasses must implement this method")

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

def animal_sound(animal):
    print(animal.speak())

dog = Dog()
cat = Cat()

animal_sound(dog)  # 输出: Woof!
animal_sound(cat)  # 输出: Meow!

2、抽象基类(Abstract Base Class,简称 ABC)

  • 抽象基类是 Python 中用于定义接口和强制子类实现特定方法的一种机制。它的核心作用是规范类的行为,确保子类遵循特定的设计约定
  • 抽象基类:在这个基础的类中,设定好一些方法,然后所有继承该基类的类必须实现抽象基类里的方法;抽象基类是无法进行实例化的。
from abc import ABC, abstractmethod

class MyClass(ABC):
    @abstractmethod
    def do_something(self):
        pass
  • 抽象基类可以通过 isinstance 或 issubclass 检查对象是否符合某种接口规范。例如,Python 的 collections.abc 模块中的
    Iterable、Sequence 等抽象基类,可以检查一个对象是否实现了迭代协议。
from collections.abc import Iterable

class MyList:
    def __iter__(self):
        return iter([1, 2, 3])

print(isinstance(MyList(), Iterable))  # 输出: True
  • 在 Python 中,由于支持鸭子类型,通常不需要显式继承抽象基类。但通过注册机制(register()),可以让不直接继承的类也被视为抽象基类的子类。
from abc import ABC

class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog:  # 未继承 Animal,但注册为 Animal 的子类
    def speak(self):
        return "Woof!"

Animal.register(Dog)  # 注册 Dog 为 Animal 的“虚拟子类”

print(issubclass(Dog, Animal))  # 输出: True
dog = Dog()
print(isinstance(dog, Animal))  # 输出: True
from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

def animal_sound(animal):
    print(animal.speak())

dog = Dog()
cat = Cat()

animal_sound(dog)  # 输出: Woof!
animal_sound(cat)  # 输出: Meow!

3、使用 isintance 而不是 type

is 与 == 的区别:

  • is:身份比较(Identity Check),判断两个对象是否是同一个对象(即内存地址是否相同)。直接比较对象的引用(指针)是否指向同一个内存地址。
  • ==:值比较(Equality Check),判断两个对象的值是否相等。调用对象的 eq() 方法,具体比较逻辑由类定义。
a = [1, 2, 3]
b = [1, 2, 3]

print(a == b)  # True(值相等)
print(a is b)  # False(内存地址不同)

type() 和 isinstance() 的区别:

  • type():获取对象的具体类型,不支持继承关系检查。返回对象的具体类型(类)。返回一个类型对象(如 )。
  • isinstance():检查对象是否属于某个类型或其子类,支持继承和多类型检查。判断对象是否是某个类型或其子类的实例。返回 True
    或 False。
class Animal:
    pass

class Dog(Animal):
    pass

dog = Dog()

# 使用 type()
print(type(dog) == Dog)  # True
print(type(dog) == Animal)  # False(无法检查继承关系)

# 使用 isinstance()
print(isinstance(dog, Dog))  # True
print(isinstance(dog, Animal))  # True(支持继承关系检查)

4、类变量和对象(实例)变量

类变量(Class Variable):

  • 定义:类变量是定义在类中,但在方法之外的变量。
  • 作用域:属于类本身,所有实例共享。
  • 生命周期:从类定义开始,到程序结束或类被销毁。
  • 访问方式:通过类名访问:ClassName.class_var。通过实例访问:instance.class_var(如果实例没有同名属性)。
class MyClass:
    class_var = 10  # 类变量

# 通过类名访问
print(MyClass.class_var)  # 输出: 10

# 通过实例访问
obj1 = MyClass()
obj2 = MyClass()
print(obj1.class_var)  # 输出: 10
print(obj2.class_var)  # 输出: 10

# 修改类变量(通过类名)
MyClass.class_var = 20
print(obj1.class_var)  # 输出: 20
print(obj2.class_var)  # 输出: 20

# 修改类变量(通过实例)
obj1.class_var = 30  # 实际上创建了一个实例变量
print(obj1.class_var)  # 输出: 30(实例变量)
print(obj2.class_var)  # 输出: 20(类变量未变)

对象变量(实例变量,Instance Variable):

  • 定义:对象变量是定义在方法中(通常是 init 方法),并以 self 为前缀的变量。
  • 作用域:属于实例对象,每个实例独立拥有。
  • 生命周期:从对象创建开始,到对象被销毁。
  • 访问方式:只能通过实例访问:instance.instance_var
class MyClass:
    def __init__(self, value):
        self.instance_var = value  # 实例变量

obj1 = MyClass(10)
obj2 = MyClass(20)

print(obj1.instance_var)  # 输出: 10
print(obj2.instance_var)  # 输出: 20

# 修改实例变量
obj1.instance_var = 30
print(obj1.instance_var)  # 输出: 30
print(obj2.instance_var)  # 输出: 20(不受影响)

5、类属性和实例属性以及查找顺序——MRO查找

  • 类变量和类属性、实例变量和实例属性的区别:在 Python 中,类变量和类属性、实例变量和实例属性本质上是相同的概念,只是术语的使用场景和侧重点略有不同。
    变量强调的是存储方式和作用域,属性强调访问方式。
  • MRO(Method Resolution Order,方法解析顺序) 是用于确定类继承关系中方法调用顺序的规则。
    MRO 主要用于解决多重继承中的方法冲突问题,确保方法调用的一致性和可预测性。

MRO的规则:

  • Python 使用 C3线性化算法 来计算 MRO。C3算法的核心规则如下:
  • 子类优先于父类:子类的方法优先于父类的方法。
  • 从左到右:在多继承中,按照类定义时的顺序从左到右查找。
  • 单调性:如果一个类在 MRO 中排在另一个类的前面,那么在所有子类的 MRO 中也应保持这种顺序。

查看 MRO:

  • 可以通过类的 mro 属性或 mro() 方法查看 MRO 顺序。
class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass

print(D.__mro__)
# 输出: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

6、静态方法、类方法以及对象方法以及参数

对象(实例)方法(Instance Method):

  • 定义:实例方法是类的普通方法,第一个参数必须是 self,表示类的实例对象
  • 调用方式:通过类的实例调用。
class MyClass:
    def instance_method(self):
        print("This is an instance method")
        print(f"Called by: {self}")

obj = MyClass()
obj.instance_method()  # 通过实例调用

类方法(Class Method):

  • 定义:类方法使用 @classmethod 装饰器定义,第一个参数必须是 cls,表示类本身。
  • 调用方式:通过类或实例调用。
class MyClass:
    class_attr = 10

    @classmethod
    def class_method(cls):
        print("This is a class method")
        print(f"Called by: {cls}")
        print(f"Class attribute: {cls.class_attr}")

MyClass.class_method()  # 通过类调用
obj = MyClass()
obj.class_method()  # 通过实例调用

静态方法(Static Method):

  • 定义:静态方法使用 @staticmethod 装饰器定义,不需要 self 或 cls 参数。
  • 调用方式:通过类或实例调用。
class MyClass:
    @staticmethod
    def static_method():
        print("This is a static method")

MyClass.static_method()  # 通过类调用
obj = MyClass()
obj.static_method()  # 通过实例调用

7、数据封装和私有属性

  • 数据封装 是面向对象编程(OOP)的核心概念之一,它通过限制对对象内部数据的直接访问来保护数据的完整性和安全性。Python通过
    私有属性访问控制 机制来实现数据封装。

私有属性的定义:

  • 在 Python 中,私有属性是通过在属性名前加双下划线 __ 来实现的。这种命名方式会触发 Python 的 **名称改写(Name mangling)
    **
    机制,使得属性在外部无法直接访问。
  • 名称改写机制:Python 通过名称改写机制将私有属性的名称改为 _类名__属性名,从而避免外部直接访问。
    虽然可以通过改写后的名称访问私有属性,但强烈不建议这样做,因为这违背了封装的原则。
class MyClass:
    def __init__(self, value):
        self.__private_attr = value  # 私有属性

    def get_private_attr(self):
        return self.__private_attr  # 通过方法访问私有属性

    def set_private_attr(self, value):
        self.__private_attr = value  # 通过方法修改私有属性

obj = MyClass(10)
print(obj.get_private_attr())  # 输出: 10

obj.set_private_attr(20)
print(obj.get_private_attr())  # 输出: 20

# 直接访问私有属性会报错
# print(obj.__private_attr)  # AttributeError: 'MyClass' object has no attribute '__private_attr'

# 通过访问改写的名称进行访问
print(obj._MyClass__private_attr)  # 输出: 20

受保护属性:

  • 在 Python 中,单下划线 _ 开头的属性被视为 受保护属性,表示该属性仅供内部使用或子类访问,但Python并不会阻止外部访问。
class MyClass:
    def __init__(self, value):
        self._protected_attr = value  # 受保护属性

obj = MyClass(10)
print(obj._protected_attr)  # 输出: 10(可以访问,但不建议)

数据封装的实现方式:

  • 方式1:使用私有属性和公共方法。通过私有属性隐藏数据,并通过公共方法(getter 和 setter)提供访问和修改的接口。
class Person:
    def __init__(self, name, age):
        self.__name = name  # 私有属性
        self.__age = age    # 私有属性

    def get_name(self):
        return self.__name  # getter 方法

    def set_name(self, name):
        self.__name = name  # setter 方法

    def get_age(self):
        return self.__age  # getter 方法

    def set_age(self, age):
        if age > 0:
            self.__age = age  # setter 方法
        else:
            print("Age must be positive")

person = Person("Alice", 30)
print(person.get_name())  # 输出: Alice
person.set_age(-10)       # 输出: Age must be positive
  • 方式 2:使用 @property 装饰器。@property 装饰器可以将方法转换为属性,从而提供更简洁的访问方式。
class Person:
    def __init__(self, name, age):
        self.__name = name  # 私有属性
        self.__age = age    # 私有属性

    @property
    def name(self):
        return self.__name  # getter

    @name.setter
    def name(self, name):
        self.__name = name  # setter

    @property
    def age(self):
        return self.__age  # getter

    @age.setter
    def age(self, age):
        if age > 0:
            self.__age = age  # setter
        else:
            print("Age must be positive")

person = Person("Alice", 30)
print(person.name)  # 输出: Alice
person.age = -10    # 输出: Age must be positive

8、python对象的自省机制

  • 在 Python 中,自省(Introspection) 是指程序在运行时能够查看、检查对象的类型、属性、方法等信息的能力。Python
    提供了丰富的内置函数和工具来支持自省,使得开发者可以动态地获取对象的信息并做出相应的处理。

自省的作用:

  • 动态获取对象的类型、属性和方法。
  • 检查对象是否具有某些特性或行为。
  • 调试和开发时快速了解对象的结构。

常用的自省工具:

  • type():返回对象的类型。
num = 42
print(type(num))  # 输出: <class 'int'>
  • isinstance():判断对象是否是某个类或其子类的实例。
class Animal:
    pass

class Dog(Animal):
    pass

dog = Dog()
print(isinstance(dog, Animal))  # 输出: True
  • id():返回对象的唯一标识符(内存地址)
a = [1, 2, 3]
print(id(a))  # 输出: 内存地址(如 140123456789)
  • dir():返回对象的所有属性和方法的名称列表。
s = "hello"
print(dir(s))  # ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', .....]
  • dict:返回对象的属性和值的字典。
class MyClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y

obj = MyClass(10, 20)
print(obj.__dict__)  # 输出: {'x': 10, 'y': 20}
  • hasattr():判断对象是否具有指定的属性或方法。
class MyClass:
    attr = 10

obj = MyClass()
print(hasattr(obj, 'attr'))  # 输出: True
print(hasattr(obj, 'method'))  # 输出: False
  • getattr():获取对象的指定属性值,如果属性不存在,可以返回默认值或抛出异常。
class MyClass:
    attr = 10

obj = MyClass()
print(getattr(obj, 'attr'))  # 输出: 10
print(getattr(obj, 'unknown', 'default'))  # 输出: default
  • setattr():设置对象的指定属性值。
class MyClass:
    pass

obj = MyClass()
setattr(obj, 'attr', 20)
print(obj.attr)  # 输出: 20

9、super函数

  • 在 Python 中,super() 是一个内置函数,用于调用父类(超类)的方法。它在继承关系中非常有用,尤其是在多重继承的情况下,可以确保方法调用的顺序符合
    MRO(Method Resolution Order,方法解析顺序)。
  • 单继承: 在单继承中,super() 用于调用父类的方法。
class Parent:
    def __init__(self, name):
        self.name = name
        print("Parent initialized")

class Child(Parent):
    def __init__(self, name, age):
        super().__init__(name)  # 调用父类的 __init__ 方法
        self.age = age
        print("Child initialized")

child = Child("Alice", 10)
# 输出:
# Parent initialized
# Child initialized
  • 多重继承:在多重继承中,super() 会按照 MRO 顺序调用父类的方法。
"""
MRO 顺序:D -> B -> C -> A
super() 按照 MRO 顺序依次调用父类的 __init__ 方法
"""
class A:
    def __init__(self):
        print("A initialized")

class B(A):
    def __init__(self):
        super().__init__()
        print("B initialized")

class C(A):
    def __init__(self):
        super().__init__()
        print("C initialized")

class D(B, C):
    def __init__(self):
        super().__init__()
        print("D initialized")

d = D()
# 输出:
# A initialized
# C initialized
# B initialized
# D initialized
  • super() 的参数:super() 可以接受两个参数: type:当前类;object_or_type:当前实例对象
super([type[, object_or_type]])
class Parent:
    def greet(self):
        print("Hello from Parent")

class Child(Parent):
    def greet(self):
        super(Child, self).greet()  # 显式调用父类方法
        print("Hello from Child")

child = Child()
child.greet()
# 输出:
# Hello from Parent
# Hello from Child

10、python中的 with 语句

  • 在 Python 中,with 语句用于简化资源管理,确保资源在使用完毕后被正确释放(如文件、网络连接、锁等)。它通过上下文管理器(Context
    Manager)实现,能够自动处理资源的初始化和清理工作。
  • 一般情况下,我们在执行某些功能后无论成功还是失败都需要执行某个逻辑,可以使用 try、except、finally 中的 finally
    实现,确保最后该逻辑一定会执行。
try:
    f_read = open("example.txt")
    print("start ...")
    raise KeyError
    f_read.close()
except KeyError as e:
    print("key error...")
    f_read.close()
else:
    print("other error...")
finally:
    print("finally...")
    f_read.close()

with 语句的作用:

  • 简化资源管理代码。
  • 确保资源在使用完毕后被正确释放。
  • 避免资源泄漏和异常处理问题。

with 语句的基本用法:

  • with 语句的基本语法如下
with 上下文管理器 as 变量:
    代码块
with open("example.txt", "r") as file:
    content = file.read()
    print(content)

上下文管理器:

  • 上下文管理器是一个实现了 enter() 和 exit() 方法的对象。
  • enter() 方法:在进入 with 代码块时调用,返回的值会赋值给 as 后的变量。
  • exit() 方法:在退出 with 代码块时调用,用于释放资源。 参数 exc_type、exc_value、traceback
    用于处理异常。如果代码块中没有异常,这三个参数为 None。
# 自定义上下文管理器

class MyContextManager:
    def __enter__(self):
        print("Entering the context")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting the context")
        if exc_type is not None:
            print(f"Exception occurred: {exc_value}")
        return True  # 抑制异常

with MyContextManager() as cm:
    print("Inside the context")
    raise ValueError("An error occurred")
# 输出:
# Entering the context
# Inside the context
# Exiting the context
# Exception occurred: An error occurred

11、contextlib 实现上下文管理器

  • Python 的 contextlib 模块提供了更简便的方式来创建上下文管理器。
  • contextlib.contextmanager 装饰器:通过生成器函数创建上下文管理器。
from contextlib import contextmanager

@contextmanager
def my_context_manager():
    print("Entering the context")
    try:
        yield "Resource"
    finally:
        print("Exiting the context")

with my_context_manager() as resource:
    print(f"Inside the context, using {resource}")
# 输出:
# Entering the context
# Inside the context, using Resource
# Exiting the context
  • contextlib.closing:用于自动调用对象的 close() 方法。
from contextlib import closing
from urllib.request import urlopen

with closing(urlopen("https://www.example.com")) as page:
    content = page.read()
    print(content)

四、自定义序列类

1、序列类型的分类

  • 在 Python 中,序列(Sequence) 是一种可迭代的、有序的数据结构,它的元素可以通过索引访问。序列类型是 Python
    中最基础的数据结构之一,常见的序列类型包括字符串(str)、列表(list)、元组(tuple)等。

序列的特点:

  • 有序性:序列中的元素是有序排列的,每个元素都有一个固定的位置(索引)。
  • 可迭代性:序列支持迭代操作,可以使用 for 循环遍历。
  • 索引访问:可以通过索引(下标)访问序列中的元素。
  • 切片操作:支持切片操作,获取子序列。
  • 长度可变性:部分序列类型(如列表)的长度是可变的,而另一些(如字符串、元组)是不可变的。

容器序列和扁平序列:

  • 容器序列:list、tuple、deque
  • 扁平序列:str、bytes、bytearray、array

可变序列与不可变序列:

  • 可变序列:list、deque、bytearray、array
  • 不可变序列:str、tuple、bytes

数组array和列表list的区别:

  • 列表:列表中元素的类型可以是任意类型
  • 数组:数组中元素的类型是相同的

2、序列操作 +、+= 和 extend 的区别

+ 操作符:

  • 作用:将两个序列拼接成一个新得序列
  • 返回值:返回一个新的序列,原序列不会被修改
  • 适用类型:列表、元组、字符串等
lst1 = [1, 2]
lst2 = [3, 4]
result = lst1 + lst2
print(result)  # 输出: [1, 2, 3, 4]
print(lst1)    # 输出: [1, 2](原列表未修改)

+= 操作符:

  • 作用:将右侧序列的元素添加到左侧序列中
  • 返回值:改变左侧序列,不返回新序列
  • 适用类型:列表、字符串等可变序列
lst1 = [1, 2]
lst2 = [3, 4]
lst1 += lst2
print(lst1)  # 输出: [1, 2, 3, 4](原列表被修改)
print(lst2)  # 输出: [3, 4](右侧列表未修改)

extend() 方法:

  • 作用:将另一个序列的元素添加到当前序列中
  • 返回值:没有返回值(即返回值为None),直接修改当前序列
  • 适用类型:列表
lst1 = [1, 2]
lst2 = [3, 4]
lst1.extend(lst2)
print(lst1)  # 输出: [1, 2, 3, 4](原列表被修改)
print(lst2)  # 输出: [3, 4](右侧列表未修改)

append() 方法:

  • 作用:将单个元素添加到列表的末尾(添加的内容作为一个元素整体被添加到末尾)
  • 返回值:无返回值(即返回值为None),直接修改原列表
lst = [1, 2, 3]
lst.append(4)
print(lst)  # 输出: [1, 2, 3, 4]

# 添加一个列表作为单个元素
lst.append([5, 6])
print(lst)  # 输出: [1, 2, 3, 4, [5, 6]]

3、实现可切片的对象

3-1、切片的基本语法

模式[start:end:step]

"""
    其中,第一个参数 start 表示切片开始的位置,默认为 0;
    第二个参数 end 表示切片截止(不包含)位置,默认为列表长度;
    第三个参数 step 表示切片的步长,默认为 1。

    当 start 为 0 时可以省略,当 end 为列表长度时可以省略,当 step 为 1 时可以省略,并且省略最后一个冒号“:”。
    另外,当 step 为负整数时,表示反向切片,这是 start 应该比 end 的值要大才行。
"""
  • 切片不会修改原序列,操作后会返回一个新的序列
  • 切片越界不会报错
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 提取索引 2 到 5 的元素
print(lst[2:6])  # 输出: [2, 3, 4, 5]

# 提取从开头到索引 4 的元素
print(lst[:5])  # 输出: [0, 1, 2, 3, 4]

# 提取从索引 5 到末尾的元素
print(lst[5:])  # 输出: [5, 6, 7, 8, 9]

# 提取整个序列
print(lst[:])  # 输出: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 每隔一个元素提取一次
print(lst[::2])  # 输出: [0, 2, 4, 6, 8]

# 反转序列
print(lst[::-1])  # 输出: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

# 从索引 2 到 8,每隔两个元素提取一次
print(lst[2:8:2])  # 输出: [2, 4, 6]

# 提取最后三个元素
print(lst[-3:])  # 输出: [7, 8, 9]

# 提取从倒数第 5 个到倒数第 2 个元素
print(lst[-5:-1])  # 输出: [5, 6, 7, 8]

# 修改索引 2 到 5 的元素
lst[2:6] = [10, 11, 12]
print(lst)  # 输出: [0, 1, 10, 11, 12, 5, 6, 7, 8, 9]

# 删除索引 2 到 5 的元素
lst[2:6] = []
print(lst)  # 输出: [0, 1, 6, 7, 8, 9]
# 等价于以下使用del方式删除
# del lst[2:6]
# print(lst)  # 输出: [0, 1, 6, 7, 8, 9]

3-2、实现可切片的对象

import numbers


class Group:
    """
    支持切片操作
    group_name:组名称
    company_name:组所属公司名称
    staffs:员工
    """

    def __init__(self, group_name, company_name, staffs):
        self.group_name = group_name
        self.company_name = company_name
        self.staffs = staffs

    def __getitem__(self, item):
        cls = type(self)
        # print("cls =>", cls, cls.__mro__, isinstance(item, slice)) # <class '__main__.Group'> (<class '__main__.Group'>, <class 'object'>) True
        if isinstance(item, slice):
            return cls(group_name=self.group_name, company_name=self.company_name, staffs=self.staffs[item])
        elif isinstance(item, numbers.Integral):
            return cls(group_name=self.group_name, company_name=self.company_name, staffs=[self.staffs[item]])

    def __str__(self):
        return str(self.staffs)

    def __len__(self):
        return len(self.staffs)

    def __contains__(self, item):
        if item in self.staffs:
            return True
        else:
            return False

    def __iter__(self):
        return iter(self.staffs)

    def __reversed__(self):
        self.staffs.reverse()


staffs = ["str", "int", "name", "age"]
group = Group(group_name='user', company_name="Company", staffs=staffs)
print(group)  # ['str', 'int', 'name', 'age']

# 实现切片 __getitem__
# print(group[0:2])  # ['str', 'int']
# 计算列表长度 __len__
# print(len(group))  # 4
# 判断是否包含 __contains__
# if "str" in group:
#     print("True")  # True
# else:
#     print("False")  # False
# 可迭代 __iter__
# for item in group:
#     print("item=>", item)  # str in name age
# 列表反转 __reversed__
# reversed(group)
# print(group)  # ['age', 'name', 'int', 'str']

4、bisect 管理可排序序列

  • 在 Python 中,bisect 模块提供了一种高效的方式来管理已排序的序列。它基于二分查找算法,能够在 O(log n)
    的时间复杂度内完成插入和查找操作,非常适合处理需要保持有序的序列。

bisect 模块的核心功能:

  • bisect_left():查找元素应该插入的位置(左侧)。
  • bisect_right()(简写 bisect()):查找元素应该插入的位置(右侧)。
  • insort_left():将元素插入到序列中(左侧)。
  • insort_right()(简写 insort()):将元素插入到序列中(右侧)。

需要保持序列严格升序时使用

import bisect

lst = [1, 2, 3, 4, 4, 4, 5]
pos_l = bisect.bisect_left(lst, 4)
print(pos_l)  # 3 
pos_r = bisect.bisect_right(lst, 4)
print(pos_r)  # 6
bisect.insort_left(lst, 2)
print(lst)  # [1, 2, 2, 3, 4, 4, 4, 5]
bisect.insort_right(lst, 10)
print(lst)  # [1, 2, 2, 3, 4, 4, 4, 5, 10]

5、什么时候不该用列表

array 和 list 的区别:

  • list 可以存放任意类型的数据
  • array 只能存放指定的数据类型。在内存中使用的连续的内存进行存储,效率更高。
import array

my_arr = array.array('i')
my_arr.append(1)
my_arr.append(3)
print(my_arr) # array('i', [1, 3])

6、列表推导式、生成器表达式、字典推导式

6-1、列表推导式(List Comprehension)

  • 作用:快速生成列表
  • 语法:
# 语法
[表达式 for 元素 in 可迭代对象 if 条件]
# 生成 1 到 10 的平方列表
squares = [x ** 2 for x in range(1, 11)]
print(squares)  # 输出: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# 过滤出偶数
evens = [x for x in range(10) if x % 2 == 0]
print(evens)  # 输出: [0, 2, 4, 6, 8]
  • 特点:

返回一个列表。
适合生成小型列表。

6-2、生成器表达式(Generator Expression)

  • 作用:快速生成生成器
  • 语法:
# 语法
(表达式 for 元素 in 可迭代对象 if 条件)
# 生成 1 到 10 的平方生成器
squares_gen = (x ** 2 for x in range(1, 11))
print(squares_gen)  # 输出: <generator object <genexpr> at 0x...>

# 使用生成器
for num in squares_gen:
    print(num, end=" ")  # 输出: 1 4 9 16 25 36 49 64 81 100
  • 特点:

返回一个生成器对象。
惰性求值,节省内存。
适合生成大型数据集。

6-3、字典推导式(Dictionary Comprehension)

  • 作用:快速生成字典
  • 语法:
{键表达式: 值表达式 for 元素 in 可迭代对象 if 条件}
# 生成数字到其平方的字典
squares_dict = {x: x ** 2 for x in range(1, 6)}
print(squares_dict)  # 输出: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 过滤出偶数并生成字典
evens_dict = {x: x ** 2 for x in range(10) if x % 2 == 0}
print(evens_dict)  # 输出: {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
  • 特点:

返回一个字典。
适合生成键值对数据。

6-4、集合推导式(Set Comprehension)

  • 作用:快速生成集合
  • 语法:
{表达式 for 元素 in 可迭代对象 if 条件}
# 生成 1 到 10 的平方集合
squares_set = {x ** 2 for x in range(1, 11)}
print(squares_set)  # 输出: {64, 1, 4, 36, 100, 9, 16, 49, 81, 25}

# 过滤出偶数并生成集合
evens_set = {x for x in range(10) if x % 2 == 0}
print(evens_set)  # 输出: {0, 2, 4, 6, 8}
  • 特点:

返回一个集合。
自动去重。

五、深入 python 的 set 和 dict

1、dict 的常见用法

  • dict(字典)是一种非常常用的数据结构,用于存储键值对(key-value pairs)。字典的特点是 快速查找、键唯一 和 可变性

2、dict 的子类

3、set 和 frozenset

4、dict和set的实现原理

六、对象引用、可变性和垃圾回收

七、元类编程

八、迭代器和生成器

九、Python Socket 编程

十、多线程、多进程和线程池编程

十一、协程和异步IO

十二、AsyncIO并发编程