python语言陷阱
python
作为一门动态语言,除了拥有强大的表达能力和高效的开发效率外,它还有着一些语言层面的陷阱。
可变参数作为默认参数
有时会看到有些小伙伴在python
中定义函数的时候会使用一些可变参数作为函数的默认参数,最常见的如:list
。
def test(a=[]):
a.append(1)
print(a)
if __name__ == '__main__':
test() # [1]
test() # [1, 1]
使用可变参数作为函数的默认参数会让最后的运行结果超出预期所料。如上述例子中,第一次和第二次的结果截然不同。
如果不明白为什么会产生这样的结果,看下面这个例子就会明白了
def test(a=[]):
a.append(1)
print(a)
def test2(a=print("test2")):
pass
if __name__ == '__main__':
test() # [1]
test() # [1, 1]
运行上述例子,你会发现控制台会去打印"test2"。通过对比你会发现,函数的参数在函数被定义的时候就已经确定了,如果参数是个表达式,则取表达式的结果。所以在test
函数中, 无论运行多少次,在函数里面都是在对函数被定义时的那个list
进行操作。
最开始的那个例子就等同于下面的代码
a = []
def test():
a.append(1)
print(a)
if __name__ == '__main__':
test() # [1]
test() # [1, 1]
避免此类事故的发生的最好的方法就是不使用可变参数,将默认参数的行为放到函数内部去定义。下面是改进后的版本。
def test(a=None):
if a is None:
a = []
a.append(1)
print(a)
if __name__ == '__main__':
test() # [1]
test() # [1]
tuple的隐式变换
a = 1, # 1
b = [1], # ([1],)
c = (1) # 1
d = (1,) # (1,)
e = True if 10 / 2 > 2 else False, 1, 2, 3 # (True, 1, 2, 3)
print(a, b, c, d, e) # (1,) ([1],) 1 (1,) (True, 1, 2, 3)
在python
中,tuple
存在着一些隐式转换的问题。观察上面例子,如果你是个python
新手你会发现,除了d的值符合你的预期,别的结果都似乎不是你想的那样(注释的部分即为运行结果)。
符合一下几种条件的都会被隐式转换。
1、当一个表达式以逗号结尾的时候,那么这个表达式的结果会被转换为tuple
。如上述a,b。
2、当一个tuple里面只有一个元素,第一个元素后面没有使用逗号隔开的时候,那么这个tuple就会被转换成仅有的那个元素。如上述c和d。
3、当一个表达式有多个值,但表达式的接受者只有一个时,那么接受者会被转成tuple
类型。如上述e。
神奇的列表
item = [[]] * 3
print(item) # [[], [], []]
item[0].append(1)
print(item) # [[1], [1], [1]]
在上面例子中,明明只对0号索引的list
进行了append
操作,为什么最后所有索引位置的list都添加进了值。
注意看第一个表达式 item = [[]] * 3
, * 3 的操作是操作的外层的列表,而里面的list
没有发生变化。因为list是可变对象,每个list
里面的list
其实指向的都是同一个list
。