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

时间:2020-07-09 20:15:37

标签: python python-3.x scope

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?

2 个答案:

答案 0 :(得分:6)

您的__iter__是可变的:它将对象的n属性设置为0。这是代码中n only 初始化。如果未调用__iter__,则__next__在寻找时将找不到n

可变__iter__方法不是一个好主意。您应该在__init__中执行初始化。

答案 1 :(得分:1)

Python对象可以分配给多个名称。所有这些名称将引用相同的实际对象。例如:

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

ab都引用相同的对象。设置b[0] = 0也将影响a,因为它是同一对象。这会给初学者带来很多痛苦。

要将对象绑定到名称,可以对其进行分配。作业有多种形式。例如,defclass分别将函数和类对象分配给名称。这些名称并不特殊,您可以随意重新绑定它们:

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

返回值的作用类似,但相反。函数中的对象在外部作用域中被分配了一个名称。

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

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

现在ab引用相同的对象。此后调用next(a)next(b)会将相同的对象传递给next,因为两个名称都绑定到该对象。

如果您分配了a = iter(a),则将a与其自身重新绑定。虽然从技术上讲不是禁忌,但它等效于仅调用iter(a)并丢弃冗余返回值。