Python:值传递还是引用传递?我们一探究竟!

183 阅读4分钟

在 Python 中,关于传递方式的讨论是一个经久不衰的话题。有些人认为 Python 是值传递,而另一些人则认为它是引用传递。那么,究竟哪一种说法是正确的呢?本文将为您揭晓真相。

理解值传递和引用传递

熟悉Java的同学,应该知道,Java的参数传递包含两种方式:值传递和引用传递。

值传递是当方法被调用时把实际参数的值传给形式参数。这样实际参数和形式参数之间互相独立,互不影响。

比如,我们看这一段Java代码:

public class Demo {
​
    public static void changeValue(int value) {
        value = 20;
    }
​
    public static void main(String[] args) {
        int a = 10;
        changeValue(a);
        System.out.println(a); // 输出 10
    }
​
}
​

在上面的例子中,我们先定义了一个变量a并初始化为10,然后调用changeValue方法,并将a作为参数传递给它,changeValue方法内部修改value的值为20,但是对于外部的变量a并没有影响,仍然是10,因为Java中的基本数据类型都是使用值传递的方式传递的。

引用传递,当方法调用时,实际参数的引用传递给形式参数,这样,实际参数和形式参数指向同一块内存地址。这样改变任何一个变量的值,另一个变量也会随之改变。

比如,上面的例子改成下面的形式:

public class Person {
​
    // ...
​
    public static void changeName(Person person) {
        person.setName("Alice");
    }
​
​
    public static void main(String[] args) {
        Person originalPerson = new Person("Bob");
        changeName(originalPerson);
        System.out.println(originalPerson.getName()); // 输出 "Alice"
    }
​
}
​

引用传递使originalPerson和person指向同一内存地址,这样修改person对象的name,originalPerson对象的name也被修改为Alice。

不过,这是Java语言中的特点。Python中的参数传递到底是值传递还是引用传递,还是其它呢?

Python函数的参数传递

翻阅官方文档,Python 的参数传递是赋值传递 ,或者叫作对象的引用传递。Python 里所有的数据类型都是对象,所以参数传递时,只是让新变量与原变量指向相同的对象而已,并不存在值传递或是引用传递一说。

怎么理解赋值传递呢?

我们看这样一段代码:

a = 1
b = a
a = a + 1

这段代码用图示表示为

image.png

Python 中的数据类型分为两类:可变类型和不可变类型。不可变类型包括数值、字符串和元组等;而可变类型包括列表、字典和集合等。这里先创建1这个对象,然后贴个标签a,b=a,只是让同一个对象被多个变量引用,又给打了个标签b,a+1创建了2这个对象,赋值操作相当于将标签a贴在了对象2上。

再来看看可变类型:

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

列表是可变的,append操作会在原列表进行更改,内存地址不变,所以l1和l2输出的值相同

这样我们知道:

  • 变量的赋值,只是表示让变量指向了某个对象,并不表示拷贝对象给变量;而一个对象,可以被多个变量所指向。
  • 可变对象(列表,字典,集合等等)的改变,会影响所有指向该对象的变量。
  • 对于不可变对象(字符串、整型、元组等等),所有指向该对象的变量的值总是一样的,也不会改变。但是通过某些操作(+= 等等)更新不可变对象的值时,会返回一个新的对象。
  • 变量可以被删除,但是对象无法被删除。

我们知道了赋值原理,函数的参数传递也是一样的

​
def demo(b):
  b = 2a = 1
demo(a)
print(a) # 1

这里调用demo方法时,将a和b都指向1这个对象,b=2,会创建一个2的对象,然后让b指向2这个对象。并不影响a的指向。

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

这里调用demo方法时,将l1和l2都指向[1, 2, 3]这个对象,append方法会在原列表进行修改不会创建新对象,l1和l2始终都指向这个对象,因此l1的值为[1, 2, 3, 4]

总结

这样我们就知道,Python的参数传递是赋值传递,或者对象的引用传递。这里和引用传递的区别就是,赋值传递指向的是一个具体的对象,而引用传递是一个具体的内存地址。如果对象是可变的,当对象改变,指向这个对象的变量都会改变;如果对象不可变,只会改变其中一个变量的值。

想要第一时间看到最新文章,可以关注公众号:郝同学的测开日记