Python的按对象传递CPL

C++中传递一般分两种:按值传递和按引用传递。简单来讲,函数的按值传递就是函数接收到某个传递来的值时,会创建一份该值的备份,然后函数的所有操作均在此备份的基础上进行,对原始值无影响,初始值不会发生更改;而按引用传递是直接在原始值的基础上进行操作,会使原始值发生更改。
而Python中,一切皆对象,自然而然Python是按照对象进行传递。这种方式相当于按值传递和按引用传递的一种结合。如果函数收到的是一个可变对象( list, dict, set )的引用,就能修改对象的原始值–相当于通过“引用传递”来赋值。如果函数收到的是一个不可变变量(strings, tuples, 和numbers)的引用,就不能直接修改原始对象–相当于通过“值传递”来赋值。
看两个例子:

#例1:
a = 1
def fun1(a):
    a = 2
fun1(a)
print(a)  # 输出结果为1

#例2:
a = []
def fun2(a):
    a.append(1)
fun2(a)
print(a)  # 输出结果为[1]

当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用就一点关系都没有了,所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没有发生改变.而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行了修改.

通过id来看引用a的内存地址可以比较理解:

a = 1
def fun1(a):
    print("func_in",id(a))   # func_in 41322472
    a = 2
    print("re-point",id(a), id(2))   # re-point 41322448 41322448
print("func_out",id(a), id(1))  # func_out 41322472 41322472
fun1(a)
print(a)  # 1

注:具体的值在不同电脑上运行时可能不同。

可以看到,在执行完a = 2之后,a引用中保存的值,即内存地址发生变化,由原来1对象的所在的地址变成了2这个实体对象的内存地址。

而第2个例子a引用保存的内存值就不会发生变化:

a = []
def fun2(a):
    print("func_in",id(a))  # func_in 53629256
    a.append(1)
print("func_out",id(a))     # func_out 53629256
fun2(a)
print(a)  # [1]

谈到Python的按对象传递,就自然而然引出了Python中的copy()和deepcopy()的区别问题。
copy()为浅拷贝
deepcopy()为深拷贝
浅拷贝是新创建了一个跟原对象一样的类型,但是其内容是对原对象元素的引用。这个拷贝的对象本身是新的,但内容不是。如果原对象的元素包含不是基本数据结构,而是list、dict或者对象的话,那么原对象或者拷贝对象改变list、dict或者对象里面的内容的话,会导致二者同时发生改变。
深拷贝则是对原对象的完全拷贝,包含对象里面的子对象的拷贝,因此拷贝对象和原对象二者是完全独立,任何一方的改变对另外一方都不会产生任何的影响。

import copy

a = [1, 2, 3, 4, ['a', 'b']]  #原始对象
b = a  #赋值,传对象的引用
c = copy.copy(a)  #对象拷贝,浅拷贝
d = copy.deepcopy(a)  #对象拷贝,深拷贝

a.append(5)  #修改对象a
a[4].append('c')  #修改对象a中的['a', 'b']数组对象

print('a = ', a)
print('b = ', b)
print('c = ', c)
print('d = ', d)

# 输出结果:
# a =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
# b =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
# c =  [1, 2, 3, 4, ['a', 'b', 'c']]
# d =  [1, 2, 3, 4, ['a', 'b']]

猜你喜欢

转载自blog.csdn.net/weixin_39188072/article/details/81456858