作用域中的漏洞、死代码或为什么会输出这样的结果



代码

def change1(list1):
        list1[1] = list1[1] + 5
def change2(number):
        number = number + 2
def main():
        numbers = [4, 8, 12]
        change1(numbers)
        variable = 15
        change2(variable)
        i = 0
        while i < 3:
                print numbers[i]
                i += 1
        print variable
main()

当我读它的时候,我以为它会输出4 8 12 15,但它输出4 13 12 15。我可以在这里看到Python处理整数和列表的方式不同,我认为如果没有全局,最后一件事是不可能的。我无法理解输出,在这种情况下,为什么它不输出4 13 12 17

你可以在这里看到几乎相同的代码,不同的类型和不同的参考:

$ python test2.py 
4
13
12
15
$ python test3.py 
4
13
12
17
$ cat test2.py test3.py 

通过参考示例

test2.py:通过引用和可变数据类型传递-示例。表/列表不足以影响main中的局部变量,您需要引用!

def change1(list1):
    list1[1] = list1[1] + 5
def change2(number):
    number = [x+2 for x in number]
def main():
    numbers = [4, 8, 12]
    change1(numbers)
    variable = [15]
    change2(variable)
    i = 0
    while i < 3:
        print numbers[i]
        i += 1
    print variable[0]
main()

test3.py:通过引用的例子,在主函数之外更改可变数据类型列表/表

def change1(list1):
    list1[1] = list1[1] + 5
def change2(number):
    number[0] += 2
def main():
    numbers = [4, 8, 12]
    change1(numbers)
    variable = [15]
    change2(variable)
    i = 0
    while i < 3:
        print numbers[i]
        i += 1
    print variable[0]
main()

传递值示例

test4.py:试图找到一个具有pass-by-value的示例,为什么它不起作用?

$ cat test4.py 
# Not yet a pass-by-value example!
global variable
variable = [15]
def change1(list1):
    list1[1] = list1[1] + 5
def change2(number):
    number = [x+2 for x in number] 
def main():
    numbers = [4, 8, 12]
    change1(numbers)
    #variable = 15
    change2(variable)
    i = 0
    while i < 3:
        print numbers[i]
        i += 1
    print variable[0]
main()
$ python test4.py 
4
13
12
15   # I expected 17! Why no 17?
def change1(list1):
        # `list1[1] =` means you are changing the object passed in
        list1[1] = list1[1] + 5
def change2(number):
        # `number = ` means you create a **new local variable**, number, 
        # based on the `number`you passed in
        number = [x+2 for x in number]

因此,如果你想更改现有的对象,你必须以某种方式引用它们,例如在中

def change3(number):
    # `number[:]` is the whole existing list and you overwrite it
    number[:] = [x+2 for x in number]

更改列表时请注意[ .. ]

Python参数是通过引用传递的。您在change1中只更改了一个对象。

但是,数值和字符串都是不可变的。您不能更改传入的不可变的值,也不能在调用者中看到该值的更改。另一方面,Dictionaries和Lists是可变的,当函数返回时,被调用函数对它们所做的更改将被保留。

更多信息:http://www.penzilla.net/tutorials/python/functions/

最终的答案是Python实际上是"通过共享调用",也称为"通过对象调用"或"通过对象引用调用"。

这在以前已经被广泛讨论过。来自那篇文章:

有时,那些读过一点CS但没有读过很多CS(或者只读过太多一种CS(的人会出现在comp.lang.python上,浪费大量精力试图告诉每个人python正在使用一些它并没有真正使用的调用模型。事实证明,他们并不真正理解Python的模型,而且往往也不理解他们最喜欢的模型。

但别介意,你唯一需要知道的是,Python的模型既不是"按值调用",也不是"按引用调用"(因为任何为Python使用这些术语的尝试都需要使用单词"-value"one_answers"-reference"的非标准定义(。最准确的描述是CLU的">通过对象调用"或">通过共享调用"。或者,如果您愿意,"<strong]通过对象引用调用>

如果你还没有读过这篇文章,你也应该读一下。

Python的语义与CLU语言的语义最为相似。Liskov等人的CLU参考手册描述了如下语义:

"我们通过共享调用参数传递技术,因为参数对象在调用者和被调用例程。此技术不会与大多数传统的论点传递技术相对应(类似于LISP中的参数传递(尤其是不是按值调用的,因为-由被调用例程形成的将对调用者可见。并且它不是通过引用调用的,因为没有提供访问权限调用程序的变量,但仅限于某些对象。">

change1中,您可以将列表中的值与value + 5交换
change2中,将5添加到number。结果是一个新对象,而不仅仅应用于传递的变量。如果你来自C++:不,Python中就没有int& var

这样做会得到预期的结果:

def change2(number):
    return number + 5
variable = 15
variable = change2(variable)

如果您仍然不想返回值,可以创建一个MutableInt类。

class MutableInt(object):
    def __init__(self, value = 0):
        self._value = int(value)
    def __add__(self, other):
        self._value += int(other)
        return self
    def __sub__(self, other):
        self._value -= int(other)
        return self
    ...

所有示例都显示了按值调用。Python只有按值调用。没有引用调用。python中的所有值都是引用(不可能有一个"对象"作为值(。因此,在传递给函数时会复制引用。列表是可变的,因此可以通过共享引用来更改其内容。在change2中,您将重新分配一个局部变量以指向另一个对象,这与所有对局部变量的赋值一样,对任何调用范围都没有影响,因为它是按值调用的。

最新更新