Fluent Python笔记持续整理

后面几节没整理;没有目录;一些重要章节不是很全,仅仅是介绍。
待拆分
Ref:https://www.52pojie.cn/thread-1816710-1-1.html

在Python中,如果一个类定义了__getitem__方法,那么该类的对象可以像序列(如列表、字符串等)一样使用索引运算符[]来访问其元素。

class Seq:
def __init__(self) -> None:
self.data = [1,2,3]
def __getitem__(self, index):
return self.data[index]

seq = Seq()
seq[0:2]

这类方法称为魔术方法,类似的还有__len__, __setitem__, __str__, __repr__, __add__, __eq__

__str____repr__用于定义对象的字符串表示形式。__str__方法用于返回对象的人类可读的字符串表示形式,通常用于打印输出或显示给用户。 __repr__方法用于返回对象的官方字符串表示形式,通常用于调试和开发过程中。 下面是一个示例,展示了如何在自定义类中使用__str____repr__方法:

class Point:
def __init__(self, x, y) -> None:
self.x = x
self.y = y

def __str__(self) -> str:
return f"print..Point({self.x}, {self.y})"
pass

def __repr__(self) -> str:
return f"Point({self.x}, {self.y})"
pass

p = Point(3, 4)
print(p)
repr(p)

总结魔术方法 TODO


列表推导式: [expression for item in iterable if condition]
在处理大型数据集时,列表推导式可能会影响性能,这时应考虑使用生成器表达式或其他更适合的方法。

在Python中,map()和filter()是两个内置函数,用于对可迭代对象进行映射和过滤操作。 1. map()函数: map()函数接受一个函数和一个或多个可迭代对象作为参数,将函数应用于每个可迭代对象中的元素,并返回一个新的迭代器(在Python 3中返回迭代器,在Python 2中返回列表)。 map()函数的语法如下:
map(function, iterable, …)

filter()函数: filter()函数接受一个函数和一个可迭代对象作为参数,根据函数的返回值(True或False)来过滤可迭代对象中的元素,并返回一个新的迭代器(在Python 3中返回迭代器,在Python 2中返回列表)。 filter()函数的语法如下:

filter(function, iterable)

使用map()和filter()函数可以简化对列表的处理,使代码更简洁、可读性更高。然而,请注意在处理大型数据集时,这些函数可能会影响性能,这时应考虑使用列表推导式或其他更适合的方法。

生成器表达式与列表推导式的语法非常相似,只是将[]替换为(),从而创建一个生成器对象而不是列表对象。以下是生成器表达式的一般语法格式

(expression for item in iterable if condition)

与列表推导式不同,生成器表达式生成的是一个迭代器,而不是立即生成一个完整的列表。这种延迟计算的特性使得生成器表达式非常适合处理大数据集或无限序列。

拆包:拆包(Unpacking)是一种将序列(如元组或列表)中的元素分配给变量的操作。它可以方便地将序列中的元素解包并赋值给多个变量。 拆包可以应用于任何可迭代对象,例如元组、列表、集合等。要进行拆包,只需将可迭代对象放在赋值语句的左侧,并使用与元素数量相同的变量进行赋值。

使用拆包可以简化代码,并使其更易读。它允许一次性访问和操作序列中的多个元素,而不需要使用索引来逐个访问。 需要注意的是,如果拆包的变量数量与序列中的元素数量不匹配,将会引发ValueError异常。如果只想拆包序列中的一部分元素,可以使用占位符(如_)来表示不需要的元素。

在Python中,* 可以用于拆包操作,它可以将可迭代对象中的剩余元素打包成一个列表。这种用法通常称为“可变长参数”或“可变长参数列表”。 使用号拆包可以处理可变长度的参数列表,无需事先知道可迭代对象中的元素个数。

Python支持嵌套拆包

lst = [1, 2, 3, 4, 5]
a, *rest, b = lst
rest

序列模式匹配

从 Python 3.10 版本开始,引入了 match 表达式,以提供更强大的模式匹配功能。match 表达式可以用于匹配和解构各种数据类型,包括序列类型。 以下是一个示例,展示了如何在 Python 3.10 中使用 match 表达式进行模式匹配:

def process_data(data):
match data:
case [1, 2, 3]:
print("匹配到 [1, 2, 3]")
case [4, x, y]:
print(f"匹配到 [4, {x}, {y}]")
case [a, b, c]:
print(f"匹配到 [{a}, {b}, {c}]")
case _:
print("未匹配到任何模式")

# 测试示例
process_data([1, 2, 3]) # 输出: 匹配到 [1, 2, 3]
process_data([4, 5, 6]) # 输出: 匹配到 [4, 5, 6]
process_data([7, 8, 9]) # 输出: 匹配到 [7, 8, 9]
process_data([10, 11, 12, 13]) # 输出: 未匹配到任何模式

字典推导式:它类似于列表推导式,但是生成的结果是字典而不是列表。

{key_expression: value_expression for item in iterable}

在Python中,defaultdict 是 collections 模块中的一个类,它是 dict 类的一个子类,用于创建具有默认值的字典。 与普通的字典不同,defaultdict 在创建时需要指定一个默认值的类型,当访问一个不存在的键时,它会自动返回默认值,而不会抛出 KeyError 异常。 以下是一个示例,展示了如何使用 defaultdict 创建具有默认值的字典:

from collections import defaultdict

d = defaultdict(int)

d['a'] = 1
d['b'] # 0

当我们使用一个字典访问一个不存在的键时,如果字典类中定义了 __missing__ 方法,那么在访问不存在的键时,Python 会自动调用该方法,并将所访问的键作为参数传递给它。 以下是一个示例,展示了如何使用 __missing__ 方法自定义字典中访问不存在的键的行为:

class MyDict(dict):
def __missing__(self, key):
return f"Key '{key}' is missing!"

d = MyDict()
d['a'] = 1

print(d['a']) # 输出: 1
print(d['b']) # 输出: Key 'b' is missing!

collections.ChainMap 是 Python 中的一个类,它用于将多个字典或映射对象链接在一起,形成一个逻辑上的单一映射。 ChainMap 提供了一种方便的方式来处理多个字典或映射对象,并将它们作为一个整体来操作。它在逻辑上将这些字典或映射对象链接在一起,形成一个查找链。当我们在 ChainMap 对象上进行键的查找时,它会按照链接顺序依次在各个字典或映射对象中查找。

collections.ChainMap的好处是他不会真的合并对象,而只是形成一个链接,因此他不会占用额外的空间。具体可看源码:https://blog.csdn.net/weixin_37780776/article/details/123777723

collections.Counter 是 Python 中的一个类,它用于计数可哈希对象的出现次数。它是一个无序的集合,其中元素存储为键,其计数存储为值。 Counter 类提供了一些有用的方法,用于对计数器进行操作,如增加、减少元素的计数、获取最常见的元素、计算元素的总数等。

from collections import Counter

counter = Counter('abcda')
counter
counter.values()

在 Python 中,创建子类时,可以选择继承内置的 dict 类或 collections.UserDict 类来实现自定义字典类的功能。 尽管 dict 类是 Python 内置的字典类,但在某些情况下,继承 collections.UserDict 类可能更适合。collections.UserDict 是一个可变字典类的包装器,它提供了更简单和安全的方式来创建自定义字典类。 以下是一个示例,展示了如何使用 collections.UserDict 来创建自定义字典类:

from collections import UserDict

class MyDict(UserDict):
def __setitem__(self, key, value):
# 自定义设置键值的行为
if key == 'special_key':
value += 10
super().__setitem__(key, value)

def __getitem__(self, key):
# 自定义获取键值的行为
return super().__getitem__(key)

my_dict = MyDict()
my_dict['special_key'] = 5
print(my_dict['special_key']) # 输出: 15

在上面的示例中,我们创建了一个名为 MyDict 的自定义字典类,并继承了 UserDict 类。通过重写 __setitem____getitem__ 方法,我们可以自定义设置和获取键值的行为。 继承 UserDict 类可以帮助我们避免直接修改 dict 对象的内部结构,从而更安全地创建自定义字典类。 当然,如果你只需要创建一个简单的字典类,而不需要自定义特定的行为,那么继承 dict 类也是可行的。

bytes_data = b'\xe4\xbd\xa0\xe5\xa5\xbd'
decoded_text = bytes_data.decode('utf-8')
decoded_text

在 Python 中,具名元组(NamedTuple)是一种特殊类型的元组,它允许你给每个元素命名,并通过名称访问元素,而不仅仅是通过索引。 具名元组是通过 collections 模块中的 namedtuple 函数创建的。

from collections import namedtuple

# 创建具名元组类
Person = namedtuple('Person', ['name', 'age', 'gender'])

# 创建具名元组实例
person1 = Person('Alice', 25, 'female')
person2 = Person('Bob', 30, 'male')

# 访问元素
print(person1.name) # 输出:Alice
print(person2.age) # 输出:30
print(person1.gender) # 输出:female

@dataclass 是 Python 3.7 引入的一个装饰器,用于简化创建和使用数据类(data class)的过程。数据类是一种用于存储数据的特殊类,它自动为我们生成一些常见的方法,如 __init____repr____eq__ 等,使得我们可以更方便地创建和操作数据对象。 使用 @dataclass 装饰器,我们可以在类定义中省略一些繁琐的代码。以下是一个使用 @dataclass 装饰器创建数据类的示例

在 Python 中,垃圾回收是自动进行的,它是通过引用计数和循环垃圾收集机制来实现的。下面是对这两种机制的简要说明:

  1. 引用计数(Reference Counting):Python 使用引用计数来追踪每个对象的引用数。当对象的引用数变为 0 时,说明没有任何引用指向该对象,Python 会立即回收该对象的内存空间。这是一种高效的垃圾回收机制,可以立即回收不再使用的对象。

然而,引用计数机制无法处理循环引用的情况,即两个或多个对象彼此引用,但没有其他引用指向它们。这种情况下,引用计数无法将对象的引用数降为 0,导致内存泄漏。为了解决这个问题,Python 提供了循环垃圾收集机制。

  1. 循环垃圾收集(Cycle Garbage Collection):Python 使用循环垃圾收集机制来检测和回收循环引用的对象。循环垃圾收集器会定期运行,它会检查所有对象的引用关系,并标记那些可以被回收的对象。然后,它会释放这些对象所占用的内存空间。

循环垃圾收集器使用了更复杂的算法,如标记-清除(mark and sweep)和分代回收(generational collection),以提高垃圾回收的效率和性能。

需要注意的是,Python 的垃圾回收机制是自动的,开发者无需手动管理内存。然而,对于一些特殊情况,如大型数据结构或循环引用的对象,可能需要注意内存的使用和释放,以避免潜在的内存泄漏问题。

在 Python 中,函数的参数传递方式是通过引用传递(pass-by-reference),也可以称为对象的引用传递。这意味着函数参数在传递过程中,实际上是将对象的引用传递给函数,而不是对象本身的副本。

总结起来,当函数参数是引用传递时,函数内部对参数对象的修改会影响到原始对象,但是对参数进行重新赋值则不会影响到原始对象。

del是一个关键字,常见用法:

  1. 删除对象
  2. 删除对象的属性
  3. 删除列表中的元素
  4. 删除字典中的键值对

python中函数是一等公民:

  1. 将函数赋值给变量
  2. 函数可以作为参数
  3. 函数作为另一个函数的返回值
  4. 嵌套函数

在 Python 中,高阶函数(Higher-order functions)是指能够接受函数作为参数,或者返回一个函数的函数。高阶函数是函数式编程的重要概念,它可以让代码更加简洁、灵活和可复用。 以下是一些常见的高阶函数的示例:

  • map
  • filter
  • sorted
  • lambda函数:lambda 函数通常只适用于简单的、单行的函数逻辑。如果你需要编写复杂的函数逻辑,还是建议使用常规的函数定义方式。

用户可以通过自定义类来创建可调用对象。为了使一个类的实例可以像函数一样被调用,需要在类中定义 __call__() 方法

总结py中星号的用法

在 Python 中,有一些流行的包和库支持函数式编程范式。以下是其中一些常用的包:

  1. functools:functools 是 Python 内置的一个模块,提供了一些函数式编程的工具函数。它包含了一些用于函数操作的高阶函数,如 map()filter()reduce(),以及一些用于函数组合和函数装饰器的工具函数。

  2. itertools:itertools 也是 Python 内置的一个模块,提供了一些用于迭代和组合的工具函数。它包含了一些常见的函数式编程模式,如生成无限迭代器、组合迭代器、过滤迭代器等。

  3. operator:operator 是 Python 内置的一个模块,提供了一些常见的运算符的函数形式。它提供了一种函数式的方式来执行常见的算术、比较和逻辑运算。

  4. toolz:toolz 是一个功能强大的函数式编程工具包,提供了一些高阶函数和工具,用于处理集合、迭代和函数组合。它提供了一些函数式编程的常见模式,如 curry()compose()pipe() 等。

  5. fn:fn 是一个专注于函数式编程的库,提供了一些函数式编程的工具函数和数据类型。它支持函数组合、柯里化、惰性求值等函数式编程的特性。

这些包和库提供了丰富的工具和函数,帮助开发者更方便地应用函数式编程的思想和模式。无论是在函数组合、迭代操作、惰性求值还是其他函数式编程的场景中,它们都能提供很多便利。

在 Python 中,类型注解是一种可选的语法,用于提供变量、函数参数、函数返回值等的类型信息。类型注解可以帮助开发者和工具在静态类型检查时发现潜在的类型错误,提高代码的可读性和可维护性。

以下是一些常用的类型注解,可以在 Python 的注解中使用:

  1. 基本类型:int、float、bool、str 等基本数据类型。

  2. 容器类型:list、tuple、dict、set 等容器类型。可以使用方括号 [] 表示列表,圆括号 () 表示元组,大括号 {} 表示字典和集合。

  3. 自定义类型:自定义的类、枚举类、命名元组等。

  4. Union 类型:使用 Union| 符号表示多个类型中的一个,表示一个变量可以是多种类型之一。

  5. Optional 类型:使用 Optional 表示一个变量可以是指定类型或者 None

  6. Callable 类型:使用 Callable 表示一个变量是可调用对象,如函数、方法等。

  7. 类型变量:使用 TypeVar 表示一个类型变量,用于泛型编程或表示复杂类型。

  8. Any 类型:使用 Any 表示任意类型,相当于取消了类型检查。

需要注意的是,类型注解在 Python 中是可选的,不会影响代码的运行。它们只是提供了一种给开发者和工具更多类型信息的方式,以提高代码的可读性和可维护性。Python 解释器在运行时不会对类型注解进行验证,类型检查需要通过静态类型检查工具(如 mypy)进行。

装饰器和闭包总结和使用装饰器改进策略模式

实例方法、静态方法、类方法比较

class MyClass:
cls_var = 'cls_var'
def __init__(self, ins_var) -> None:
self.ins_var = ins_var
pass

@classmethod
def cls_method(cls):
print('This is a cls method')
print(cls.cls_var)

def ins_method(self):
print('This is a ins method')
print(self.ins_var)

@staticmethod
def static_method():
print('This is a static method')
print(MyClass.cls_var)

MyClass.static_method()

在Python中,有两种方式可以限制对类属性的访问:私有属性和受保护的属性。

  1. 私有属性: 私有属性是以双下划线 __ 开头的属性,它们只能在类的内部访问,无法从外部直接访问。私有属性的目的是防止意外的修改或访问,以保护类的内部实现细节。

尽管无法直接访问私有属性和方法,但可以通过使用 实例变量._类名__属性名的方式来间接访问私有属性和方法。

请注意,这种方式只是一个约定,不是真正的访问控制机制。在Python中,没有真正的私有性,它只是一种约定,用于指示这些属性和方法应该被视为私有的。

  1. 受保护的属性: 受保护的属性是以单下划线 _ 开头的属性,它们建议在类的外部不直接访问,但可以从子类中访问。受保护的属性是一种更宽松的访问限制,用于指示这些属性应该被视为受保护的。

__slots__ 是一个特殊的类属性,用于限制类的实例可以拥有的属性。 通过使用 __slots__,你可以告诉Python仅为类的实例分配指定的属性,从而节省了内存空间。当你知道类的实例只需要固定的一组属性时,使用 __slots__ 可以提高性能。 下面是一个示例,演示了如何使用 __slots__

class MyClass:
__slots__ = ("attribute1", "attribute2")

def __init__(self, value1, value2):
self.attribute1 = value1
self.attribute2 = value2

obj = MyClass("Value 1", "Value 2")
print(obj.attribute1)
print(obj.attribute2)

obj.attribute3 = "Value 3" # 无法为属性3分配内存,会引发 AttributeError

在上面的示例中,我们定义了一个名为 MyClass 的类,并使用 __slots__ 属性指定了类的实例只能拥有 attribute1 和 attribute2 这两个属性。 在类的 __init__方法中,我们为这两个属性赋予了初始值。 创建 MyClass 的实例 obj 后,我们可以访问这两个属性的值。 然而,当我们尝试为 obj 的 attribute3 属性赋值时,会引发 AttributeError 异常。这是因为 __slots__ 属性限制了实例只能拥有 attribute1 和 attribute2 这两个属性,无法为其他属性分配内存。 需要注意的是,__slots__ 是一个类属性,而不是实例属性。它仅对类的实例起作用,不对类本身起作用。 使用 __slots__ 可以有效地减少实例所占用的内存空间,但需要注意选择适当的属性列表,确保不会限制过多或过少的属性。

动态存取属性

  1. 点号赋值
  2. getattr()、 setattr()
  3. 使用字典: obj.__dict__['name'] = 'John'

zip函数 拉链

鸭子类型是一种动态类型系统的概念,它强调在编程中关注对象的行为而不是具体的类型。根据鸭子类型的原则,只要一个对象具有特定的方法或属性,那么它就可以被视为具有相同的行为,而不需要显式地指定相同的类型。 在Python中,鸭子类型编程可以通过以下方式实现: 1. 不依赖具体的类型:编写代码时,不需要关注对象的具体类型,而是关注对象是否具有所需的方法或属性。例如,如果一个对象具有read()和write()方法,那么它可以被当作文件对象来使用,而不需要是file类型的实例。 2. 使用try-except语句:在使用某个方法或属性之前,可以使用try-except语句来捕获可能的异常。如果对象具有所需的方法或属性,那么代码将正常执行;如果对象没有所需的方法或属性,那么会抛出异常,可以在except块中处理该异常。 以下是一个简单的示例,演示了鸭子类型编程的概念

def process_data(obj):
try:
obj.read()
obj.process()
obj.write()
except AttributeError:
print("对象不具有所需的方法或属性")

class FileObject:
def read(self):
print("读取文件")

def process(self):
print("处理数据")

def write(self):
print("写入文件")

class DatabaseObject:
def read(self):
print("读取数据库")

def process(self):
print("处理数据")

def write(self):
print("写入数据库")

file_obj = FileObject()
database_obj = DatabaseObject()

process_data(file_obj) # 输出: 读取文件、处理数据、写入文件
process_data(database_obj) # 输出: 读取数据库、处理数据、写入数据库
process_data("Hello") # 输出: 对象不具有所需的方法或属性

在Python中,可以通过子类化内置类型来创建自定义的数据类型。内置类型,如list、dict、str等,可以作为基类来定义子类,从而扩展或定制其行为。 以下是一个简单的示例,演示了如何子类化内置类型list:

class MyList(list):
def __init__(self, *args):
super().__init__(*args)

def append(self, item):
print("Appending item:", item)
super().append(item)

my_list = MyList([1, 2, 3])
print(my_list) # 输出: [1, 2, 3]

my_list.append(4) # 输出: Appending item: 4
print(my_list) # 输出: [1, 2, 3, 4]

需要注意的是,子类化内置类型有一些限制和注意事项。例如,某些内置类型的行为可能是通过C语言实现的,因此无法直接覆盖。此外,一些内置类型具有特殊的方法和行为,需要进行特殊处理。

多重继承、混入类:略。

在Python中,TypedDict是一种用于定义具有特定键和值类型的字典的类型提示工具。它是Python 3.8版本中引入的,并且需要使用typing模块进行导入。 TypedDict允许我们为字典的键和值指定类型注解,以提供更严格的类型检查和类型提示。以下是一个示例:

from typing import TypedDict

class Person(TypedDict):
name: str
age: int

person: Person = {
"name": "John",
"age": 30
}

在上面的示例中,我们定义了一个名为Person的TypedDict,它具有两个键:name和age。我们在键后面使用冒号(:)指定了键的类型注解,以及值的类型注解。 然后,我们可以使用Person类型来注解一个字典变量person,并确保该字典的键和值类型与Person类型定义匹配。 TypedDict提供了更强的类型约束,可以在静态类型检查工具(如mypy)或IDE中提供更准确的类型提示。它适用于需要对字典的结构和类型进行严格控制的情况。 需要注意的是,TypedDict只在运行时对字典进行类型检查,而不是在编译时。因此,它不能完全替代编写健壮的输入验证和数据校验代码。

typing.cast是Python中的一个类型提示工具函数,它用于显式地指定一个对象的类型,并返回该对象的类型转换后的结果。 cast函数的签名如下:

def cast(type, value) -> type:
...

其中,type参数是目标类型,value参数是要进行类型转换的对象。cast函数会将value对象转换为type类型,并返回转换后的结果。 以下是一个示例:

from typing import cast

num_str = "123"
num_int = cast(int, num_str)
print(num_int) # 输出: 123
print(type(num_int)) # 输出: <class 'int'>

在上面的示例中,我们将字符串num_str通过cast函数转换为整数类型int。尽管num_str的类型是字符串,但我们使用cast函数显式地指定了目标类型为整数,并得到了转换后的结果。 需要注意的是,cast函数并不会进行实际的类型检查或类型转换。它仅仅是一个类型提示工具,用于向静态类型检查器(如mypy)提供额外的信息,以便进行更准确的类型推断和类型检查。 在使用cast函数时,应该谨慎使用,并确保对象的实际类型与指定的目标类型是兼容的,以避免运行时错误。

在Python中,我们可以使用泛化类(Generic Class)来实现具有通用性的类,以便在不同的类型上使用相同的代码。泛化类可以与类型参数一起使用,这样我们就可以在类定义中使用这些参数来表示不确定的类型。 以下是一个示例,展示如何使用泛化类来实现一个通用的堆栈类:

from typing import TypeVar, Generic

T = TypeVar('T')

class Stack(Generic[T]):
def __init__(self):
self.items = []

def push(self, item: T) -> None:
self.items.append(item)

def pop(self) -> T:
if not self.is_empty():
return self.items.pop()
else:
raise IndexError("Stack is empty")

def is_empty(self) -> bool:
return len(self.items) == 0

def size(self) -> int:
return len(self.items)

在上面的示例中,我们使用typing模块中的TypeVar来定义一个类型变量T。然后,在类定义中使用Generic[T]来表示这是一个泛化类,并且T是一个类型参数。 在类的方法中,我们可以使用类型参数T来表示不确定的类型。例如,在push方法中,我们接受一个类型为T的参数,并将其添加到堆栈中。在pop方法中,我们使用类型参数T来指定返回值的类型。 使用泛化类时,我们可以在实例化类时指定具体的类型,或者让类型推断机制自动推断类型。以下是一些示例:

stack = Stack[int]()  # 实例化一个整数类型的堆栈
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop()) # 输出: 3
print(stack.size()) # 输出: 2

stack2 = Stack[str]() # 实例化一个字符串类型的堆栈
stack2.push("Hello")
stack2.push("World")
print(stack2.pop()) # 输出: "World"
print(stack2.size()) # 输出: 1

通过使用泛化类,我们可以在不同的类型上使用相同的代码逻辑,从而实现更通用、可重用的类。

在Python中,运算符重载(Operator Overloading)是指通过特殊的方法(也称为魔术方法或双下划线方法)来定义自定义类型的行为,使其支持标准的运算符操作。 以下是一些常用的运算符重载方法及其对应的运算符:

__add__(self, other): 运算符 + 的重载方法,用于实现两个对象相加的操作。

__sub__(self, other): 运算符 - 的重载方法,用于实现两个对象相减的操作。

__mul__(self, other): 运算符 * 的重载方法,用于实现两个对象相乘的操作。

__div__(self, other): 运算符 / 的重载方法,用于实现两个对象相除的操作。

__eq__(self, other): 运算符 == 的重载方法,用于实现两个对象相等比较的操作。

__lt__(self, other): 运算符 < 的重载方法,用于实现两个对象小于比较的操作。

__gt__(self, other): 运算符 > 的重载方法,用于实现两个对象大于比较的操作。 以下是一个示例,展示如何在自定义类中重载运算符:

class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __add__(self, other):
if isinstance(other, Point):
return Point(self.x + other.x, self.y + other.y)
elif isinstance(other, int) or isinstance(other, float):
return Point(self.x + other, self.y + other)
else:
raise TypeError("Unsupported operand type for +")

def __eq__(self, other):
if isinstance(other, Point):
return self.x == other.x and self.y == other.y
else:
return False

def __str__(self):
return f"({self.x}, {self.y})"

# 使用运算符重载
p1 = Point(1, 2)
p2 = Point(3, 4)
p3 = p1 + p2
print(p3) # 输出: (4, 6)

p4 = p1 + 5
print(p4) # 输出: (6, 7)

print(p1 == p2) # 输出: False
print(p1 == Point(1, 2)) # 输出: True

在Python中,iter()是一个内置函数,用于返回一个可迭代对象的迭代器。 可迭代对象是指实现了__iter__()方法或__getitem__()方法的对象。迭代器是一个实现了__iter__()方法和__next__()方法的对象。iter()函数接受一个可迭代对象作为参数,并返回该可迭代对象的迭代器。 以下是iter()函数的语法:

可迭代对象(Iterable)和迭代器(Iterator)是Python中用于迭代操作的两个重要概念,它们之间有一些区别:

  1. 可迭代对象(Iterable):

    • 可迭代对象是指实现了iter()方法或getitem()方法的对象。
    • 可迭代对象可以使用for循环进行迭代,也可以使用内置函数iter()将其转换为迭代器。 - 可迭代对象每次迭代都会返回一个新的迭代器。
  2. 迭代器(Iterator):

    • 迭代器是指实现了iter()方法和next()方法的对象。
    • 迭代器用于从可迭代对象中逐个获取元素,每次调用next()方法返回迭代对象中的下一个元素。
    • 迭代器具有内部状态,可以记住当前迭代的位置。
    • 当迭代器中没有更多的元素可供获取时,调用next()方法会引发StopIteration异常。
      下面是一个示例,展示可迭代对象和迭代器的区别:
class SentenceIterator:
def __init__(self, words):
self.words = words
self.index = 0

# 有了这个函数才是迭代器
def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word

class Sentence:
'''实现一个类,传入一个句子,在使用for迭代的时候,每次返回这个句子的单词(空格划分)'''
def __init__(self, val:str) -> None:
self.words = val.split(' ')
pass

def __iter__(self):
'''每次迭代都返回新的返回迭代器,有了这个方法才是可迭代对象'''
return SentenceIterator(self.words)

sentence = Sentence("I'm a Person.")
for word in sentence:
print(word)

生成器(Generator)是一种特殊的迭代器,它可以使用函数和yield语句来定义。生成器函数可以逐个产生元素,而不需要一次性生成所有元素。

def countdown(n):
while n > 0:
yield n
n -= 1

# 创建生成器对象
generator = countdown(5)

# 使用生成器逐个获取元素
print(next(generator)) # 输出: 5
print(next(generator)) # 输出: 4
print(next(generator)) # 输出: 3
print(next(generator)) # 输出: 2
print(next(generator)) # 输出: 1
# print(next(generator)) # 引发 StopIteration 异常

在上述示例中,我们定义了一个生成器函数countdown(),它使用while循环和yield语句逐个产生元素。我们通过调用生成器函数来创建一个生成器对象generator。 然后,我们使用next()函数逐个获取生成器中的元素。每次调用next()函数时,生成器函数会从上次暂停的位置继续执行,直到遇到下一个yield语句。 需要注意的是,当生成器中没有更多的元素可供获取时,再次调用next()函数会引发StopIteration异常。 生成器的一个重要特点是它们在迭代过程中保持状态,而不是一次性生成所有元素。这使得生成器非常适合处理大量数据或无限序列,因为它们只在需要时产生元素,从而节省了内存和计算资源。 除了使用生成器函数创建生成器之外,还可以使用生成器表达式来创建生成器。生成器表达式与列表推导式类似,但使用圆括号而不是方括号。 以下是使用生成器表达式创建生成器的示例:

numbers = [1, 2, 3, 4, 5]

# 使用生成器表达式创建生成器对象
generator = (num for num in numbers if num % 2 == 0)

# 使用生成器逐个获取元素
print(next(generator)) # 输出: 2
print(next(generator)) # 输出: 4
# print(next(generator)) # 引发 StopIteration 异常

在上述示例中,我们使用生成器表达式(num for num in numbers if num % 2 == 0)创建了一个生成器对象generator。该生成器对象会逐个产生列表numbers中满足条件的偶数。

在Python中,上下文管理器(Context Manager)是一种用于管理资源的对象,它定义了在进入和退出特定代码块时要执行的操作。上下文管理器通常用于确保资源的正确分配和释放,例如打开和关闭文件、获取和释放锁等。 上下文管理器可以使用两种方式来实现:通过类实现和通过装饰器实现。 1. 类实现上下文管理器: - 通过定义一个类,并在类中实现__enter__()和__exit__()方法来创建上下文管理器。 - __enter__()方法在进入代码块前执行,通常用于获取资源或执行必要的准备工作,并将资源返回给调用者。 - __exit__()方法在退出代码块时执行,通常用于释放资源或执行清理操作。 - 如果在代码块中发生异常,异常会被传递给__exit__()方法处理,可以在__exit__()方法中进行异常处理和日志记录等操作。 以下是一个使用类实现上下文管理器的示例:

# 模拟 Python 的打开文件、关闭文件操作
class Filemanager:
def __init__(self, name, mode):
print('calling __init__ method')
self.name = name
self.mode = mode
self.file = None

def __enter__(self):
print('caling __enter__ method')
self.file = open(self.name, self.mode)
return self.file

def __exit__(self, exc_type, exc_val, exc_tb):
print('caling __exit__ method')
if self.file:
self.file.close


# Filemanager为上下文管理器
# with Filemanager('test.txt', 'w') as f 是上下文表达式,f为资源对象
with Filemanager('test.txt', 'w') as f:
print('ready to write to file')
f.write('Hello World')

exit方法中的参数exc_type、exc_val、exc_tb分别表示exception type、exception value、traceback。

装饰器实现上下文管理器:

  • 通过使用@contextlib.contextmanager装饰器和生成器函数来创建上下文管理器。
  • 在生成器函数内部,使用yield语句将控制权暂时交给调用者,并在yield语句前后执行进入和退出代码块的操作。
  • 调用者可以使用with语句来使用上下文管理器,而不需要手动调用enter()和exit()方法。 以下是一个使用装饰器实现上下文管理器的示例:
from contextlib import contextmanager

@contextmanager
def file_manager(name, mode):
try:
f = open(name, mode)
yield f
finally:
f.close()

with file_manager('test.txt', 'w') as f:
f.write('hello world')

在上述示例中,我们使用@contextmanager装饰器将生成器函数mycontextmanager()转换为上下文管理器。在生成器函数内部,我们使用yield语句将控制权交给调用者,并在yield语句前后执行进入和退出代码块的操作。 然后,我们使用with语句来使用上下文管理器。在with代码块中,我们可以执行需要的操作,而不需要手动调用enter()和__exit()方法。如果在代码块中发生异常,异常会被传递给生成器函数中的except块处理。 上下文管理器是一种非常有用的编程模式,它可以确保资源的正确分配和释放,提高代码的可读性和健壮性。

GIL(Global Interpreter Lock)是Python解释器中的一个机制,它是为了保证解释器在多线程环境下的安全性而引入的。GIL的存在导致了Python解释器在同一时间只能执行一个线程的字节码,从而限制了多线程并行执行的能力。

GIL的作用是在解释器级别上保护Python对象免受并发访问的影响。由于Python的内存管理不是线程安全的,GIL可以确保同一时间只有一个线程能够操作Python对象,从而避免了多线程访问同一对象时可能引发的竞态条件和数据不一致问题。

由于GIL的存在,Python的多线程并不能真正发挥多核处理器的并行计算能力。在CPU密集型任务中,多线程的性能可能比单线程还要差。然而,在I/O密集型任务中,多线程仍然可以提供一定的性能优势,因为线程可以在等待I/O操作完成时释放GIL,允许其他线程执行。

需要注意的是,GIL只存在于CPython解释器中,它是Python的参考实现。其他一些Python解释器,如Jython和IronPython,没有GIL,可以实现真正的并行执行。

为了充分利用多核处理器的并行计算能力,可以考虑使用多进程、异步编程或使用其他语言编写CPU密集型任务的模块。

下面是几个使用Python实现并发的示例: 1. 多线程并发下载文件: 使用threading模块创建多个线程,每个线程负责下载一个文件。

import threading
import requests

def download_file(url, filename):
response = requests.get(url)
with open(filename, 'wb') as file:
file.write(response.content)

# 文件下载链接列表
urls = [
'http://example.com/file1.txt',
'http://example.com/file2.txt',
'http://example.com/file3.txt'
]

# 创建多个线程进行文件下载
threads = []
for url in urls:
filename = url.split('/')[-1]
thread = threading.Thread(target=download_file, args=(url, filename))
thread.start()
threads.append(thread)

# 等待所有线程执行完毕
for thread in threads:
thread.join()

多进程并发处理任务:使用multiprocessing模块创建多个进程,每个进程负责处理一个任务。

import multiprocessing

def process_task(task):
# 处理任务
print(f"Processing task: {task}")

# 任务列表
tasks = ['task1', 'task2', 'task3']

# 创建多个进程处理任务
processes = []
for task in tasks:
process = multiprocessing.Process(target=process_task, args=(task,))
process.start()
processes.append(process)

# 等待所有进程执行完毕
for process in processes:
process.join()

异步并发执行网络请求: 使用asyncio模块和aiohttp库进行异步编程,实现并发执行多个网络请求。

import asyncio
import aiohttp

async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()

# 网络请求链接列表
urls = [
'http://example.com/page1',
'http://example.com/page2',
'http://example.com/page3'
]

async def main():
tasks = [fetch(url) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(result)

asyncio.run(main())

在Python中,可以使用multiprocessing.Pool类来自建进程池,以实现并发执行多个任务的目的。进程池可以提高任务的执行效率,减少创建和销毁进程的开销。 下面是一个使用multiprocessing.Pool自建进程池的示例:

import multiprocessing

def process_task(task):
# 处理任务
print(f"Processing task: {task}")

# 创建进程池,指定进程数量
pool = multiprocessing.Pool(processes=4)

# 任务列表
tasks = ['task1', 'task2', 'task3', 'task4', 'task5']

# 使用进程池执行任务
pool.map(process_task, tasks)

# 关闭进程池,阻止新的任务提交
pool.close()

# 等待所有任务执行完毕
pool.join()

在上述示例中,我们首先创建了一个进程池对象pool,通过指定processes参数来设置进程的数量。然后,我们定义了一个任务处理函数process_task,该函数用于处理每个任务。 接着,我们创建了一个任务列表tasks,其中包含了需要处理的多个任务。使用pool.map()方法,我们将任务列表和任务处理函数作为参数传递给进程池,进程池会自动将任务分配给空闲的进程进行处理。 最后,我们关闭进程池并调用pool.join()方法,以等待所有任务执行完毕。 需要注意的是,进程池在执行任务时会自动管理进程的创建和销毁,因此不需要手动创建和销毁进程。进程池内部会维护一个进程队列,根据任务的数量和进程池的大小来动态分配任务给进程

concurrent.futures是Python标准库中的一个模块,提供了高级的并发编程接口,用于管理并发任务的执行和结果的获取。它建立在threading和multiprocessing模块之上,提供了线程池和进程池的实现。 concurrent.futures模块主要包含以下两个类: 1. ThreadPoolExecutor:线程池执行器,用于管理线程池并发执行任务。 2. ProcessPoolExecutor:进程池执行器,用于管理进程池并发执行任务。 这两个执行器类都实现了Executor接口,提供了一系列方法来提交任务、获取结果、关闭执行器等。 下面是一个使用concurrent.futures模块的示例:

from concurrent.futures import ThreadPoolExecutor, as_completed
from concurrent import futures

def process_task(task):
# 处理任务
print(f"Processing task: {task}")
return f"Result: {task}"

# 创建线程池执行器,指定线程数量
with ThreadPoolExecutor(max_workers=4) as executor:
# 任务列表
tasks = ['task1', 'task2', 'task3', 'task4', 'task5']

# 提交任务到线程池
futures = [executor.submit(process_task, task) for task in tasks]

# 获取任务结果
for future in as_completed(futures):
result = future.result()
print(result)

下面是一个使用ThreadPoolExecutor来并发下载文件的示例代码:

import concurrent.futures
import requests

def download_file(url):
response = requests.get(url)
filename = url.split('/')[-1]
with open(filename, 'wb') as file:
file.write(response.content)
print(f"Downloaded file: {filename}")

# 文件下载链接列表
urls = [
'http://example.com/file1.txt',
'http://example.com/file2.txt',
'http://example.com/file3.txt'
]

# 创建线程池执行器,指定线程数量
with concurrent.futures.ThreadPoolExecutor() as executor:
# 提交下载任务到线程池
futures = [executor.submit(download_file, url) for url in urls]

# 获取任务结果
for future in concurrent.futures.as_completed(futures):
result = future.result()

在上述示例中,我们首先定义了一个download_file函数,用于下载指定URL的文件。该函数使用requests库发送HTTP请求并将响应内容写入本地文件。 然后,我们创建了一个文件下载链接列表urls,其中包含了需要下载的文件的URL。 接下来,我们创建了一个线程池执行器executor,并使用executor.submit()方法将下载任务提交给线程池执行器。submit()方法返回一个Future对象,表示任务的执行结果。 最后,我们使用concurrent.futures.as_completed()函数来迭代Future对象,获取下载任务的结果。as_completed()函数会返回一个迭代器,按照任务的完成顺序返回Future对象。我们通过调用future.result()方法获取每个任务的结果。 需要注意的是,在使用ThreadPoolExecutor时,我们使用了with语句来自动管理执行器的创建和关闭。在with代码块中,我们可以提交任务、获取结果等操作。执行器会在代码块结束时自动关闭,释放资源。

Python asyncio(异步I/O)是一种基于事件循环的异步编程库,用于编写高效的并发代码。它提供了一种协程(coroutine)的方式,使得编写异步代码更加简洁和可读。 下面是一个简单的示例,展示如何使用asyncio来执行异步任务:

import asyncio

async def hello():
print("Hello")
await asyncio.sleep(1)
print("World")

async def main():
await asyncio.gather(
hello(),
hello(),
hello()
)

asyncio.run(main())

在上述示例中,我们定义了一个hello协程函数,其中包含了一个异步的打印任务和一个异步的等待任务。通过使用await关键字,我们可以在协程中等待其他协程的完成。 然后,我们定义了一个main协程函数,它使用asyncio.gather函数来并发执行多个协程任务。 最后,我们使用asyncio.run函数来运行main协程函数,从而启动整个异步程序。 需要注意的是,asyncio在Python 3.7及以上版本中是一个内置的标准库,可以直接使用。在旧版本的Python中,你可能需要通过pip来安装asyncio库。

21.2.可异步调用对象
在Python中,可以使用asyncio.ensure_future或asyncio.create_task来将可调用对象转换为可异步调用的对象。这样可以在异步程序中并发地执行多个可调用对象。 下面是一个示例代码,展示如何使用asyncio.ensure_future来异步调用可调用对象:

import asyncio

async def hello():
print("Hello")

async def world():
print("World")

async def main():
task1 = asyncio.ensure_future(hello())
task2 = asyncio.ensure_future(world())
await asyncio.gather(task1, task2)

asyncio.run(main())

在上述示例中,我们定义了两个异步函数hello和world,它们分别打印”Hello”和”World”。 然后,在main函数中,我们使用asyncio.ensure_future将这两个异步函数转换为可异步调用的对象task1和task2。 最后,我们使用asyncio.gather来并发地执行这两个任务。 另外,从Python 3.7开始,可以使用asyncio.create_task来实现相同的效果:

import asyncio

async def hello():
print("Hello")

async def world():
print("World")

async def main():
task1 = asyncio.create_task(hello())
task2 = asyncio.create_task(world())
await asyncio.gather(task1, task2)

asyncio.run(main())

这两种方法都可以将可调用对象转换为可异步调用的对象,以便在异步程序中并发地执行多个任务。

在Python中,从Python 3.7开始,我们可以使用asyncio库来创建异步上下文管理器。异步上下文管理器是一种特殊的对象,它可以在异步代码中使用async with语法来管理资源的获取和释放。 下面是一个示例代码,展示如何创建和使用异步上下文管理器:

import asyncio

class AsyncContextManager:
async def __aenter__(self):
print("Entering async context")
await asyncio.sleep(1)
return "Resource"

async def __aexit__(self, exc_type, exc_val, exc_tb):
print("Exiting async context")
await asyncio.sleep(1)

async def main():
async with AsyncContextManager() as resource:
print(f"Using resource: {resource}")
await asyncio.sleep(2)

asyncio.run(main())

在上述示例中,我们定义了一个AsyncContextManager类,它实现了aenteraexit方法。这两个方法分别在进入和退出异步上下文时被调用。 在aenter方法中,我们可以执行一些异步操作来获取资源。在这个示例中,我们使用await asyncio.sleep(1)模拟获取资源的耗时操作,并返回一个表示资源的字符串。 在aexit方法中,我们可以执行一些异步操作来释放资源。在这个示例中,我们同样使用await asyncio.sleep(1)模拟释放资源的耗时操作。 然后,在main协程函数中,我们使用async with语法来使用异步上下文管理器。在进入上下文时,会调用aenter方法,获取资源并将其赋值给resource变量。然后,在退出上下文时,会调用aexit方法,释放资源。 需要注意的是,这个示例使用asyncio.run来运行main协程函数,从而启动整个异步程序。

在Python中,从Python 3.6开始,我们可以使用async for语法来进行异步迭代,以及使用异步可迭代对象来支持异步迭代操作。 异步迭代是指在迭代过程中可以暂停和恢复执行,以便在等待异步操作完成时不阻塞事件循环。 下面是一个示例代码,展示如何进行异步迭代和使用异步可迭代对象:

import asyncio

class AsyncIterable:
def __init__(self, data):
self.data = data

def __aiter__(self):
return self

async def __anext__(self):
if not self.data:
raise StopAsyncIteration
await asyncio.sleep(1) # 模拟异步操作
item = self.data.pop(0)
return item

async def main():
async for item in AsyncIterable([1, 2, 3]):
print(item)
await asyncio.sleep(1)

asyncio.run(main())

在上述示例中,我们定义了一个AsyncIterable类,它实现了aiteranext方法。aiter方法返回一个异步迭代器对象,而anext方法定义了异步迭代的行为。 在anext方法中,我们使用await asyncio.sleep(1)模拟异步操作的等待时间。然后,我们从数据列表中取出一个元素并返回。 然后,在main协程函数中,我们使用async for语法来进行异步迭代。在每次迭代时,会调用anext方法来获取下一个元素,并在等待异步操作完成时暂停执行。 需要注意的是,这个示例使用asyncio.run来运行main协程函数,从而启动整个异步程序。

异步对象的类型提示
在Python中,可以使用类型提示来指定异步对象的类型。从Python 3.5开始,引入了typing模块,其中包含了一些用于异步编程的类型提示工具。 下面是一些常用的用于异步对象类型提示的工具: 1. typing.Coroutine: 用于指定协程函数的返回类型。 2. typing.Awaitable: 用于指定一个对象是可等待的,可以使用await关键字来等待其完成。 3. typing.AsyncIterable: 用于指定异步可迭代对象的类型。 4. typing.AsyncIterator: 用于指定异步迭代器的类型。 5. typing.AsyncContextManager: 用于指定异步上下文管理器的类型。 下面是一个示例代码,展示如何使用这些类型提示工具:

import asyncio
from typing import Coroutine, Awaitable, AsyncIterable, AsyncIterator, AsyncContextManager

async def foo() -> Coroutine:
await asyncio.sleep(1)
return 42

async def bar() -> Awaitable[int]:
await asyncio.sleep(1)
return 42

async def baz() -> AsyncIterable[int]:
for i in range(5):
yield i
await asyncio.sleep(1)

async def qux() -> AsyncIterator[int]:
for i in range(5):
yield i
await asyncio.sleep(1)

async def spam() -> AsyncContextManager[str]:
async with open("file.txt") as file:
content = await file.read()
return content

asyncio.run(foo())
asyncio.run(bar())
asyncio.run(baz())
asyncio.run(qux())
asyncio.run(spam())

在上述示例中,我们定义了几个异步函数,每个函数使用不同的类型提示来指定返回类型。 在foo函数中,我们使用Coroutine类型提示来指定返回的协程对象的类型。 在bar函数中,我们使用Awaitable类型提示来指定返回的对象是可等待的。 在baz函数中,我们使用AsyncIterable类型提示来指定返回的对象是异步可迭代的。 在qux函数中,我们使用AsyncIterator类型提示来指定返回的对象是异步迭代器。 在spam函数中,我们使用AsyncContextManager类型提示来指定返回的对象是异步上下文管理器。 需要注意的是,这个示例使用asyncio.run来运行每个异步函数,从而启动相应的异步程序。

使用动态属性访问json数据

在Python中,可以使用动态属性来访问JSON数据。动态属性允许我们在对象上创建或修改属性,从而实现对JSON数据的灵活访问。 下面是一个示例代码,展示如何使用动态属性访问JSON数据:

import json

class JSONData:
def __init__(self, json_str):
self.data = json.loads(json_str)

def __getattr__(self, name):
if name in self.data:
value = self.data[name]
if isinstance(value, dict):
return JSONData(json.dumps(value))
else:
return value
else:
raise AttributeError(f"'JSONData' object has no attribute '{name}'")

# 使用动态属性访问JSON数据
json_str = '{"name": "John", "age": 30, "address": {"city": "New York", "country": "USA"}}'
data = JSONData(json_str)

print(data.name) # 输出: John
print(data.age) # 输出: 30
print(data.address) # 输出: <__main__.JSONData object at 0x...>
print(data.address.city) # 输出: New York
print(data.address.country) # 输出: USA

后面的略

文章作者: Met Guo
文章链接: https://guoyujian.github.io/2024/03/08/Fluent-Python%E7%AC%94%E8%AE%B0%E6%8C%81%E7%BB%AD%E6%95%B4%E7%90%86/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Gmet's Blog