Chapter5 Behavioral Design Pattern
1、什么是状态机,给出概念和举例说明
状态机(State Machine)是一种行为模型,用于描述一个实体(如对象、系统或过程)在不同状态之间的转换。状态机的核心思想是,实体在任何时刻都处于一个特定的状态,并且根据输入事件或条件,从一个状态转换到另一个状态。
状态机广泛应用于计算机科学、工程学和许多其他领域,用于建模和控制复杂的行为和流程。
状态机的基本组成部分
-
状态(State):
-
状态是实体在某一时刻的行为模式或条件。状态可以是初始状态、中间状态或最终状态。
-
例如,一个交通信号灯的状态可以是“红灯”、“黄灯”或“绿灯”。
-
-
事件(Event):
-
事件是触发状态转换的外部输入或条件。事件可以是用户操作、系统信号或其他任何触发条件。
-
例如,用户按下按钮、系统收到一个信号或时间到达某个点。
-
-
转换(Transition):
-
转换是从一个状态到另一个状态的路径。每个转换通常由一个事件触发,并且可能伴随着一些动作(如执行某些操作)。
-
例如,从“红灯”状态转换到“绿灯”状态,可能由一个定时器事件触发。
-
-
动作(Action):
-
动作是在状态转换过程中执行的操作。动作可以在转换之前或之后执行。
-
例如,在从“红灯”转换到“绿灯”时,可以执行一个动作来显示绿灯。
-
状态机的工作原理
-
初始化:
-
状态机在启动时进入一个初始状态。
-
例如,交通信号灯在启动时进入“红灯”状态。
-
-
事件处理:
-
状态机等待事件的发生。
-
当事件发生时,状态机根据当前状态和事件,决定是否触发状态转换。
-
例如,当定时器事件发生时,交通信号灯从“红灯”状态转换到“绿灯”状态。
-
-
状态转换:
-
如果事件触发了状态转换,状态机从当前状态转换到目标状态。
-
在转换过程中,可以执行一些动作。
-
例如,在从“红灯”转换到“绿灯”时,可以执行一个动作来显示绿灯。
-
-
重复:
-
状态机继续等待新的事件,重复上述过程。
-
例如,交通信号灯在“绿灯”状态后,可能会转换到“黄灯”状态,然后再次转换到“红灯”状态。
-
状态机的类型
-
有限状态机(Finite State Machine, FSM):
-
有限状态机是一个具有有限个状态的状态机。
-
它是最常见的状态机类型,广泛应用于软件和硬件设计中。
-
例如,一个简单的交通信号灯系统可以使用有限状态机来建模。
-
-
状态图(Statechart):
-
状态图是一种扩展的有限状态机,支持层次结构、并发状态和历史状态。
-
它比普通的有限状态机更强大,适用于复杂系统的设计。
-
例如,一个复杂的嵌入式系统可以使用状态图来建模。
-
状态机的应用
-
软件工程:用于建模和控制软件系统的状态转换。
-
嵌入式系统:用于控制硬件设备的行为。
-
游戏开发:用于建模游戏角色的行为和状态转换。
-
网络协议:用于建模网络协议的状态转换。
-
用户界面:用于建模用户界面的状态和交互。
示例
class TrafficLight: def __init__(self): self.state = "red" def change(self): if self.state == "red": self.state = "green" print("Light is now green") elif self.state == "green": self.state = "yellow" print("Light is now yellow") elif self.state == "yellow": self.state = "red" print("Light is now red") # 使用状态机 light = TrafficLight() light.change() # Light is now green light.change() # Light is now yellow light.change() # Light is now red
在这个例子中,TrafficLight 类定义了一个简单的状态机,包含三个状态(红灯、黄灯和绿灯)和一个事件(change 方法)。
每次调用 change 方法时,状态机从当前状态转换到下一个状态,并打印当前状态。
总结
class Event: def __init__(self, name): self.name = name def __str__(self): return self.name class Widget: def __init__(self, parent=None): self.parent = parent def handle(self, event): handler = f"handle_{event}" if hasattr(self, handler): method = getattr(self, handler) method(event) elif self.parent is not None: self.parent.handle(event) elif hasattr(self, "handle_default"): self.handle_default(event) class MainWindow(Widget): def handle_close(self, event): print(f"MainWindow: {event}") def handle_default(self, event): print(f"MainWindow Default: {event}") class SendDialog(Widget): def handle_paint(self, event): print(f"SendDialog: {event}") class MsgText(Widget): def handle_down(self, event): print(f"MsgText: {event}") def main(): mw = MainWindow() sd = SendDialog(mw) msg = MsgText(sd) for e in ("down", "paint", "unhandled", "close"): evt = Event(e) print(f"Sending event -{evt}- to MainWindow") mw.handle(evt) print(f"Sending event -{evt}- to SendDialog") sd.handle(evt) print(f"Sending event -{evt}- to MsgText") msg.handle(evt) if __name__ == "__main__": main()
追问一:SendDialog(mw) 中的 mw 是传递给 SendDialog 构造函数的参数,最终赋值给 self.parent??
关键概念解析
1. 构造函数(__init__)的继承
- 规则:如果子类(如
SendDialog)没有定义自己的__init__方法,Python 会自动调用父类(如Widget)的__init__。 - 代码验证:
class Parent: def __init__(self, x): print(f"Parent __init__ called with x={x}") class Child(Parent): # 子类没有定义 __init__ pass # 创建 Child 实例时,自动调用 Parent.__init__ c = Child(10) # 输出:Parent __init__ called with x=10
即使 Child 没有显式定义构造函数,创建实例时依然会调用父类 Parent 的 __init__。
2. 示例代码中的 SendDialog
SendDialog没有定义自己的__init__,因此会继承父类Widget的__init__。- 创建
SendDialog实例时的流程:
sd = SendDialog(mw) # 触发 Widget.__init__(self, parent=mw)
这里的 mw 参数会被传递给 Widget 的 __init__ 方法,并赋值给 self.parent。
完整执行流程拆解
1. 创建 SendDialog 实例
当执行 sd = SendDialog(mw) 时:
- Python 发现
SendDialog没有定义__init__,于是调用父类Widget的__init__。 Widget的__init__方法被调用:self.parent被设置为mw(即MainWindow实例)。
2. 验证 SendDialog 的 parent 属性
可以通过代码验证:
sd = SendDialog(mw) print(sd.parent == mw) # 输出 True
sd的parent属性确实指向mw。
为什么这能工作?
1. 隐式的构造函数继承
- 由于
SendDialog没有自己的__init__,父类Widget的__init__会自动生效。 - 因此,
SendDialog(mw)中的参数mw实际上是传递给Widget.__init__的parent参数。
2. 对象组合的体现
self.parent = mw是典型的 对象组合(Object Composition),即一个对象(SendDialog实例)通过持有另一个对象(MainWindow实例)的引用来实现协作。- 这构建了责任链结构:
MsgText → SendDialog → MainWindow。
对比显式定义子类构造函数
如果 SendDialog 需要自定义构造函数,必须显式调用父类构造函数:
class SendDialog(Widget): def __init__(self, parent): super().__init__(parent) # 必须显式调用父类构造函数 # 其他自定义初始化代码 def handle_paint(self, event): ...
super().__init__(parent)确保父类Widget的初始化逻辑(如设置self.parent)被执行。
总结
- 看似“没有构造函数”:
SendDialog通过继承机制隐式使用了父类Widget的构造函数。 - 参数传递路径:
SendDialog(mw)→Widget.__init__(self, parent=mw)→self.parent = mw。 - 责任链的构建:通过
parent参数将对象链接在一起,形成事件传递的链式结构。
这种设计使得代码高度简洁——子类只需关注自己需要处理的事件(如 handle_paint),而通用逻辑(如事件传递)完全由父类 Widget 实现。
这正是面向对象编程中 继承 和 组合 协同工作的经典案例!
追问二、那这几行代码是继承还是组合?
mw = MainWindow() sd = SendDialog(mw) msg = MsgText(sd)
这几行代码同时涉及到了 继承 和 组合,但它们是不同维度的概念。
1. 继承(Inheritance)
- 定义:类之间的垂直关系(父类 → 子类),子类自动获得父类的属性和方法。
- 代码中的体现:
class MainWindow(Widget): ... # MainWindow 继承自 Widget class SendDialog(Widget): ... # SendDialog 继承自 Widget class MsgText(Widget): ... # MsgText 继承自 Widget
- 关键点:
MainWindow、SendDialog、MsgText都是Widget的子类。- 继承的目的是复用代码:例如,所有子类都自动拥有
Widget的handle()方法。
2. 组合(Composition)
- 定义:对象之间的水平关系,一个对象持有另一个对象的引用。
- 代码中的体现:
mw = MainWindow() # 创建 MainWindow 实例(链的顶端) sd = SendDialog(mw) # 创建 SendDialog 实例,并设置其 parent=mw(组合!) msg = MsgText(sd) # 创建 MsgText 实例,并设置其 parent=sd(组合!)
- 关键点:
sd的parent字段指向mw,msg的parent字段指向sd。- 组合的目的是构建责任链:形成
msg → sd → mw的事件传递链路。
3. 为什么容易混淆?
- 语法相似性:
SendDialog(mw)看起来像“继承父类”,但实际上:SendDialog继承自Widget(垂直关系)。SendDialog(mw)是将mw作为参数传递给构造函数,实现组合(水平关系)。
4. 一图胜千言
继承关系(垂直):
Widget
├── MainWindow
├── SendDialog
└── MsgText
组合关系(水平):
msg → sd → mw
5. 终极判断方法
- 继承:看类定义时的括号
class Child(Parent)。 - 组合:看对象创建时的参数
Child(parent_obj),本质是设置对象属性(如self.parent)。
6. 代码执行细节验证
以 sd = SendDialog(mw) 为例:
SendDialog没有定义自己的__init__,所以调用父类Widget的__init__。Widget的构造函数接收parent=mw,并赋值给self.parent。- 最终
sd.parent = mw,形成组合关系。
总结
SendDialog(Widget)→ 继承(复用代码)。SendDialog(mw)→ 组合(构建责任链)。- 继承是“是什么”(
SendDialog是一种Widget)。 - 组合是“有什么”(
SendDialog对象持有MainWindow对象的引用)。
这种设计模式(责任链)的威力正源于两者的结合:继承提供统一的事件处理接口(handle),组合提供动态的链式结构。
理解这一点后,代码会变得非常清晰!
适用场景的话,责任链模式常用于事件处理系统,比如GUI中的事件传播,比如这里的组件层级处理事件。或者当多个对象可以处理同一请求,但具体由哪个处理在运行时决定的情况。例如,不同的中间件处理请求,或者审批流程中的多级审批。
class FootballTeamIterator: def __init__(self, members): self.members = members self.index = 0 def __iter__(self): return self def __next__(self): if self.index < len(self.members): val = self.members[self.index] self.index += 1 return val else: raise StopIteration() class FootballTeam: def __init__(self, members): self.members = members def __iter__(self): return FootballTeamIterator(self.members) def main(): members = [f"player{str(x)}" for x in range(1, 23)] members = members + ["coach1", "coach2", "coach3"] team = FootballTeam(members) team_it = iter(team) try: while True: print(next(team_it)) except StopIteration: print("(End)") if __name__ == "__main__": main()
如上的代码FootballTeamIterator这个同时有__iter__和__next__方法,那他就是迭代器,但是FootballTeam只有 __iter__方法,这个又算什么呢?不能算迭代器吧
- FootballTeamIterator 是迭代器吗?
是的,因为它同时有 __iter__ 和 __next__ 方法。
- FootballTeam 算什么?不能算迭代器吧?
对,FootballTeam 不是迭代器。它是一个可迭代对象,因为它有 __iter__ 方法并返回一个迭代器(FootballTeamIterator),但它没有 __next__ 方法。
1. 迭代器(Iterator)和可迭代对象(Iterable)的定义
在 Python 中:
- 迭代器(Iterator):一个对象如果实现了 __iter__ 和 __next__ 两个方法,它就是一个迭代器。
- __iter__ 方法返回迭代器自身(通常是 return self)。
- __next__ 方法返回序列中的下一个元素,并在没有更多元素时抛出 StopIteration 异常。
-
- 可迭代对象(Iterable):一个对象如果实现了 __iter__ 方法,并且该方法返回一个迭代器,它就是一个可迭代对象。可迭代对象可以用在 for 循环中或通过 iter() 函数获取迭代器,但它本身不一定是迭代器。
2. 代码中的 FootballTeamIterator
让我们看看 FootballTeamIterator 类的定义:
class FootballTeamIterator: def __init__(self, members): self.members = members self.index = 0 def __iter__(self): return self def __next__(self): if self.index < len(self.members): val = self.members[self.index] self.index += 1 return val else: raise StopIteration()
- 特性:
- 它有 __iter__ 方法,返回 self,表明它是一个迭代器。
- 它有 __next__ 方法,负责从 self.members 中按顺序返回下一个元素,并在元素耗尽时抛出 StopIteration。
- 结论:FootballTeamIterator 是一个迭代器,因为它同时实现了 __iter__ 和 __next__。
3. 代码中的 FootballTeam
再看看 FootballTeam 类的定义:
class FootballTeam: def __init__(self, members): self.members = members def __iter__(self): return FootballTeamIterator(self.members)
- 特性:
- 它有 __iter__ 方法,返回一个 FootballTeamIterator 实例(一个迭代器)。
- 它没有 __next__ 方法。
- 结论:FootballTeam 是一个可迭代对象,因为它实现了 __iter__ 方法并返回一个迭代器。但它不是迭代器,因为它没有 __next__ 方法。
4. 代码的行为
我们来分析 main 函数的执行过程:
def main(): members = [f"player{str(x)}" for x in range(1, 23)] members = members + ["coach1", "coach2", "coach3"] team = FootballTeam(members) team_it = iter(team) try: while True: print(next(team_it)) except StopIteration: print("(End)")
- team = FootballTeam(members):创建一个 FootballTeam 实例,存储了 members 列表(包括 22 个球员和 3 个教练)。
- team_it = iter(team):调用 FootballTeam.__iter__,返回一个新的 FootballTeamIterator 实例,初始化时 index = 0。
- next(team_it):调用 FootballTeamIterator.__next__,从 members 中依次返回元素(player1, player2, ...),每次调用 index 递增,直到抛出 StopIteration。
- 输出:打印所有成员,最后打印 (End)。
在这里,FootballTeam 本身不负责迭代,而是把迭代的任务交给 FootballTeamIterator。
5. 这种设计的好处
- 分离关注点:FootballTeam 负责存储和管理数据(球员和教练列表),而 FootballTeamIterator 负责迭代逻辑。这种分工让代码更清晰。
-
- 支持多次迭代:每次调用 iter(team) 都会创建一个新的 FootballTeamIterator 实例,因此你可以多次独立地迭代 team。例如:
7. 总结
- FootballTeamIterator 是一个迭代器,负责实际的迭代过程。
- FootballTeam 是一个可迭代对象,提供了获取迭代器的方式,但不直接参与迭代。
这种设计在 Python 中很常见。例如,内置的 list 类是一个可迭代对象,它的 __iter__ 方法返回一个 list_iterator(迭代器对象),由后者完成迭代工作。
追问一:python中生成器和迭代器的区别?
Python 中生成器和迭代器的区别
在 Python 中,生成器(Generator) 和 迭代器(Iterator) 是两个密切相关但有所区别的概念。它们都用于处理数据序列,但在定义、实现方式、功能和使用场景上有所不同。下面将详细阐述它们的区别。
1. 基本定义
- 迭代器(Iterator)
迭代器是一个实现了迭代器协议的对象。迭代器协议要求对象定义两个方法:
- __iter__():返回迭代器对象自身。
- __next__():返回序列中的下一个元素,当没有更多元素时抛出 StopIteration 异常。
简单来说,迭代器是可以被 for 循环或 next() 函数调用的对象,用于逐个获取元素。
- 生成器(Generator)
生成器是一种特殊的迭代器,通常由生成器函数或生成器表达式创建:
- 生成器函数:使用 yield 关键字的函数,能够暂停执行并返回值,而不是一次性运行完成。
- 生成器表达式:类似于列表推导式,但使用圆括号(如 (x for x in range(10))),返回一个生成器对象。
2. 创建方式
- 迭代器
- 通过定义一个类并实现 __iter__() 和 __next__() 方法创建。
- 通过内置函数 iter() 将可迭代对象(如列表、元组)转换为迭代器。例如:iter([1, 2, 3])。
- 生成器
- 通过包含 yield 关键字的函数创建。
- 通过生成器表达式创建,例如:(x for x in range(10))。
3. 状态管理
- 迭代器
需要开发者手动管理迭代状态。例如,在 __next__() 方法中记录当前的位置或计数器。
- 生成器
自动管理状态。每次调用 yield 时,生成器会暂停并保存当前的执行上下文(包括局部变量和指令指针),下次调用时从暂停处继续执行。
4. 惰性求值
- 迭代器
通常与底层数据结构绑定。如果数据是预先加载的(例如列表),迭代器会一次性访问整个数据集,可能会占用较多内存。
- 生成器
支持惰性求值,即只在需要时生成下一个元素。这种特性使得生成器在处理大数据集或无限序列时非常高效,能够显著节省内存。
5. 可读性和简洁性
- 迭代器
实现迭代器需要定义一个类并手动实现协议方法,代码相对冗长。
- 生成器
使用生成器函数或表达式,代码更简洁、直观。yield 关键字让逻辑更加自然。
6. 功能差异
- 迭代器
只能向前迭代,无法后退或重置。每次调用 __next__() 返回下一个元素,直到抛出 StopIteration。
- 生成器
同样只能向前迭代,但支持惰性求值。此外,生成器还提供额外的控制方法,如 send()、throw() 和 close(),使其更灵活。
7. 使用场景
- 迭代器
适用于需要自定义迭代逻辑的场景,比如遍历复杂数据结构。Python 的内置类型(如列表、元组、字典)都支持生成迭代器。
- 生成器
适用于需要惰性求值或处理大数据的场景,例如读取大文件、生成无穷序列(如斐波那契数列)或实现协程。
8. 内存效率
- 迭代器
内存占用取决于底层数据结构。如果数据是预加载的(如列表),内存开销较大。
- 生成器
由于惰性求值,通常只在需要时生成数据,内存占用极低,特别适合处理大规模数据。
9. 示例对比
以下通过生成斐波那契数列的前 10 项来对比两者的实现:
迭代器实现
class FibonacciIterator: def __init__(self, n): self.n = n self.a, self.b = 0, 1 self.count = 0 def __iter__(self): return self def __next__(self): if self.count < self.n: result = self.a self.a, self.b = self.b, self.a + self.b self.count += 1 return result else: raise StopIteration # 使用 fib_iter = FibonacciIterator(10) for num in fib_iter: print(num)
def fibonacci_generator(n): a, b = 0, 1 for _ in range(n): yield a a, b = b, a + b # 使用 for num in fibonacci_generator(10): print(num)
对比分析:
- 迭代器需要定义一个类并手动管理状态,代码较长。
- 生成器只需一个函数和 yield,实现更简洁。
10. 实际应用示例
生成器在大规模数据处理中尤为强大。例如,逐行读取大文件:
def read_large_file(file_path): with open(file_path, 'r') as file: for line in file: yield line # 使用 for line in read_large_file('large_file.txt'): print(line.strip())
11. 总结
- 生成器是迭代器的子集,通过 yield 实现惰性求值,自动管理状态,代码简洁,适合处理大数据或无限序列。
- 迭代器更通用,通过类实现,适用于需要自定义迭代逻辑的场景。
- 两者都支持 for 循环,但生成器在内存效率和简洁性上更有优势。
追问二:不太了解迭代器为什么要__iter__这个方法去返回自身实例,比如你举的例子:
class FibonacciIterator: def __init__(self, n): self.n = n self.a, self.b = 0, 1 self.count = 0 def __iter__(self): return self def __next__(self): if self.count < self.n: result = self.a self.a, self.b = self.b, self.a + self.b self.count += 1 return result else: raise StopIteration # 使用 fib_iter = FibonacciIterator(10) for num in fib_iter: print(num)
我来解释一下为什么迭代器需要 __iter__ 方法返回自身实例,比如在你给的 FibonacciIterator 示例中。
可迭代对象和迭代器的区别
首先,我们需要明白两个概念:可迭代对象(Iterable) 和 迭代器(Iterator)。
- 可迭代对象:是一个可以被 for 循环遍历的对象,比如列表、元组或字典。它需要实现 __iter__ 方法,这个方法返回一个迭代器。
- 迭代器:是一个既实现了 __iter__ 方法(返回自身),又实现了 __next__ 方法(返回序列中下一个元素)的对象。
在你的例子中,FibonacciIterator 类定义如下:
class FibonacciIterator: def __init__(self, n): self.n = n self.a, self.b = 0, 1 self.count = 0 def __iter__(self): return self def __next__(self): if self.count < self.n: result = self.a self.a, self.b = self.b, self.a + self.b self.count += 1 return result else: raise StopIteration
这里,FibonacciIterator 同时实现了 __iter__ 和 __next__,所以它是一个迭代器。
for 循环的工作原理
要理解 __iter__ 的作用,我们先看看 for 循环是怎么工作的:
- 当你写 for num in fib_iter: 时,Python 首先调用 fib_iter.__iter__(),获取一个迭代器。
- 然后在循环中反复调用这个迭代器的 __next__() 方法,获取下一个元素。
- 当 __next__() 抛出 StopIteration 异常时,循环结束。
在你的代码中:
- fib_iter = FibonacciIterator(10) 创建了一个迭代器对象。
- for num in fib_iter: 调用 fib_iter.__iter__(),返回 fib_iter 自身。
- 然后每次循环调用 fib_iter.__next__(),生成斐波那契数列的下一个数字。
为什么 __iter__ 要返回自身?
你可能会问:既然 fib_iter 已经是迭代器了,为什么还需要 __iter__ 方法返回自己,而不是直接用它呢?这是因为 Python 的 迭代器协议 要求:
- 迭代器必须实现 __iter__ 方法,并且这个方法返回迭代器自身(也就是 self)。
- 这样设计是为了统一接口,让 for 循环或其他需要迭代的功能可以一致地处理所有对象,不管它们是可迭代对象还是迭代器。
具体原因有以下几点:
- 统一处理可迭代对象和迭代器
- 对于可迭代对象(比如列表),__iter__ 返回一个新的迭代器。
- 对于迭代器(比如 FibonacciIterator),__iter__ 返回自身。
- 这样,for 循环只需要调用 __iter__(),就能保证拿到一个迭代器,不用关心对象到底是可迭代对象还是迭代器。
- 支持多次调用 iter()
如果你在迭代器上调用 iter(fib_iter),它会返回 fib_iter 自身。这保证了迭代器的行为是一致的。比如:
fib_iter = FibonacciIterator(3) iter1 = iter(fib_iter) # 返回 fib_iter 自身 iter2 = iter(fib_iter) # 还是返回 fib_iter 自身
- iter1 和 iter2 是同一个对象,因为 __iter__ 返回的是 self。
- 与可迭代对象的行为区分
可迭代对象和迭代器的行为不同: - 可迭代对象:每次调用 __iter__() 都会返回一个新的迭代器。比如:
lst = [1, 2, 3] iter1 = iter(lst) # 返回一个新的迭代器 iter2 = iter(lst) # 返回另一个新的迭代器
- 每次迭代都可以从头开始。
- 迭代器:因为 __iter__ 返回自身,迭代器有状态,迭代一次后就耗尽了。比如:
fib_iter = FibonacciIterator(3) for num in fib_iter: print(num) # 打印 0, 1, 1 for num in fib_iter: print(num) # 什么都不打印,因为已经耗尽
第二次循环时,fib_iter 的状态已经到了尽头(self.count >= self.n),不会重新开始。
总结
迭代器需要 __iter__ 方法返回自身,是为了满足 Python 的迭代器协议。这种设计:
- 让迭代器可以直接被 for 循环使用。
- 与可迭代对象的行为区分开来(可迭代对象生成新迭代器,迭代器保持自身状态)。
- 提供统一的接口,方便 Python 处理各种对象。
在你的 FibonacciIterator 示例中,__iter__ 返回 self,确保 fib_iter 作为一个迭代器,能被 for 循环识别和使用,同时它的状态(self.a、self.b 和 self.count)会在迭代中逐步更新,直到耗尽。
在 Python 的多重赋值语句 self.a, self.b = self.b, self.a + self.b 中,右边的表达式 self.b 和 self.a + self.b 是先计算完成的,
然后结果才被同时赋给左边的 self.a 和 self.b。因此,右边的 self.a + self.b 中使用的 self.a 和 self.b 都是更新前的值,而不是更新后的值。
换句话说,左边的 self.a 在赋值时并不会立即影响右边 self.a + self.b 的计算,因为整个右边的值是在赋值发生之前就已经确定了的。
你的疑问可能是:左边的 self.a 被赋值为 self.b 后,会不会影响右边的 self.a + self.b?答案是不会。
用代码验证
我们可以看一下 FibonacciIterator 中的行为。假设初始值是 self.a = 0, self.b = 1,然后执行 __next__ 方法:
def __next__(self): if self.count < self.n: result = self.a # 返回当前的 self.a print(f"更新前: a={self.a}, b={self.b}") self.a, self.b = self.b, self.a + self.b print(f"更新后: a={self.a}, b={self.b}") self.count += 1 return result else: raise StopIteration
总结
在 self.a, self.b = self.b, self.a + self.b 中:
- 右边的 self.a + self.b 使用的是更新前的 self.a 和 self.b。
- 原因是 Python 先计算右边的所有表达式,形成一个元组,然后再同时赋给左边的变量。
- 左边的 self.a 被赋值后,不会影响右边的计算,因为赋值是最后一步。
这种机制保证了斐波那契数列的正确生成,也解答了关于 self.a 是更新前还是更新后的疑问。

浙公网安备 33010602011771号