Python特殊方法

定义

Python 中的特殊方法(也称为“魔术方法”或“dunder 方法”,因为它们的名称前后有两个下划线,如 __init__)是 Python 类的一部分,用来定义对象的某些行为。这些方法在特定情况下被自动调用。它们可以帮助我们实现一些内置功能,比如运算符重载、对象创建、字符串表示等。

常见的特殊方法

Python 中的魔术方法可以按照不同的类别进行划分,根据它们的功能和触发场景。下面将这些方法分为几大类,并逐一介绍:

对象的创建与销毁

  • __new__(cls, ...)作用:控制对象的创建过程,在对象创建之前被调用。通常不需要重写,除非需要自定义对象的创建逻辑。__new__ 返回类的实例。

  • __init__(self, ...)作用:对象初始化方法,创建对象后立即调用,用于设置对象属性。

  • __del__(self):作用:对象销毁方法,当对象被垃圾回收时调用。可以用来执行清理操作,但不常使用。(在GC之前,Python会调用这个对象的__del__()方法完成一些终止化工作。如果没有__del__()方法,那么Python不做特殊的处理。)

对象表示

  • __str__(self)

    • 作用:定义对象的用户友好字符串表示,当调用 print()str() 时被触发。
  • __repr__(self)

    • 作用:定义对象的官方字符串表示,通常用于调试。通过 repr() 或在解释器中直接调用对象时触发。
  • __format__(self, format_spec)

    • 作用:定义对象的格式化输出,当使用 format() 函数或 f-string 时调用。
  • __bytes__(self)

    • 作用:定义对象的字节表示,当调用 bytes() 函数时触发。

属性访问与管理

__getattr__(self, name)

  • 作用:当你试图访问的属性不存在时,__getattr__ 会被调用。它可以用于提供默认属性值或动态生成属性。

  • 注意__getattr__ 只在属性不存在时才被调用。如果属性存在,Python 会直接访问它,而不会调用 __getattr__

示例:

1
2
3
4
5
6
7
8
9
10
11
class MyClass:
def __init__(self):
self.existing_attr = "I exist"

def __getattr__(self, name):
# 当属性不存在时,返回默认消息
return f"Attribute '{name}' not found!"

obj = MyClass()
print(obj.existing_attr) # 输出: I exist
print(obj.non_existing_attr) # 输出: Attribute 'non_existing_attr' not found!

__getattribute__(self, name)

  • 作用__getattribute__ 是更底层的属性访问控制方法。无论属性是否存在,每次访问对象属性时都会调用它。一般不重写。

  • 注意:由于 __getattribute__ 在所有属性访问时都会触发,因此使用时要非常小心,以避免无限递归(需要在函数内使用 super()object.__getattribute__() 来访问属性)。

示例:

1
2
3
4
5
6
7
8
9
10
class MyClass:
def __init__(self):
self.existing_attr = "I exist"

def __getattribute__(self, name):
print(f"Trying to access {name}")
return super().__getattribute__(name) # 正常返回属性值

obj = MyClass()
print(obj.existing_attr) # 输出: Trying to access existing_attr \n I exist

在这个例子中,每次我们尝试访问属性时,都会打印出属性名称,并且通过 super().__getattribute__(name) 确保正常返回属性值。

__setattr__(self, name, value)

  • 作用:每当你尝试设置对象的属性值时,__setattr__ 会被调用。你可以使用它来控制属性赋值行为,比如验证、修改或记录赋值操作。

  • 注意:为了避免无限递归,在 __setattr__ 内部你需要使用 super().__setattr__(name, value)object.__setattr__(self, name, value) 来真正设置属性值,而不能直接使用 self.name = value,否则会再次调用 __setattr__

示例:

1
2
3
4
5
6
7
8
9
10
11
class MyClass:
def __init__(self):
self.existing_attr = "I exist" # __setattr__ 会被调用

def __setattr__(self, name, value):
print(f"Setting {name} to {value}")
super().__setattr__(name, value) # 使用父类的 setattr 赋值

obj = MyClass()
obj.new_attr = "Hello" # 输出: Setting new_attr to Hello
print(obj.new_attr) # 输出: Hello

__delattr__(self, name)

  • 作用:当你使用 del 删除对象的属性时,__delattr__ 会被调用。你可以控制是否允许删除某些属性或在删除时执行一些操作。

  • 注意:与 __setattr__ 类似,要避免递归调用,你需要使用 super().__delattr__(name) 来删除属性,而不是直接 del self.name

示例:

1
2
3
4
5
6
7
8
9
10
11
12
class MyClass:
def __init__(self):
self.existing_attr = "I exist"

def __delattr__(self, name):
print(f"Deleting {name}")
super().__delattr__(name) # 使用父类的 delattr 来真正删除属性

obj = MyClass()
del obj.existing_attr # 输出: Deleting existing_attr
# 尝试访问会抛出 AttributeError,因为属性已被删除
# print(obj.existing_attr)

在这个例子中,当我们删除属性 existing_attr 时,__delattr__ 会打印出属性名称,并通过 super() 调用父类方法实际删除属性。

完整示例:属性访问控制器

结合 __getattr____setattr____delattr__,我们可以实现一个属性访问控制器,动态生成属性并限制某些属性的删除。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class MyClass:
def __init__(self):
self.existing_attr = "I exist"

def __getattr__(self, name):
# 动态生成不存在的属性
return f"Attribute '{name}' not found, dynamically generated value!"

def __setattr__(self, name, value):
print(f"Setting {name} to {value}")
super().__setattr__(name, value)

def __delattr__(self, name):
if name == 'protected_attr':
print(f"Cannot delete {name}, it is protected!")
else:
print(f"Deleting {name}")
super().__delattr__(name)

obj = MyClass()

# 动态访问不存在的属性
print(obj.some_attr) # 输出: Attribute 'some_attr' not found, dynamically generated value!

# 设置新属性
obj.new_attr = "New value" # 输出: Setting new_attr to New value

# 删除属性
del obj.new_attr # 输出: Deleting new_attr

# 尝试删除受保护的属性
obj.protected_attr = "Can't delete me"
del obj.protected_attr # 输出: Cannot delete protected_attr, it is protected!

property()@property 语法糖

除了上述魔术方法,Python 还提供了内置的 property() 函数和 @property 装饰器,用于简化属性访问和管理。这允许我们定义“getter”、“setter”和“deleter”方法。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MyClass:
def __init__(self):
self._value = 0

@property
def value(self):
return self._value

@value.setter
def value(self, new_value):
print(f"Setting value to {new_value}")
self._value = new_value

@value.deleter
def value(self):
print("Deleting value")
del self._value

obj = MyClass()
obj.value = 10 # 输出: Setting value to 10
print(obj.value) # 输出: 10
del obj.value # 输出: Deleting value

容器行为(列表、字典等)

在 Python 中,容器类(如列表、字典等)的行为可以通过魔术方法来定制。通过实现这些魔术方法,可以让自定义对象表现得像标准容器一样,支持索引访问、迭代、元素添加和删除等操作。

下面将详细介绍 Python 中与容器行为相关的魔术方法,并附上代码示例。

__len__(self)

  • 作用:定义容器的长度,当使用 len() 函数时会调用 __len__,它返回容器中元素的个数。

示例:

1
2
3
4
5
6
7
8
9
class MyContainer:
def __init__(self, data):
self.data = data

def __len__(self):
return len(self.data) # 返回容器的长度

obj = MyContainer([1, 2, 3, 4])
print(len(obj)) # 输出: 4

__getitem__(self, key)

  • 作用:定义通过键或索引获取元素的行为。可以让对象表现得像列表、字典或其他可索引的容器。该方法是实现切片的关键。

示例:

1
2
3
4
5
6
7
8
9
class MyContainer:
def __init__(self, data):
self.data = data

def __getitem__(self, key):
return self.data[key] # 根据索引或键返回元素

obj = MyContainer([10, 20, 30, 40])
print(obj[2]) # 输出: 30

在这个例子中,__getitem__ 使得 MyContainer 对象可以像列表一样通过索引访问元素。

__setitem__(self, key, value)

  • 作用:定义通过键或索引设置元素的行为。允许对象像列表或字典一样修改内部数据。

示例:

1
2
3
4
5
6
7
8
9
10
class MyContainer:
def __init__(self, data):
self.data = data

def __setitem__(self, key, value):
self.data[key] = value # 根据索引或键设置元素值

obj = MyContainer([10, 20, 30, 40])
obj[1] = 99 # 将索引 1 位置的值改为 99
print(obj.data) # 输出: [10, 99, 30, 40]

在这个例子中,__setitem__ 允许通过索引修改容器中的值。

__delitem__(self, key)

  • 作用:定义通过键或索引删除元素的行为。让对象可以像列表或字典一样删除元素。

示例:

1
2
3
4
5
6
7
8
9
10
class MyContainer:
def __init__(self, data):
self.data = data

def __delitem__(self, key):
del self.data[key] # 根据索引或键删除元素

obj = MyContainer([10, 20, 30, 40])
del obj[2] # 删除索引 2 位置的元素
print(obj.data) # 输出: [10, 20, 40]

在这个例子中,__delitem__ 允许通过索引删除容器中的元素。

__iter__(self)__next__(self)

  • 作用:定义对象的迭代行为。通过实现 __iter__()__next__(),可以让对象像列表一样进行迭代。__iter__() 返回一个迭代器对象,__next__() 返回容器的下一个元素。

  • 注意:通常 __iter__ 返回的是对象本身,而 __next__ 负责返回每个元素,迭代结束时抛出 StopIteration

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyContainer:
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 # 迭代结束时抛出 StopIteration 异常

obj = MyContainer([10, 20, 30, 40])

for item in obj:
print(item)

输出:

1
2
3
4
10
20
30
40

在这个例子中,__iter____next__ 方法使 MyContainer 支持迭代器协议,允许我们使用 for 循环遍历对象。

__contains__(self, item)

  • 作用:定义 in 运算符的行为,用于检查某个元素是否在容器中。__contains__ 返回布尔值 TrueFalse

示例

1
2
3
4
5
6
7
8
9
10
class MyContainer:
def __init__(self, data):
self.data = data

def __contains__(self, item):
return item in self.data # 检查元素是否存在于容器中

obj = MyContainer([10, 20, 30, 40])
print(20 in obj) # 输出: True
print(50 in obj) # 输出: False

在这个例子中,__contains__ 使得我们可以使用 in 运算符来检查 MyContainer 对象中是否包含某个元素。

__reversed__(self)

  • 作用:定义当调用 reversed() 函数时的行为,使容器能够以相反顺序进行迭代。

示例:

1
2
3
4
5
6
7
8
9
10
11
class MyContainer:
def __init__(self, data):
self.data = data

def __reversed__(self):
return reversed(self.data) # 反向迭代

obj = MyContainer([10, 20, 30, 40])

for item in reversed(obj):
print(item)

输出:

1
2
3
4
40
30
20
10

在这个例子中,__reversed__ 允许我们使用 reversed() 函数以相反顺序迭代 MyContainer 对象中的元素。

运算符重载

Python 中的运算符可以通过魔术方法进行重载,以便在类实例上执行这些运算符操作。常见的运算符及其对应的魔术方法包括:

  • 算术运算符

    • **__add__(self, other)**:+ 运算符
    • **__sub__(self, other)**:- 运算符
    • **__mul__(self, other)**:* 运算符
    • **__truediv__(self, other)**:/ 运算符
    • **__floordiv__(self, other)**:// 运算符
    • **__mod__(self, other)**:% 运算符
    • **__pow__(self, other)**:** 运算符
  • 比较运算符

    • **__eq__(self, other)**:==
    • **__ne__(self, other)**:!=
    • **__lt__(self, other)**:<
    • **__le__(self, other)**:<=
    • **__gt__(self, other)**:>
    • **__ge__(self, other)**:>=
  • 位运算符

    • **__and__(self, other)**:&
    • **__or__(self, other)**:|
    • **__xor__(self, other)**:^
    • **__lshift__(self, other)**:<<
    • **__rshift__(self, other)**:>>
  • 一元运算符

    • **__neg__(self)**:- (取负)
    • **__pos__(self)**:+ (正号)
    • **__invert__(self)**:~ (按位取反)

上下文管理

  • __enter__(self) 和 **__exit__(self, exc_type, exc_val, exc_tb)**:

    • 作用:实现上下文管理协议,使对象可以与 with 语句一起使用。__enter__ 在进入上下文时调用,__exit__ 在退出上下文时调用。
    1
    2
    3
    4
    5
    6
    7
    class MyContext:
    def __enter__(self):
    print("Entering context")
    def __exit__(self, exc_type, exc_value, traceback):
    print("Exiting context")
    with MyContext():
    print("Inside the context")

调用与调用相关

  • **__call__(self, ...)**:

    • 作用:使对象可以像函数一样被调用。
    1
    2
    3
    4
    5
    class CallableClass:
    def __call__(self, x):
    return x * 2
    obj = CallableClass()
    print(obj(5)) # 输出: 10

反射操作

  • **__getattr__(self, name)**:

    • 作用:当访问不存在的属性时被调用。可用于动态属性或提供默认值。
  • **__setattr__(self, name, value)**:

    • 作用:设置属性值时调用。

哈希与相等性

  • **__hash__(self)**:

    • 作用:定义对象的哈希值,使对象可用于集合、字典的键。
  • **__eq__(self, other)**:

    • 作用:定义相等性运算 (==)。
  • **__ne__(self, other)**:

    • 作用:定义不等性运算 (!=)。

辨析

__new____init__

__new__ 方法

  • 作用__new__ 是一个静态方法,用于创建对象。在对象创建时,它负责分配内存并返回新实例。它是在类被调用时最先执行的方法,在 __init__ 之前被调用。

  • 参数__new__(cls, ...),其中 cls 是当前类的引用,后面的参数是传递给类构造函数的参数。

  • 返回值__new__ 必须返回类的一个实例,通常通过 super().__new__(cls) 来实现,表示用父类的 __new__ 方法创建对象。

适用场景:

  • 当你需要控制对象的创建过程时,例如实现单例模式(确保某个类只创建一个实例)或子类化不可变类型(如 intstrtuple)时,可以使用 __new__

示例:

1
2
3
4
5
6
7
8
9
10
11
class MyClass:
def __new__(cls, *args, **kwargs):
print("Creating instance...")
instance = super().__new__(cls) # 分配内存,创建对象
return instance

def __init__(self, name):
print("Initializing instance...")
self.name = name

obj = MyClass("Alice")

输出:

1
2
Creating instance...
Initializing instance...

在这个例子中,__new__ 在对象创建时被调用,用于分配内存,而 __init__ 随后负责初始化对象属性。

__init__ 方法

  • 作用__init__ 是用于初始化对象的。它在对象已经被 __new__ 方法创建并分配内存之后调用,负责为对象设置初始状态(比如为实例变量赋值)。

  • 参数__init__(self, ...),其中 self 是当前对象的实例,后面的参数是传递给类构造函数的参数。

  • 返回值__init__ 没有返回值,因为它仅负责初始化对象。

示例:

1
2
3
4
5
6
class MyClass:
def __init__(self, name):
print("Initializing instance...")
self.name = name

obj = MyClass("Alice")

输出:

1
Initializing instance...

这里 __init__ 接受对象实例 self 作为第一个参数,用来设置对象的初始属性。

__new____init__ 的关系

  • __new__ 负责创建对象,在实例化过程中最先执行
  • __init__ 负责初始化对象,在 __new__ 之后执行,设置对象的初始状态。

它们在对象实例化过程中的执行顺序如下:

  1. 调用 __new__ 来创建对象,分配内存。
  2. 如果 __new__ 返回的对象是当前类的实例,Python 会自动调用 __init__,对该对象进行初始化。

在子类中的使用

有时,在子类中需要重写 __new____init__ 方法。例如,当我们继承不可变类型(如 inttuple)时,通常需要自定义 __new__,因为这些类型的实例在创建之后无法修改。

示例:子类化不可变类型 int

1
2
3
4
5
6
7
8
9
10
11
class MyInt(int):
def __new__(cls, value):
print("Calling __new__")
return super().__new__(cls, value)

def __init__(self, value):
print("Calling __init__")
self.value = value

obj = MyInt(10)
print(obj)

输出:

1
2
3
Calling __new__
Calling __init__
10

在这个例子中,MyInt 继承了不可变类型 int,需要在 __new__ 中控制对象的创建,因为 int 的实例一旦创建就不能改变。而 __init__ 仅用于添加额外的属性。

__new__ 的典型使用场景:单例模式

__new__ 常用于实现单例模式,即保证一个类只能创建一个实例。

示例:单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Singleton:
_instance = None

def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance

def __init__(self, name):
self.name = name

obj1 = Singleton("Alice")
obj2 = Singleton("Bob")

print(obj1 is obj2) # 输出: True
print(obj1.name) # 输出: Bob
print(obj2.name) # 输出: Bob

在这个例子中,__new__ 保证 Singleton 类只能有一个实例,即使多次调用类构造函数,返回的都是同一个实例。

__str__和__repr__

__str____repr__ 是 Python 中两个非常重要的魔术方法,用于定义对象的字符串表示形式。虽然它们都负责返回对象的字符串表示,但它们有不同的使用场景和目的。

__str__(self)

  • 作用__str__ 定义了对象的用户友好的字符串表示。当你使用 str() 函数或 print() 打印对象时,调用的是 __str__ 方法。它的目的是返回一个易于阅读的字符串,供用户直接使用。
  • 典型用途:输出对象的简明描述,主要供人类阅读。

__repr__(self)

  • 作用__repr__ 定义了对象的官方字符串表示,即面向开发者的表示形式。repr() 函数会调用 __repr__ 方法,通常期望返回一个尽可能精确完整的字符串,能够明确地表示这个对象,并且在某些情况下能够用于重新创建对象(eval(repr(obj)) == obj 这种方式)。

  • 典型用途:调试和日志记录时,开发者用来查看对象的详细信息,便于追踪程序行为。

示例:

1
2
3
4
5
6
7
8
9
10
class MyClass:
def __init__(self, name, age):
self.name = name
self.age = age

def __repr__(self):
return f"MyClass(name='{self.name}', age={self.age})"

obj = MyClass("Alice", 30)
print(repr(obj)) # 输出: MyClass(name='Alice', age=30)

在这个例子中,__repr__ 返回一个精确的、带有完整信息的字符串,这个字符串尽量能够帮助开发者了解对象的内部状态。

注意:如果一个类没有实现 __str__,那么当你使用 print()str() 函数时,Python 会退而调用 __repr__

如果一个类这两个方法都没有实现,那么当调用 print()str() 时,Python 会使用该类的默认实现。这种默认行为由 object 类提供,返回一个包含类名和内存地址的字符串,表示对象的唯一标识符。

示例

1
2
3
4
5
6
7
8
class MyClass:
def __init__(self, name, age):
self.name = name
self.age = age

obj = MyClass("Alice", 30)
print(obj)
# 输出 <__main__.MyClass object at 0x7f83b2c4d880>

解释

  • __main__: 表示该类定义在当前模块(也就是主程序模块)中。如果类定义在另一个模块中,这部分会显示模块名。
  • MyClass: 是类的名称。
  • 0x7f83b2c4d880: 是该对象在内存中的地址(这是十六进制的内存地址,表示对象的唯一位置)。

拓展

迭代器

在 Python 中,迭代是一种遍历容器(如列表、元组、字典、集合等)元素的方式。迭代器提供了一种访问容器元素的机制,避免显式使用索引。要理解迭代的工作原理,我们需要了解几个核心概念,包括迭代器(iterator)、可迭代对象(iterable)、__iter____next__ 方法。

可迭代对象(Iterable)

  • 定义:一个对象是可迭代的,如果它实现了 __iter__() 方法,返回一个迭代器,或者它定义了一个 __getitem__() 方法(支持通过索引访问)。
  • 示例:列表、元组、字符串、字典等都是可迭代对象。

可迭代对象可以使用 for 循环进行迭代,或者通过 iter() 函数显式地获得它的迭代器。

示例:

1
2
3
4
5
6
7
8
9
my_list = [1, 2, 3]
for item in my_list:
print(item)

# 或者显式使用 iter()
it = iter(my_list)
print(next(it)) # 输出: 1
print(next(it)) # 输出: 2
print(next(it)) # 输出: 3

迭代器(Iterator)

  • 定义:一个对象是迭代器,如果它实现了 __iter__() 方法并返回自身,以及实现了 __next__() 方法。__next__() 方法返回下一个元素,当没有元素时抛出 StopIteration 异常。
  • 特点:迭代器是一次性可消耗的,即迭代器只能遍历一次,遍历完就无法重新开始。

示例:

1
2
3
4
5
6
7
my_list = [1, 2, 3]
iterator = iter(my_list) # 创建一个迭代器

print(next(iterator)) # 输出: 1
print(next(iterator)) # 输出: 2
print(next(iterator)) # 输出: 3
# next(iterator) # 这时调用会抛出 StopIteration 异常

__iter____next__ 魔术方法

  • **__iter__(self)**:可迭代对象和迭代器都应该实现这个方法。对于可迭代对象,__iter__() 返回一个新的迭代器对象;对于迭代器,__iter__() 通常返回 self(即迭代器本身)。

  • **__next__(self)**:这是迭代器对象实现的关键方法。它返回序列中的下一个元素,如果序列结束,则抛出 StopIteration 异常。

自定义迭代器示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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):
item = self.data[self.index]
self.index += 1
return item
else:
raise StopIteration

my_iter = MyIterator([1, 2, 3])
for item in my_iter:
print(item)

输出:

1
2
3
1
2
3

迭代的执行流程

  • 当一个对象被用于 for 循环时,Python 会在后台调用该对象的 __iter__() 方法,以获取一个迭代器。
  • 然后,循环会反复调用迭代器的 __next__() 方法,直到抛出 StopIteration 异常为止。

执行过程的分解:

1
2
3
4
5
6
7
8
9
my_list = [1, 2, 3]
iterator = iter(my_list) # 获取迭代器

while True:
try:
item = next(iterator)
print(item)
except StopIteration:
break

生成器(Generators)

生成器是创建迭代器的一种简洁方式。它通过 yield 关键字逐步返回值,每次迭代时暂停函数的执行状态,保存上下文,以便下次继续。

  • 定义生成器函数:生成器函数与普通函数的区别在于它使用了 yield 关键字,而不是 return
  • 生成器对象:调用生成器函数返回一个生成器对象,生成器对象是一个迭代器。

示例:

1
2
3
4
5
6
7
8
9
def my_generator():
yield 1
yield 2
yield 3

gen = my_generator()

for value in gen:
print(value)

输出:

1
2
3
1
2
3

生成器的执行过程:

生成器函数在每次调用 next() 时会暂停在 yield 语句,保存当前的状态,并在下一次调用时从该状态继续执行。

1
2
3
4
5
6
gen = my_generator()

print(next(gen)) # 输出: 1
print(next(gen)) # 输出: 2
print(next(gen)) # 输出: 3
# next(gen) # 这时调用会抛出 StopIteration 异常

iter() 函数与 next() 函数

  • **iter(object)**:返回对象的迭代器。该函数首先检查对象是否实现了 __iter__ 方法,如果没有,则检查是否实现了 __getitem__,以支持通过索引访问。
  • **next(iterator, default)**:调用迭代器的 __next__() 方法返回下一个元素。如果迭代结束,并且提供了 default 参数,则返回 default,否则抛出 StopIteration

示例:

1
2
3
4
5
6
7
my_list = [1, 2, 3]
iterator = iter(my_list)

print(next(iterator)) # 输出: 1
print(next(iterator)) # 输出: 2
print(next(iterator, "End of list")) # 输出: 3
print(next(iterator, "End of list")) # 输出: End of list

可迭代对象和迭代器的区别

  • 可迭代对象:实现了 __iter__() 方法,可以返回一个迭代器,但它自身不一定是迭代器。
  • 迭代器:实现了 __iter__()__next__() 方法,且 __iter__() 通常返回自身,__next__() 负责返回序列中的下一个值。

可迭代对象和迭代器的主要区别在于,迭代器一次性消耗,即遍历完后不能再次迭代;而可迭代对象每次调用 __iter__ 方法都会返回一个新的迭代器。

惰性迭代

迭代器和生成器的一个重要特性是惰性迭代,即它们不会一次性生成所有元素,而是按需逐个生成。当我们需要处理非常大的数据集时,这种特性非常有用。

示例:生成无限序列

1
2
3
4
5
6
7
8
9
10
def count(start=0):
while True:
yield start
start += 1

counter = count()

print(next(counter)) # 输出: 0
print(next(counter)) # 输出: 1
print(next(counter)) # 输出: 2

生成器 count() 将一直生成数字,直到被手动终止。