Python中一些深不见底的“坑”
Python是一门清晰简洁的语言,如果你对一些细节不了解的话,就会掉入到那些深不见底的“坑”里,下面,我就来总结一些Python里常见的坑。
列表创建和引用
嵌套列表的创建
使用*号来创建一个嵌套的list:
li=[[]]*3 print(li) #Out:[[],[],[]]
通过这个方法,可以得到一个包含3个list的嵌套list,我们来给第一个list增加一个元素:
li[0].append(1) print(li) #Out:[[1],[1],[1]]
通过输出的结果可以看初,我们只给第一元素增加元素,结果三个list都增加了一个元素。这是因为[[]]*3并不是创建了三个不同list,而是创建了三个指向同一个list的对象,所以,当我们操作第一个元素时,其他两个元素内容也会发生变化的原因。效果等同于下面这段代码:
li=[] element=[[]] li=element+element+element print(li) #Out:[[],[],[]] element.append(1) print(li) #Out:[[1],[1],[1]]
我们可以打印出元素的内存地址一探究竟:
li=[[]]*3 print([id(inner_list)forinner_listinli]) #Out:[6830760,6830760,6830760]
到这我们可以明白原因了。那如何解决了?可以这样:
li=[[]for_inrange(3)]
这样我们就创建了三个不同的list对象
print([id(inner_list)forinner_listinli]) #Out:[6331048,6331528,6331488]
列表元素的引用
不要使用索引方法遍历list,例如:
foriinrange(len(tab)): print(tab[i])
比较好的方法是:
forelemintab: print(elem)
for语句会自动生成一个迭代器。如果你需要索引位置和元素,使用enumerate函数:
fori,eleminenumerate(tab): print((i,elem))
注意==符号的使用
if(var==True): #当var是:True、1、1.0、1L时if条件成立 if(var!=True): #当var不是True和1时if条件成立 if(var==False): #当var是False或者0(or0.0,0L,0j)if条件成立 if(var==None): #var是Noneif条件成立 ifvar: #当var非空(None或者大小为0)对象string/list/dictionary/tuple,non-0等if条件成立 ifnotvar: #当var空(None或者大小为0)对象string/list/dictionary/tuple,non-0等if条件成立 ifvarisTrue: #只有当var时True时if条件成立1也不行 ifvarisFalse: #只有当var时False时if条件成立0也不行 ifvarisNone: #和var==None一致
捕获异常由于提前检查
不够优雅的代码:
ifos.path.isfile(file_path): file=open(file_path) else: #dosomething
比较好的做法:
try: file=open(file_path) exceptOSErrorase: #dosomething
在python2.6+的里面可以更简洁:
withopen(file_path)asfile:
之所以这么用,是这么写更加通用,比如file_path给你传个None就瞎了,还得判断是不是None,如果不判断,就又得抓异常,判断的话,代码有多写了很多。
类变量初始化
不要在对象的init函数之外初始化类属性,主要有两个问题
- 如果类属性更改,则初始值更改。
- 如果将可变对象设置为默认值,您将获得跨实例共享的相同对象。
错误示范(除非你想要静态变量)
``` classCar(object): color="red" wheels=[Wheel(),Wheel(),Wheel(),Wheel()] ```
正确的做法:
``` classCar(object): def__init__(self): self.color="red" self.wheels=[Wheel(),Wheel(),Wheel(),Wheel()] ``` **函数默认参数** ``` deffoo(li=[]): li.append(1) print(li) foo([2]) #Out:[2,1] foo([3]) #Out:[3,1] ```
该代码的行为与预期的一样,但如果我们不传递参数呢?
``` foo() #Out:[1]Asexpected... foo() #Out:[1,1]Notasexpected... ```
这是因为函数参数类型是定义是确认的而不是运行时,所以在两次函数调用时,li指向的是同一个list对象,如果要解决这个问题,可以这样:
``` deffoo(li=None): ifnotli: li=[] li.append(1) print(li) foo() #Out:[1] foo() #Out:[1] ```
这虽然解决了上述的问题,但,其他的一些对象,比如零长度的字符串,输出的结果就不是我们想要的。
``` x=[] foo(li=x) #Out:[1] foo(li="") #Out:[1] foo(li=0) #Out:[1] ```
最常用的办法是检查参数是不是None
``` deffoo(li=None): ifliisNone: li=[] li.append(1) print(li) foo() #Out:[1] ``` **在遍历时修改**
for语句在遍历对象是会生成一个迭代器,如果你在遍历的过程中修改对象,会产生意想不到的结果:
alist=[0,1,2] forindex,valueinenumerate(alist): alist.pop(index) print(alist) #Out:[1]
第二个元素没有被删除,因为迭代按顺序遍历索引。上述循环遍历两次,结果如下:
#Iteration#1 index=0 alist=[0,1,2] alist.pop(0)#removes'0' #Iteration#2 index=1 alist=[1,2] alist.pop(1)#removes'2' #loopterminates,butalistisnotempty: alist=[1]
如果避免这个问题了,可以创建另外一个list
alist=[1,2,3,4,5,6,7] forindex,iteminreversed(list(enumerate(alist))): #deleteallevenitems ifitem%2==0: alist.pop(index) print(alist) #Out:[1,3,5,7]
整数和字符串定义
python预先缓存了一个区间的整数用来减少内存的操作,但也正是如此,有时候会出很奇特的错误,例如:
>>>-8is(-7-1) False >>>-3is(-2-1) True
另外一个例子
>>>(255+1)is(255+1) True >>>(256+1)is(256+1) False
通过不断的测试,会发现(-3,256)这区间的整数都返回True,有的甚至是(-8,257)。默认情况下,[-5,256]会在解释器第一次启动时创建并缓存,所以才会有上面的奇怪的行为。这是个很常见但很容易被忽略的一个坑。解决方案是始终使用equality(==)运算符而不是identity(is)运算符比较值。
Python还保留对常用字符串的引用,并且可以在比较is字符串的身份(即使用)时产生类似的混淆行为。
>>>'python'is'py'+'thon' True
python字符串被缓存了,所有python字符串都是该对象的引用,对于不常见的字符串,即使字符串相等,比较身份也会失败。
>>>'thisisnotacommonstring'is'thisisnot'+'acommonstring' False >>>'thisisnotacommonstring'=='thisisnot'+'acommonstring' True
所以,就像整数规则一样,总是使用equal(==)运算符而不是identity(is)运算符比较字符串值。
列表推导和循环中的变量泄漏
有个例子:
i=0 a=[iforiinrange(3)] print(i)#Outputs2
python2中列表推导改变了i变量的值,而python3修复了这个问题:
i=0 a=[iforiinrange(3)] print(i)#Outputs0
类似地,for循环对于它们的迭代变量没有私有的作用域
i=0 foriinrange(3): pass print(i)#Outputs2
这种行为发生在Python2和Python3中。
为了避免泄漏变量的问题,请在列表推导和for循环中使用新的变量。
or操作符
例如
ifa==3orb==3orc==3:
这个很简单,但是,再看一个:
ifaorborc==3:#Wrong
这是由于or的优先级低于==,所以表达式将被评估为if(a)or(b)or(c==3):。正确的方法是明确检查所有条件:
ifa==3orb==3orc==3:#RightWay
或者,可以使用内置函数any()代替链接or运算符:
ifany([a==3,b==3,c==3]):#Right
或者,为了使其更有效率:
ifany(x==3forxin(a,b,c)):#Right
更加简短的写法:
if3in(a,b,c):#Right
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。