进阶篇 python 的值传递

值传递和引用传递

  • 值传递,通常就是拷贝参数的值,然后传递给函数里的新变量,这样,原变量和新变量之间互相独立,互不影响。

  • 引用传递,通常是指把参数的引用传给新的变量,这样,原变量和新变量就会指向同一块内存地址。如果改变了其中任何一个变量的值,那么另外一个变量也会相应地随之改变。

Python 函数的参数传递

这里首先引用 Python 官方文档中的一段说明:

“Remember that arguments are passed by assignment in Python. Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per Se.”

准确地说,Python 的参数传递是赋值传递 (pass by assignment),或者叫作对象的引用传递(pass by object reference)。Python 里所有的数据类型都是对象,所以参数传递时,只是让新变量与原变量指向相同的对象,并不存在值传递或是引用传递一说。

需要注意的是,赋值或对象的引用传递,不是指向一个具体的内存地址,而是指向一个具体的对象。

  • 对象是可变(mutable),当其改变时,所有指向这个对象的变量都会改变。
    包含 :整数、字符串、元组
  • 对象不可变(immutable),简单的赋值只能改变其中一个变量的值,其余变量则不受影响。
    包含:列表,字典,集合

清楚了这一点,如果你想通过一个函数来改变某个变量的值,通常有两种方法:

  • 直接将可变数据类型(比如列表,字典,集合)当作参数传入,直接在其上修改;

  • 创建一个新变量,来保存修改后的值,然后将其返回给原变量

思考题

  1. 下面的代码中, l1、l2 和 l3 都指向同一个对象吗?

    l1 = [1, 2, 3]
    l2 = [1, 2, 3]
    l3 = l2
    

    解答:

    列表是可变对象,每创建一个列表,都会重新分配内存,因此 l1 和 l2 并不是同一个对象,由于 l3 = l2 表明 l3 指向 l2 的对象

  2. 下面的代码中,打印 d 最后的输出是什么呢?

    def func(d):
       d['a'] = 10
       d['b'] = 20
    
    d = {'a': 1, 'b': 2}
    func(d)
    print(d)
    
    # {'a': 10, 'b': 20}
    

    解答:

    字典是可变对象,元素可以原处修改,所以经过 func()处理后,打印的是 {'a': 10, 'b': 20}

  3. 下面的代码中,打印 d 最后的输出是什么呢?

    def func(d):
       d['a'] = 10
       d['b'] = 20
       d = {'a': 1, 'b': 2}
    
    d = {}
    func(d)
    print(d) 
    
    # {'a': 10, 'b': 20}
    

    解答:

    字典是可变对象,元素可以原处修改,func() 函数中 d['a'] = 10; d['b'] = 20,修改了 d;

    d = {'a': 1, 'b': 2} 表示给 d 赋值,都会重新分配内存,因此这里的 d 和外面的 d 不是同一个对象。

    def func(d):
       print(2, id(d))
       d['a'] = 10
       d['b'] = 20
       d = {'a': 1, 'b': 2}
       print(3, id(d))
    
    d = {}
    print(1, id(d))
    func(d)
    print(4, id(d))
    print(d)
    
    # 输出
    # 1 4465468432
    # 2 4465468432
    # 3 4465467952
    # 4 4465468432
    # {'a': 10, 'b': 20}
    

发表评论

电子邮件地址不会被公开。 必填项已用*标注