【问题标题】:When can a variable be altered in a function call? [duplicate]什么时候可以在函数调用中更改变量? [复制]
【发布时间】:2017-11-02 00:25:57
【问题描述】:

在 Python 中,可以在函数中更改某些类型的对象而无需返回任何内容。对于某些对象,情况并非如此。

例如,如果我们将字典传递给函数并在函数内对其进行更改,则原始字典将被更改:

def add_key(d, key):
    d[key] = True

d = {}
print d
# {}

add_key(d, 1)
print d
# {1: True}

相比之下,我们不能像这样改变整数:

def increment_int(i):
    i += 1

i = 1
print i
# 1

increment_int(i)
print i
# still 1!

起初我认为规则可能只是不可变对象不能以这种方式在函数内更改。一个类似的例子不适用于字符串:

def append_string(s1, s2):
    s1 += s2

s = "hello"
print s
# "hello"

append_string(s, " world")
print s
# "hello", not "hello world" :( 

不过我很困惑,因为在函数中应用于可变对象的某些操作似乎在之后会可见,而其他操作则不会。例如。将项目附加到列表确实有效:

def append_list(l, val):
    l.append(val)

l = []
print l
# []

append_list(l, 1)
print l
# [1]

虽然交换两个列表的名称不会:

def swap_lists(l1, l2):
    l1, l2 = l2, l1

l1 = [1]
l2 = [2]
print l1, l2
# [1] [2]

swap_lists(l1, l2)
print l1, l2
# still [1] [2], even though lists are mutable!

又一个例子,看似奇怪且出乎意料的行为:

def swap_then_append(l1, l2):
    l1, l2 = l2, l1
    l1.append(1)
    l2.append(2)

l1 = []
l2 = []
print l1, l2
# [] []

swap_then_append(l1, l2)
print l1, l2
# [2] [1]

有人能解释一下这里发生了什么吗?


编辑:已确定此问题的possible duplicate;那里的答案部分地向我解释了发生了什么,我仍然不明白为什么,例如,上面的函数swap_lists 实际上并没有交换列表。现在我在进一步考虑它,我仍然不明白为什么上面的increment_intappend_string 没有做我最初期望的事情,除了它与整数和字符串的不变性有关.

【问题讨论】:

  • 感谢您指出可能的重复 - 阅读该问题和答案很有帮助,但我仍然觉得我不明白这里发生了什么。我在底部添加了一个编辑,承认可能的重复,但我仍然非常感谢某人更深入的回答!
  • 您可以改变可变对象,但分配(包括交换)会将新对象绑定到名称,因此它的效果对于包含该名称的范围是本地的。请看Facts and myths about Python names and values
  • Python 中的“变量”最好被视为对象的标签。该对象存在或不附有任何标签(嗯,有点——如果该对象没有被任何其他对象引用,那么它就会被销毁,但这是一个旁白)。作为对象的点独立于附加到它的标签或标签而存在。例如l1 = l2 = [1, 2, 3] 会创建两个标签,它们粘在同一个列表上。这与对象的可变性或不变性无关。当一个标签被分配给一个对象,然后你将它分配给另一个对象时,你只是在交换那个标签。

标签: python


【解决方案1】:

dupe target 中的解释很好地解释了正在发生的事情,但我将解决 swap_then_append 中发生的事情。

def swap_then_append(l1, l2):
    print l1,l2
    # [3],[4]
    l1, l2 = l2, l1
    print l1,l2
    # [4] [3]
    l1.append(1)
    l2.append(2)

l1 = [3]
l2 = [4]
print l1, l2
# [3] [4]

swap_then_append(l1, l2)
print l1, l2
# [3,2] [4,1]

“交换”会更改用于引用列表的变量的名称,但仅在函数内。虽然底层对象的突变是永久性的,但变量的分配取决于范围。函数中的新l1l2 也可以称为其他名称,因为它们现在只是隐藏了函数签名中定义的原始变量l1l2,这反过来又隐藏了@ 987654327@ 和 l2 在主脚本中定义。然后,突变将应用于原始列表。

【讨论】:

    猜你喜欢
    • 2018-11-13
    • 2012-08-16
    • 1970-01-01
    • 2011-04-04
    • 2019-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-27
    相关资源
    最近更新 更多