为什么在此代码中更改了变量 a?

Why is the variable a is changed in this code?

class PowTwo:
    """Class to implement an iterator
    of powers of two"""

    def __init__(self, max=0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        if self.n <= self.max:
            result = 2 ** self.n
            self.n += 1
            return result
        else:
            raise StopIteration
a = PowTwo(3)
b = iter(a)
print(next(a))

没有这个片段 b = iter(a),输出是

Traceback (most recent call last):
  File "/Users/Mark/test2.py", line 20, in <module>
    print(next(a))
  File "/Users/Mark/test2.py", line 13, in __next__
    if self.n <= self.max:
AttributeError: 'PowTwo' object has no attribute 'n'

我的问题:

b = iter(a)我没用a = iter(a)。这里变量a怎么变了?

您的 __iter__ 是可变的:它将对象的 n 属性设置为 0。这是代码中 nonly 初始化。如果不调用__iter____next__查找时不会找到n

可变 __iter__ 方法不是个好主意。您应该在 __init__.

中执行初始化

Python 对象可以分配给多个名称。所有这些名称都将指代同一个实际对象。例如:

a = [1, 2, 3]
b = a

ab 指的是同一个对象。设置b[0] = 0也会影响a,因为是同一个对象。这给初学者带来了很大的痛苦。

要将对象绑定到名称,您可以分配它。作业有多种形式。例如,defclass 分别将函数和 class 对象分配给名称。这些名字没有什么特殊的,大家可以随意重新绑定:

def a():
    print('Hi')

b = a   # b now refers to a function object
a = 1   # a is now an integer, while b is the original function

当您将参数传递给函数时,会发生另一种类型的赋值。您传入的对象绑定到参数列表中的局部名称。函数局部名称指的是您传入的完全相同的对象,而不是副本:

a = 1
def f(x):
    print(x is a)
f(a)  # True

Return 值的工作方式类似,但相反。函数中的对象被分配给外部作用域中的名称。

现在假设您有一个对象 a = PowTwo(3)。当您对其调用 b = iter(a) 时,会发生以下情况:

  1. iter的调用等同于PowTwo.__iter__(a)
  2. 您在全局范围内分配给 a 的对象绑定到 __iter__
  3. 中的参数 self
  4. __iter__ 中的对象 self 通过 return 调用
  5. 绑定到 b

现在 ab 指的是同一个对象。之后调用 next(a)next(b) 会将同一个对象传递给 next,因为两个名称都绑定到它。

如果您分配了 a = iter(a),您将重新绑定 a 到它自己。虽然技术上不是空操作,但它相当于只调用 iter(a) 并丢弃冗余的 return 值。