【问题标题】:Strange Python Scope Issue奇怪的 Python 范围问题
【发布时间】:2016-04-16 15:37:30
【问题描述】:

我以为我对 Python 的变量作用域理解得很好,但是今天我碰到了这段代码。

from __future__ import print_function

def main():
    v_Matrix =[[1, 2, 3],
               [4, 5, 6],
               [7, 8, 9]]

    print(v_Matrix)
    print()

    f_Rotate(v_Matrix)
    print(v_Matrix)

    hh = 3
    f_This(hh)
    print(hh)

def f_Swap(v_Matrix, r1, c1, r2, c2):
    v_Matrix[r1][c1], v_Matrix[r2][c2] = v_Matrix[r2][c2], v_Matrix[r1][c1]

def f_Transpose(v_Matrix):
    for row in range(len(v_Matrix)):
        for col in range(row):
            f_Swap(v_Matrix, row, col, col, row)

def f_FlipVertical(v_Matrix):
    v_Size = len(v_Matrix)
    for row in range(v_Size // 2):
        for col in range(v_Size):
            f_Swap(v_Matrix, row, col, v_Size - row - 1, col)

def f_Rotate(v_Matrix):
    f_Transpose(v_Matrix)
    f_FlipVertical(v_Matrix)

def f_This(hh):
    hh = hh * 2

if __name__ == '__main__':
    main()

运行时,变量v_Matrix 的行为似乎是全局的。但是,测试变量 hh 的行为与预期一样,其中一个作用域位于 main() 中,另一个作用域位于 f_This 中。

v_Matrix 不是全局的,在各个函数中被修改,但不会在这些函数之间来回传递,甚至不会返回到main()。然而,在这些函数中应用于v_Matrix 的值更改可以在main() 范围内访问,如输出之间所示。但是,正如预期的那样,测试变量hh的值在main()范围内没有变化,只是在函数的范围内。

什么给了?

【问题讨论】:

  • 那是因为列表是可变的。有关详细讨论,请参阅docs.python.org/2/reference/…
  • 并且Python在调用函数时将指向可变对象的指针作为参数传递,而不是创建对象的副本。与 C 风格的指针一样,您可以修改所指向对象的内容,但不能通过赋值使函数的调用者指向新对象。
  • @Selcuk 好的,关于您的链接中的范围没有太多提及。但是,您的评论确实帮助我指出了这样一个事实,即可变对象,尤其是列表在基于“外部”与“内部”容器等的范围方面具有不同的行为。我还不完全理解,但我会.我想我在研究范围时错过了这个事实。谢谢。
  • @Matt Jordan 感谢您的评论和 Selcuk 的评论,我正在处理它。我迷路了,现在我找到了。谢谢。
  • @JayJay123,可变和不可变对象没有基于范围的不同行为。它们以完全相同的方式传递给函数。只是可变对象可以就地更改,而不可变对象则不能。例如,hh=hh*2 不会改变现有的包含 3 的整数对象,而是将名为的本地 hh 重新分配给另一个包含 6 的整数对象。外部的 hh 名称仍然引用 3。对于矩阵,有一个将名为v_Matrix 的外部变量和内部变量分配给列表,并且列表对象就地更改。

标签: python scope


【解决方案1】:

Python 中的一切都是一个对象,无论对象是可变的还是不可变的,它们都以相同的方式传递给函数。但是,可以在函数外部看到对可变对象的更改。

例子:

def f(c,d):
    print('Inside f()')
    print('  id(a) =',id(a),'value =',a)
    print('  id(b) =',id(b),'value =',b)
    print('  id(c) =',id(c),'value =',c)
    print('  id(d) =',id(d),'value =',d)
    c = 4     # local name c is assigned a new object.
    d[0] = 5  # local name d's object is mutated
    print('  id(a) =',id(a),'value =',a)
    print('  id(b) =',id(b),'value =',b) # b and d objects are same, so b changed
    print('  id(c) =',id(c),'value =',c) # id changed, not same as a
    print('  id(d) =',id(d),'value =',d) # id did not change, still same as b
    d = [6]   # local name d is assigned a new object.
    print('  id(d) =',id(d),'value =',d) # id changed, not same as b 
a = 3 # immutable
b = [3] # mutable
print('Before f()')
print('  id(a) =',id(a),'value =',a)
print('  id(b) =',id(b),'value =',b)
f(a,b)
print('After f()')
print('  id(a) =',id(a),'value =',a)
print('  id(b) =',id(b),'value =',b)

带注释的输出:

Before f()
  id(a) = 507107264 value = 3
  id(b) = 67001608 value = [3]
Inside f()
  id(a) = 507107264 value = 3
  id(b) = 67001608 value = [3]
  id(c) = 507107264 value = 3    # id(c) == id(a)
  id(d) = 67001608 value = [3]   # id(d) == id(b)
  id(a) = 507107264 value = 3
  id(b) = 67001608 value = [5]   # id(d)==id(b), so b changed when d did
  id(c) = 507107296 value = 4    # c was reassigned, id(c) != id(a)
  id(d) = 67001608 value = [5]   # id(d) == id(b)...object mutated
  id(d) = 65761864 value = [6]   # d was reassigned, id(d) != id(b)
After f()
  id(a) = 507107264 value = 3    # immutable a unaffected
  id(b) = 67001608 value = [5]   # sees mutation of list contents from d[0]=5

【讨论】:

  • 感谢您的详细解答。
【解决方案2】:

list 是一个可变数据结构。这意味着您可以在保持id() 不变的同时更改其值。如果您要传递一个命名这样一个对象的变量,因为它具有相同的id,它就是您在代码的各个部分中引用的同一个对象。所以如果你在任何地方改变它,它就会永远变异。

所以解决方案是,不要传递它们。而是保留它并在数据转换(不是突变)中使用它,即创建另一个或更好但使用生成器并只评估最终需要的列表。

【讨论】:

  • 感谢您的回答和信息。
猜你喜欢
  • 1970-01-01
  • 2012-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多