传值还是传引用?
Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象。
而变量是对象的一个引用(又称为名字或者标签),对象的操作都是通过引用来完成的。
例如,在a = []
中,[]
是一个空列表对象,变量 a
是该对象的一个引用。
在 Python 中,「变量」更准确叫法是「名字」,赋值操作 =
就是把一个名字绑定到一个对象上。就像Java中的对象的引用一样。
这和C/C++中的变量有非常大的区别(C中的变量是一段内存区域,而python中的对象分配和回收是另外的机制)
Python 参数传递采用的是“传对象引用”的方式。总结来说:
- 对于不可变对象作为函数参数,相当于C系语言的值传递。
- 对于可变对象作为函数参数,相当于C系语言的引用传递。
其中不可变对象包括基本数据类型(int, float, bool, string等)和元组。
可变对象包括所有自己定义的class和内置的list, set, dict等。
那么如何在python中实现C/C++中传递基本数据类型的引用的效果呢?
- 使用可变数据类型代替,比如本来要用
int &d
, 可以换成传lst
进去,在函数体里修改lst[0]
的值,但这样应该是浪费了一定空间,而且感觉上怪怪的。 - 改变函数逻辑,把变了的数据
return
出来,但对某些递归算法这样做可能会增加增加实现的难度。 - 使用
global
, 即全局变量,但使用全局变量会带来额外的风险。
逻辑表达式的短路运算特性
-
a and b
当
a==False
时,不论b
为何值,表达式结果为False
当
a==True
时,不论b
为何值,表达式结果为b
-
a or b
当
a==True
时,不论b
为何值,表达式结果为True
当
a==False
时,不论b
为何值,表达式结果为b
-
例如
print(False or 'Y')
的结果是Y
,而不是True
print(False or 0)
的结果为0
,而不是False
print(True and 1)
的结果是1
,而不是True
python中的赋值、深拷贝和浅拷贝
在做搜做算法的相关题目时,发现结果总为空,原因是在函数体内将局部变量append到外部的res里之后,待到局部函数运行结束,清除了所有局部变量,那么res里面的元素就指向一个空地址了。
## 求子集
def func1(lst):
res = []
def backtrack(routes, start): # routes记录路径(即子集),start记录开始位置(间接记录剩下的选择)
res.append(routes)
print(routes)
for i in range(start, len(lst)):
routes.append(lst[i])
backtrack(routes, i+1)
routes.pop()
backtrack([], 0)
return res
修改方法如下
## 求子集
def func1(lst):
res = []
def backtrack(routes, start): # routes记录路径(即子集),start记录开始位置(间接记录剩下的选择)
res.append(routes.copy())
print(routes)
for i in range(start, len(lst)):
routes.append(lst[i])
backtrack(routes, i+1)
routes.pop()
backtrack([], 0)
return res
就只要在append的时候append一个列表的浅拷贝就好。
总结如下:
-
赋值,即
a = b
传递对象的引用,赋值之后a 和 b 都指向同一个对象。
给对象赋值有可变对象和不可变对象之分
-
若是不可变对象,即内置类型(int,string,tuple等)则表现与浅拷贝一样。
-
若是可变对象,即list, set, dict或自己定义的class等,这里需要注意,则
a[0] = "#"
等价于b[0]="#"
-
-
浅拷贝,即
a=b.copy()
a 和 b 是一个独立的对象,但他们的子对象还是指向统一对象(是对象的引用)。
- 若对不可变对象进行浅拷贝,则没什么意义,和用
=
赋值等价 - 若是可变对象,相当于用一层循环创造一个副本,也就是深层的对象仍然有可能是赋值过来的,跟原来的有牵连。
b = [] for item in a: b.append(item)
- 若对不可变对象进行浅拷贝,则没什么意义,和用
-
深拷贝,即
a = copy.deepcopy(b)
(需要import copy
)深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的。
相当于递归的将每一层都完全拷贝一份,创造一份完全独立的副本
python中的+
和+=
例子:
# 程序一
x=[1,2,3]
def f(x):
x=x+[4]
f(x)
print(x)
# 程序二
x=[1,2,3]
def f(x):
x+=[4]
f(x)
print(x)
两个程序的运行结果不一样,原因如下:
-
对于可变类型:
+: 代表连接操作,其结果会创建一个新的对象。
+=: 代表追加操作,即 in-place 操作,在原地把另一个对象的内容追加到对象中。
-
对于不可变类型: + 与 += 都代表连接或求和操作,两者没有什么区别,其操作的结果都会产生一个新的对象。