python中的這些坑,早看早避免。
說一說python中遇到的坑,躲坑看這一篇就夠了
傳遞引數時候不要使用列表
def foo(num,age=[]):
age.append(num)
print("num",num)
return age
print(foo(1))
print(foo(2))
print(foo(3))
複製程式碼
上面的程式碼輸出的結果並不是我們預期的那樣,列印出三個陣列[1],[2],[3]。
而是下面這樣.
num 1
[1]
num 2
[1, 2]
num 3
[1, 2, 3]
複製程式碼
為什麼會這樣呢,原因就是引數age是一個列表,列表是一個可變物件,同時在作為函式引數時,相當於全域性變數,在函式預處理時就已經分配了記憶體空間。那麼我們如何修改呢?
其實很簡單隻要不讓列表引數作為列表,一般可變型別物件作為引數的時候預設都是給定None,然後根據物件判斷是否為空,如果為空再去定義成列表,修改如下:
def foo(num, age=None):
if not age:
age = []
age.append(num)
print("num", num)
return age
print(foo(1))
print(foo(2))
print(foo(3))
複製程式碼
for。。。else的使用場景
在刷pythontip的時候遇到這道題,覺得很有必要和大家普及下for。。else的用處,好了下面我們開始:
輸出100以內的所有素數,素數之間以一個空格區分(注意,最後一個數字之後不能有空格)。
#在一般領域,對正整數n,如果用2到根號N之間的所有整數去除,均無法整除,則n為質數又叫素數。
import math
num = [] #存放1-100之間的素數
for i in range(2, 100):
for j in range(2, int(math.sqrt(i)) + 1):
if i % j == 0:
break
else:
num.append(i) #根據定義如果都無法正常才加入
for index, i in enumerate(num):
if index == len(num) - 1:
print(i)
else:
print(i, end=" ")
複製程式碼
根據關鍵語句「所有整數去除,均無法整除,則n為質數又叫素數。」,轉化成程式也就是說在所有的的數字都迴圈完了,還不能出才作為質數,也就是最後的那個else,體現了這句話。由此可以看出for。。else還是挺重要的。
字典賦值
看下面的程式碼,猜想輸出結果:
a = {}
a[1] = "A"
a[1.0] = "B"
a[2] = "C"
print(a)
複製程式碼
如果不知道字典裡的鍵和hash有關,就不會知道結果是下面這個樣子
{1: 'B', 2: 'C'}
複製程式碼
這是為什麼呢?
因為,Python中的字典是通過檢查鍵值是否相等以及雜湊值來確定兩個鍵是否相同.
具有相同值的不可變物件在Python中始終具有相同的雜湊值.
因為
1=1.0
所以hash(1)==hash(1.0).同樣的我們知道python中的true相等,我們試著
計算其hash值可以看到hash(1)==hash(True)。
由此我們可以得到如下等式:
hash(1)==hash(1.0)==hash(True)
複製程式碼
因為只不可變物件才存在hash值所以 hash([])不存在。同樣我們可以推斷出
hash(0) == hash(False) == hash("")
複製程式碼
根據PEP285中Review部分第6條所述,bool類其實是從int類繼承而來.
print(isinstance(True, int))
複製程式碼
關於if判斷條件正確寫法
python3中0=[]=()={}=None=False="",所以當我們在判斷列表,或者字典字串是否為空的時候不用再使用 a==None,這樣的語句了。
a=[]
b={}
c=""
if not a:
print("a不為空")
if not b:
print("b不為空")
if not c:
print("c不為空")
複製程式碼
同樣的程式碼少寫
一般我們寫if判斷的時候,我們都寫成下面這種形式:
if type == "A":
print(1)
elif type == "B":
print(2)
複製程式碼
像這樣的我們需要寫好多重複程式碼的程式,此時就要考慮是否優化了,針對這種情況我們可以優先考慮字典。
my_dict = {"A":1, "B":2} #etc
print(my_dict[type])
複製程式碼
另外我們在使用給物件的屬性賦值的時候
class A():
def __init__(self,dicts):
self.name=dicts["name"]
self.age=dicts["age"]
self.sex=dicts["sex"]
self.hobby=dicts["hobby"]
if __name__ == '__main__':
dicts={"name":"lisa","age":23,"sex":"women","hobby":"hardstyle"}
a=A(dicts)
複製程式碼
我們看到我們需要換取傳入的字典的各個鍵值,並建立鍵值同名一個屬性,這裡我們只有4個還好,想象一下如果我們傳入的字典有100個鍵。。。如何還是這樣一個一個賦值不敢想不敢想,人家都寫完程式碼了,你還在賦值有木有。。
其實一開始的那段程式碼已經給出了答案,如果不會也沒關係,
下面我們就來點pythonic的python。來解決這個問題。
上面程式碼簡化為:
class A():
def __init__(self,dicts):
self.__dict__.update(dicts)
print(self.__dict__)
if __name__ == '__main__':
dicts={"name":"lisa","age":23,"sex":"women","hobby":"hardstyle"}
a=A(dicts)
複製程式碼
小心閉包中的坑,python的惰性計算
我們觀察下面的程式碼
ls = []
for x in range(5):
ls.append(lambda: x**2)
print(ls[0]())
print(ls[1]())
print(ls[2]())
複製程式碼
我們以為它會輸出[0],[1],[4].但實際情況是。。。。。
16
16
16
複製程式碼
這是什麼鬼?
其實這和python的惰性求值有關。惰性求值,也就是延遲求值,表示式不會在它被繫結到變數之後就立即求值,而是等用到時再求值。x實際不在lambda的作用域中。只有當lambda被呼叫時,x的值才會被傳給它。也就是最後的一次迴圈中x為4,後面的ls[1],ls[1],ls[2],ls[3]實際都是16。同時這是面試常考的一個點,希望大家牢記。
這個問題考察了閉包。
執行檔案路徑和當前路徑
執行檔案的路徑和當前的路徑這是兩個概念
獲取檔案的當前路徑時可以的使用
import os
os.getcwd()
複製程式碼
但是在需要執行的檔案的獲取其執行路徑的時候就最好不要用這個了。
一般使用下面這種方式,動態的獲取路徑
import sys
sys.path[0]
複製程式碼
使用eval轉整的時候數字前不能有0
eval("02")
複製程式碼
會發生錯誤:
Traceback (most recent call last):
File "/demo/1.py", line 1, in <module>
eval("02")
File "<string>", line 1
02
^
SyntaxError: invalid token
複製程式碼
While 1比While True快?
python2的時候是這樣,以為python3 True=1所以結果實際是一樣的。
由於Python2中,True/False不是關鍵字,因此我們可以對其進行任意的賦值,這就導致程式在每次迴圈時都需要對True/False的值進行檢查;而對於1,則被程式進行了優化,而後不會再進行檢查。
Python3中,由於True/False已經是關鍵字了,不允許進行重新賦值,因此,其執行結果與while 1不再有區別
處理長的字串
對於長的字串我們一般使用"""多文字"""的形式,但是換行的時候容易導致哪裡出錯,此時可以考慮在外面加個小括號,像這樣
("""多文字""")
複製程式碼
關於requests模組的編碼問題
作者實際上提供了個自動識別網頁編碼的程式碼,在獲取res(請求的物件),獲取原始碼之前使用
下面的程式碼即可獲取正確的網站編碼。
res.encoding=res.apparent_encoding
複製程式碼
更多工具使用以及python技巧,請關注公眾號:python學習開發。
如果您喜歡我的文章不防動動小手轉發一波,謝謝。
點選閱讀原文進入我的部落格園,看程式碼更方便。
由於人數超過100所以需要新增我微信:italocxa,然後拉您入群。