欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python的按对象传递CPL

程序员文章站 2022-07-14 18:26:42
...

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']]