漏洞范围,死代码或为何输出?

时间:2011-08-13 13:10:37

标签: python pass-by-reference pass-by-value

代码

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:传递引用和可变数据类型-example。表/列表不足以影响main中的局部变量,需要Reference!

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:pass-by-reference示例,在主函数外部更改可变数据类型列表/表

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:试图找到一个带值传递的例子,为什么它不起作用?

$ 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?

5 个答案:

答案 0 :(得分:2)

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]

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

答案 1 :(得分:1)

Python参数通过引用传递。你只改变change1中的一个对象。

  

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

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

答案 2 :(得分:1)

确切的答案是Python实际上是“通过共享调用”,也称为“按对象调用”或“按对象引用调用”。

这是extensively discussed before。从那篇文章:

  

人们不时会在comp.lang.python上弹出那些读过一点CS而不是很多CS(或者只是一种CS)的人,并浪费了很多精力来告诉大家Python使用的是一些它并没有真正使用的调用模型。事实证明,他们并不真正理解Python的模型,而且他们常常也不了解他们最喜欢的模型。

     

但是没关系,你需要知道的唯一事情是Python的模型既不是“按值调用”也不是“按引用调用”(因为任何尝试将这些术语用于Python都要求你使用非标准的定义单词“-value”和“-reference”)。最准确的描述是CLU的“按对象调用”或“通过共享调用”。或者,如果您愿意,“按对象引用调用”。

     

如果您还没有这样做,也应该阅读this

Python的语义最类似于语言CLU的语义。 Liskov 的CLU参考手册描述了这样的语义:

  

“我们通过共享调用传递技术调用的参数,       因为参数对象是在。之间共享的       调用者和被调用的例程。这种技术没有       对应于大多数传统的论证传递技术       (它类似于在LISP中传递的论点)。 特别是它       不是按值调用因为参数的突变       由被调用的例程形成的调用者将可以看到。       并且不是按引用进行调用,因为没有给出访问权限       调用者的变量,但仅限于某些对象。“

答案 3 :(得分:0)

change1中,您可以使用value + 5兑换列表中的值 在change2中,您向number添加了5。结果是一个新对象,不仅仅应用于传递的变量。 如果你来自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

    ...

答案 4 :(得分:0)

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