Python程序员鲜为人知但你应该知道的17个问题
一、不要使用可变对象作为函数默认值
...: def_list.append(value)
...: returndef_list
...:
In[2]:my_list=append_to_list(1)
In[3]:my_list
Out[3]:[1]
In[4]:my_other_list=append_to_list(2)
In[5]:my_other_list
Out[5]:[1,2]#看到了吧,其实我们本来只想生成[2]但是却把第一次运行的效果页带了进来
In[6]:importtime
In[7]:defreport_arg(my_default=time.time()):
...: print(my_default)
...:
In[8]:report_arg()#第一次执行
1399562371.32
In[9]:time.sleep(2)#隔了2秒
In[10]:report_arg()
1399562371.32#时间竟然没有变
这2个例子说明了什么?字典,集合,列表等等对象是不适合作为函数默认值的.因为这个默认值实在函数建立的时候就生成了,每次调用都是用了这个对象的”缓存”.我在上段时间的分享python高级编程也说到了这个问题,这个是实际开发遇到的问题,好好检查你学过的代码,也许只是问题没有暴露
可以这样改:
defappend_to_list(element,to=None):
iftoisNone:
to=[]
to.append(element)
returnto
二、生成器不保留迭代过后的结果
In[13]:2ingen
Out[13]:True
In[14]:3ingen
Out[14]:True
In[15]:1ingen
Out[15]:False#1为什么不在gen里面了?因为调用1->2,这个时候1已经不在迭代器里面了,被按需生成过了
In[20]:gen=(iforiinrange(5))
In[21]:a_list=list(gen)#可以转化成列表,当然a_tuple=tuple(gen)也可以
In[22]:2ina_list
Out[22]:True
In[23]:3ina_list
Out[23]:True
In[24]:1ina_list#就算循环过,值还在
Out[24]:True
三、lambda在闭包中会保存局部变量
In[29]:my_list=[lambda:iforiinrange(5)]
In[30]:forlinmy_list:
....: print(l())
....:
4
4
4
4
4
这个问题还是上面说的python高级编程中说过具体原因.其实就是当我赋值给my_list的时候,lambda表达式就执行了i会循环,直到i=4,i会保留
但是可以用生成器
In[31]:my_gen=(lambda:nforninrange(5))
In[32]:forlinmy_gen:
....: print(l())
....:
0
1
2
3
4
也可以坚持用list:
In[33]:my_list=[lambdax=i:xforiinrange(5)]#看我给每个lambda表达式赋了默认值
In[34]:forlinmy_list:
....: print(l())
....:
0
1
2
3
4
有点不好懂是吧,在看看python的另外一个魔法:
In[35]:defgroupby(items,size):
....: returnzip(*[iter(items)]*size)
....:
In[36]:groupby(range(9),3)
Out[36]:[(0,1,2),(3,4,5),(6,7,8)]
一个分组的函数,看起来很不好懂,对吧?我们来解析下这里
In[39]:[iter(items)]*3
Out[39]:
[<listiteratorat0x10e155fd0>,
<listiteratorat0x10e155fd0>,
<listiteratorat0x10e155fd0>]#看到了吧,其实就是把items变成可迭代的,重复三回(同一个对象哦),但是别忘了,每次都.next(),所以起到了分组的作用
In[40]:[lambdax=i:xforiinrange(5)]
Out[40]:
[<function__main__.<lambda>>,
<function__main__.<lambda>>,
<function__main__.<lambda>>,
<function__main__.<lambda>>,
<function__main__.<lambda>>]#看懂了吗?
四、在循环中修改列表项
In[45]:foriina:
....: ifnoti%2:
....: a.remove(i)
....:
In[46]:a
Out[46]:[1,3,5]#没有问题
In[50]:b=[2,4,5,6]
In[51]:foriinb:
....: ifnoti%2:
....: b.remove(i)
....:
In[52]:b
Out[52]:[4,5]#本来我想要的结果应该是去除偶数的列表
思考一下,为什么?是因为你对列表的remove,影响了它的index
In[53]:b=[2,4,5,6]
In[54]:forindex,iteminenumerate(b):
....: print(index,item)
....: ifnotitem%2:
....: b.remove(item)
....:
(0,2)#这里没有问题2被删除了
(1,5)#因为2被删除目前的列表是[4,5,6],所以索引list[1]直接去找5,忽略了4
(2,6)
五、IndexError-列表取值超出了他的索引数
In[55]:my_list=[1,2,3,4,5]
In[56]:my_list[5]#根本没有这个元素
---------------------------------------------------------------------------
IndexError Traceback(mostrecentcalllast)
<ipython-input-56-037d00de8360>in<module>()
---->1my_list[5]
IndexError:listindexoutofrange#抛异常了
In[57]:my_list[5:]#但是可以这样,一定要注意,用好了是trick,用错了就是坑啊
Out[57]:[]
六、重用全局变量
In[58]:defmy_func():
....: print(var)#我可以先调用一个未定义的变量
....:
In[59]:var="global"#后赋值
In[60]:my_func()#反正只要调用函数时候变量被定义了就可以了
global
In[61]:defmy_func():
....: var="locallychanged"
....:
In[62]:var="global"
In[63]:my_func()
In[64]:print(var)
global#局部变量没有影响到全局变量
In[65]:defmy_func():
....: print(var)#虽然你全局设置这个变量,但是局部变量有同名的,python以为你忘了定义本地变量了
....: var="locallychanged"
....:
In[66]:var="global"
In[67]:my_func()
---------------------------------------------------------------------------
UnboundLocalError Traceback(mostrecentcalllast)
<ipython-input-67-d82eda95de40>in<module>()
---->1my_func()
<ipython-input-65-0ad11d690936>inmy_func()
1defmy_func():
---->2 print(var)
3 var="locallychanged"
4
UnboundLocalError:localvariable"var"referencedbeforeassignment
In[68]:defmy_func():
....: globalvar#这个时候得加全局了
....: print(var)#这样就能正常使用
....: var="locallychanged"
....:
In[69]:var="global"
In[70]:
In[70]:my_func()
global
In[71]:print(var)
locallychanged#但是使用了global就改变了全局变量
七、拷贝可变对象
In[73]:my_list1
Out[73]:[[1,2,3],[1,2,3]]
In[74]:my_list1[1][0]="a"#我只修改子列表中的一项
In[75]:my_list1
Out[75]:[["a",2,3],["a",2,3]]#但是都影响到了
In[76]:my_list2=[[1,2,3]foriinrange(2)]#用这种循环生成不同对象的方法就不影响了
In[77]:my_list2[1][0]="a"
In[78]:my_list2
Out[78]:[[1,2,3],["a",2,3]]
八、python多继承(C3)
In[1]:classA(object):
...: deffoo(self):
...: print("classA")
...:
In[2]:classB(object):
...: deffoo(self):
...: print("classB")
...:
In[3]:classC(A,B):
...: pass
...:
In[4]:C().foo()
classA#例子很好懂,C继承了A和B,从左到右,发现A有foo方法,返回了
看起来都是很简单,有次序的从底向上,从前向后找,找到就返回.再看例子:
In[5]:classA(object):
...: deffoo(self):
...: print("classA")
...:
In[6]:classB(A):
...: pass
...:
In[7]:classC(A):
...: deffoo(self):
...: print("classC")
...:
In[8]:classD(B,C):
...: pass
...:
In[9]:D().foo()
classC#?按道理,顺序是D->B->A,为什么找到了C哪去了
这也就涉及了MRO(MethodResolutionOrder):
In[10]:D.__mro__
Out[10]:(__main__.D,__main__.B,__main__.C,__main__.A,object)
简单的理解其实就是新式类是广度优先了,D->B,但是发现C也是继承A,就先找C,最后再去找A
九、列表的+和+=,append和extend
In[18]:a_list+=[1] In[19]:print("ID(+=):",id(a_list)) In[20]:a_list=a_list+[2] In[21]:print("ID(list=list+...):",id(a_list)) In[29]:id(a_list) In[30]:a_list.append(1) In[31]:id(a_list) In[32]:a_list.extend([2]) In[33]:id(a_list) 十、datetime也有布尔值 In[35]:print(""datetime.time(0,0,0)"(Midnight)->",bool(datetime.time(0,0,0))) In[36]:print(""datetime.time(1,0,0)"(1am)->",bool(datetime.time(1,0,0))) In[38]:b=1 In[39]:print("aisb",bool(aisb)) In[40]:c=999 In[41]:d=999 In[42]:print("cisd",bool(cisd)) In[43]:print("256is257-1",256is257-1) In[44]:print("257is258-1",257is258-1) In[45]:print("-5is-6+1",-5is-6+1) In[46]:print("-7is-6-1",-7is-6-1) In[48]:b="helloworld!" In[49]:print("aisb,",aisb) In[50]:print("a==b,",a==b) In[52]:print("aisa,",aisa) In[53]:print("a==a,",a==a) In[66]:list2=list1#就是个引用,你操作list2,其实list1的结果也会变 In[67]:list3=list1[:] In[69]:importcopy In[70]:list4=copy.copy(list1)#他和list3一样都是浅拷贝 In[71]:id(list1),id(list2),id(list3),id(list4) In[72]:list2[0]=3 In[73]:print("list1:",list1) In[74]:list3[0]=4 In[75]:list4[1]=4 In[76]:print("list1:",list1) #再看看深拷贝和浅拷贝的区别 In[88]:fromcopyimportcopy,deepcopy In[89]:list1=[[1],[2]] In[90]:list2=copy(list1)#还是浅拷贝 In[91]:list3=deepcopy(list1)#深拷贝 In[92]:id(list1),id(list2),id(list3) In[93]:list2[0][0]=3 In[94]:print("list1:",list1) In[95]:list3[0][0]=5 In[96]:print("list1:",list1) 十三、bool其实是int的子类 In[98]:True+True In[99]:3*True+True In[100]:3*True-False In[104]:True<<10 十五、元组是不是真的不可变? In[112]:tup[0]+=[1] TypeError:"tuple"objectdoesnotsupportitemassignment In[113]:tup In[114]:tup=([],) In[115]:tup[0].extend([1]) In[116]:tup[0] 这里有个不错的解释Python"s+=IsWeird,PartII: In[118]:my_tup+=(4,) In[119]:my_tup=my_tup+(5,) In[120]:my_tup In[121]:my_tup=(1,) In[122]:print(id(my_tup)) In[123]:my_tup+=(4,) In[124]:print(id(my_tup)) In[125]:my_tup=my_tup+(5,) In[126]:print(id(my_tup)) 十六、python没有私有方法/变量?但是可以有”伪”的 In[132]:my_instance=my_class() In[133]:my_instance.public_method() In[134]:my_instance._my_class__private_method() In[135]:my_instance.call_private_method_in_class() In[136]:my_instance._my_class__private_variable 十七、异常处理加else In[154]:whilei<2: In[156]:whilei<2: In[159]:foriinrange(2):
("ID:",4481323592)
("ID(+=):",4481323592)#使用+=还是在原来的列表上操作
("ID(list=list+...):",4481293056)#简单的+其实已经改变了原有列表
In[28]:a_list=[]
Out[29]:4481326976
Out[31]:4481326976#append是在原有列表添加
Out[33]:4481326976#extend也是在原有列表上添加
这是一个坑
In[34]:importdatetime
(""datetime.time(0,0,0)"(Midnight)->",False)
(""datetime.time(1,0,0)"(1am)->",True)
十一、"=="和is的区别
我的理解是”is”是判断2个对象的身份,==是判断2个对象的值
In[37]:a=1
("aisb",True)
("cisd",False)#原因是python的内存管理,缓存了-5-256的对象
("256is257-1",True)
("257is258-1",False)
("-5is-6+1",True)
("-7is-6-1",False)
In[47]:a="helloworld!"
("aisb,",False)#很明显他们没有被缓存,这是2个字段串的对象
("a==b,",True)#但他们的值相同
#But,有个特例
In[51]:a=float("nan")
("aisa,",True)
("a==a,",False)#亮瞎我眼睛了~
十二、浅拷贝和深拷贝
我们在实际开发中都可以向对某列表的对象做修改,但是可能不希望改动原来的列表.浅拷贝只拷贝父对象,深拷贝还会拷贝对象的内部的子对象
In[65]:list1=[1,2]
Out[71]:(4480620232,4480620232,4479667880,4494894720)
("list1:",[3,2])
("list1:",[3,2])#对list3和list4操作都没有对list1有影响
Out[92]:(4494896592,4495349160,4494896088)
("list1:",[[3],[2]])#看到了吧假如你操作其子对象还是和引用一样影响了源
("list1:",[[3],[2]])#深拷贝就不会影响
In[97]:isinstance(True,int)
Out[97]:True
Out[98]:2
Out[99]:4
Out[100]:3
Out[104]:1024
---------------------------------------------------------------------------
TypeError Traceback(mostrecentcalllast)
<ipython-input-112-d4f292cf35de>in<module>()
---->1tup[0]+=[1]
Out[113]:([1],)#我靠又是亮瞎我眼睛,明明抛了异常还能修改?
Out[116]:[1]#好吧,我有点看明白了,虽然我不能直接操作元组,但是不能阻止我操作元组中可变的子对象(list)
In[117]:my_tup=(1,)
Out[120]:(1,4,5)#?嗯不是不能操作元组嘛?
4481317904
4480606864#操作的不是原来的元组所以可以
4474234912
In[127]:classmy_class(object^E):
.....: defpublic_method(self):
.....: print("Hellopublicworld!")
.....: def__private_method(self):#私有以双下划线开头
.....: print("Helloprivateworld!")
.....: defcall_private_method_in_class(self):
.....: self.__private_method()
Hellopublicworld!#普通方法
Helloprivateworld!#私有的可以加"_+类名字+私有方法名字”
Helloprivateworld!#还可以通过类提供的公有接口内部访问
Out[136]:1
In[150]:try:
.....: print("thirdelement:",a_list[2])
.....:exceptIndexError:
.....: print("raisedIndexError")
.....:else:
.....: print("noerrorintry-block")#只有在try里面没有异常的时候才会执行else里面的表达式
.....:
raisedIndexError#抛异常了没完全完成
In[153]:i=0
.....: print(i)
.....: i+=1
.....:else:
.....: print("inelse")
.....:
0
1
inelse#while也支持哦~
In[155]:i=0
.....: print(i)
.....: i+=1
.....: break
.....:else:
.....: print("completedwhile-loop")
.....:
0#被break了没有完全执行完就不执行else里面的了
In[158]:foriinrange(2):
.....: print(i)
.....:else:
.....: print("completedfor-loop")
.....:
0
1
completedfor-loop
.....: print(i)
.....: break
.....:else:
.....: print("completedfor-loop")
.....:
0#也是因为break了相关文章