Python 的技巧和方法你瞭解多少?
學了這些你的python程式碼將會改善,你的技巧將會提高。
1. 路徑操作
比起os模組的path方法,python3標準庫的pathlib模組的Path處理起路徑更加的容易。
獲取當前檔案路徑
前提匯入os和pathlib包。。
os版:
print(os.path.dirname(__file__)) print(os.getcwd()) 複製程式碼
pathlib版:
print(pathlib.Path.cwd()) 複製程式碼
看著好像沒啥區別,然後看下面這個。
獲取上兩級檔案目錄
os版
print(os.path.dirname(os.path.dirname(os.getcwd()))) 複製程式碼
pathlib版
print(pathlib.Path.cwd().parent.parent) 複製程式碼
拼接路徑
os版
print(os.path.join(os.path.dirname(os.path.dirname(os.getcwd())),"yamls","a.yaml")) 複製程式碼
pathlib版
parts=["yamls","a.yaml"] print(pathlib.Path.cwd().parent.parent.joinpath(*parts)) 複製程式碼
執行時拼接路徑
os版
os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'yamls',f'{site_name}.yaml') 複製程式碼
pathlib版
parts=["yamls","a.yaml"] print(pathlib.Path(__file__).resolve().parent.parent.joinpath(*parts)) 複製程式碼
另外pathlib生成的是個物件,在open檔案操作中可以直接執行的但是如果當作字串操作會出現錯誤,此時需要對其進行轉換,使用os.fspath()即可,不過一般很少有操作路徑字串的習慣。
綜合起來,還是pathlib拼接路徑方便。
2. 儲存標準格式的yaml檔案
程式設計免不了要寫配置檔案,怎麼寫配置也是一門學問。
YAML 是專門用來寫配置檔案的語言,非常簡潔和強大,遠比 JSON 格式方便。
YAML在python語言中有PyYAML安裝包。
前提安裝第三方庫
pip install pyaml pip install ruamel.yaml 複製程式碼
關於yaml的讀取知識網上一堆了我就不說了,這裡主要說寫入。
from ruamel import yaml data={"age":23,"sex":"男","name":"牛皮"} with open(conf_file, "w", encoding='utf-8') as fs: yaml.dump(data, fs, Dumper=yaml.RoundTripDumper, allow_unicode=True) 複製程式碼
yaml寫檔案和json一樣也是使用dump。
3. 同時迭代兩個列表
以前的時候我是這麼解決的
a = ["a", "b", "c", "d"] b = [1, 2, 3]# 空的補充None for index, a_item in enumerate(a): b_item = None if len(b) - 1 <= index: pass else: b_item = b[index] print({a_item:b_item}) 複製程式碼
現在我通過itertools標準庫的zip升級版zip_longest解決,可以通過fillvalue引數補充缺失值。當然如果比較的元素個數相同可以直接用zip。
from itertools import zip_longest a = ["a", "b", "c", "d","e"] b = [1, 2, 3]# 空的補充None for a_item, b_item in zip_longest(a,b,fillvalue=0): print({a_item:b_item}) 複製程式碼
4. 三元表示式還能這麼用?
一般的我們這樣寫
a="hello" if 2>1 else "bye" print(a) 複製程式碼
我們知道python中false實際式0,true是1,所以對於上面的式子我們就可以這麼寫了。
a=["hello","bye"][2<1] print(a) 複製程式碼
因為2<1是false也就是0,所以輸出了第一個元素hello。
5.簡單的類使用namedtuple代替
先來一個簡單的例子
import collections # Person=collections.namedtuple('Person','name age') # 如果使用python中的關鍵字會出現錯誤,此時使用rename欄位。 # 按照元素在元組中的下標賦值。class就是_2,def是_3 Person = collections.namedtuple('Person', ['name', 'age', 'class', 'def', 'name', 'name'], rename=True) p = Person(name='lisa', age='12', _2="class2", _3="def", _4="name2", _5="name3") print(p) # 如果出現相同的欄位第二次出現的時候也是用其下標,參考上面的例子。 # _fields檢視欄位名,可以發現內建模組和重複的欄位標記為_加下標的形式 print(p._fields) # 使用_asdict將namedtuple轉為OrderedDict。 od = p._asdict() print(od) # 然後可以轉為字典 print(dict(od)) # _replace()方法構建一個新例項,因為namedtuple是不可變型別所以這個方法可以返回一個新的物件。 new_p = p._replace(name="samJ") print(new_p) print(new_p is p)# 可以看到不是同一個物件。 複製程式碼
一個實用的例子pyppeteer的例子感受下
import asyncio import pyppeteer from collections import namedtuple Response = namedtuple("rs", "title url html cookies headers history status") async def get_html(url, timeout=30): # 預設30s browser = await pyppeteer.launch(headless=True, args=['--no-sandbox']) page = awaitbrowser.newPage() res = await page.goto(url, options={'timeout': int(timeout * 1000)}) data = await page.content() title = await page.title() resp_cookies = await page.cookies() resp_headers = res.headers resp_history = None resp_status = res.status response = Response(title=title, url=url, html=data, cookies=resp_cookies, headers=resp_headers, history=resp_history, status=resp_status) return response if __name__ == '__main__': url_list = ["http://www.10086.cn/index/tj/index_220_220.html", "http://www.10010.com/net5/011/", "http://python.jobbole.com/87541/"] task = (get_html(url) for url in url_list) loop = asyncio.get_event_loop() results = loop.run_until_complete(asyncio.gather(*task)) for res in results: print(res.title) 複製程式碼
6 使用列舉讓數字變得更易懂。
import enum # 列舉 @enum.unique class Sex(enum.Enum): man = 12 woman = 13 # 因為加了唯一值的裝飾器所以下面新增屬性會報錯 # boy=12 print(Sex.man.name) print(Sex.woman.value) # 遍歷 for item in Sex: print(item.name) print(item.value) print("-" * 40) # 其他使用方式 words = enum.Enum( value='item', names=('a b c d e f'), ) # 輸出元素c,必須是上面names裡含有的值 print(words.c) print(words.f) # 因為names不含有w所以報錯 try: print(words.w) except AttributeError as e: print(e.args) print("-" * 40) for word in words: print(word.name, word.value)# 預設賦值為、從1開始自增。 print("-" * 40) # 如果自定義元素的值嘖改為一下元組的形式 words2 = enum.Enum( value='item2', names=[('a', 23), ('b', 56), ("c", 12), ("d", 333)] ) for word2 in words2: print(word2.name, word2.value) 複製程式碼
7 鏈式合併字典chainmap的使用
from collections import ChainMap # ChainMap d1 = {'a': 1, 'b': 2} d2 = {'a2': 3, 'b2': 4} d3 = {'a3': 5, 'b3': 6} d4 = {'a4': 7, 'b4': 8} c = ChainMap(d1, d2, d3, d4)# 多個字典合併為一個 for k, v in c.items(): print(k, v) print(c.maps)# 要搜尋的索引列表 c.maps = list(reversed(c.maps))# 逆轉對映列表 print(c) # 因為c和d1-d4對應的索引位置實際是一個所以,修改c的時候會影響到d1到d4其中餓的一個值,同理修改 # d1-d4的時候也會影響到c。 # 所以使用new_child建立一個新的對映。再修改就影響不到底層的資料了。 c2 = c.new_child() c2["a4"] = 100 print(c) print(c2) # 輸出發現c的值沒有發生變化,只要c2變化。 d5 = {"a5": 34, "b5": 78} c2 = c2.new_child(d5)# 可以在原來的對映基礎上新增新的對映 print(c2) 複製程式碼
8 在不打亂列表順序的基礎上插入元素
import bisect """ bisect 模組,用於維護有序列表。 bisect 模組實現了一個演算法用於插入元素到有序列表。 在一些情況下,這比反覆排序列表或構造一個大的列表再排序的效率更高。 Bisect 是二分法的意思,這裡使用二分法來排序,它會將一個元素插入到一個有序列表的合適位置, 這使得不需要每次呼叫 sort 的方式維護有序列表。 """ values = [14, 85, 77, 26, 50, 45, 66, 79, 10, 3, 84, 77, 1] print("New Pos Content") print("--- --- -------") l = [] for i in values: postion = bisect.bisect(l, i)# 返回插入的位置 bisect.insort(l, i)# 等於insort_right print('{:3}{:3}'.format(i, postion), l) """ Bisect模組提供的函式有: bisect.bisect_left(a,x, lo=0, hi=len(a)) : 查詢在有序列表 a 中插入 x 的index。lo 和 hi 用於指定列表的區間,預設是使用整個列表。如果 x 已經存在,在其左邊插入。返回值為 index。 bisect.bisect_right(a,x, lo=0, hi=len(a)) bisect.bisect(a, x,lo=0, hi=len(a)) : 這2個函式和 bisect_left 類似,但如果 x 已經存在,在其右邊插入。 bisect.insort_left(a,x, lo=0, hi=len(a)) : 在有序列表 a 中插入 x。和 a.insert(bisect.bisect_left(a,x, lo, hi), x) 的效果相同。 bisect.insort_right(a,x, lo=0, hi=len(a)) bisect.insort(a, x,lo=0, hi=len(a)) : 和 insort_left 類似,但如果 x 已經存在,在其右邊插入。 Bisect 模組提供的函式可以分兩類: bisect* 只用於查詢 index, 不進行實際的插入; 而 insort* 則用於實際插入。該模組比較典型的應用是計算分數等級: """ 複製程式碼
8 關於字典的邏輯運算你瞭解多少
# 使用&操作符檢視字典的相同之處 #字典鍵支援常見的集合操作,並集交集差集。 a = {'x': 1, 'y': 2, 'z': 3} b = {'w': 2, 'z': 4, 'x': 3, 'z': 3} # 獲取相同的鍵 c = a.keys() & b.keys() print(c) # 獲取相同的鍵值對 d = a.items() & b.items() print(d) # 建立一個新的字典並刪除某些鍵 e = {k: a[k] for k in a.keys() - {'z', 'x'}} print(e) 複製程式碼
9 給切片起個名字
a="safr3.14" print(a[-4:]) #上面可以改為 pie=slice(len(a)-4,len(a)) print(a) 複製程式碼
10 獲取出現頻率高的元素
from collections import Counter text = "abcdfegtehto;grgtgjri"# 可迭代物件 lis = ["a", "c", "d", "t", "b"] dic = {"a": 1, "b": 4, "c": 2, "d": 9}# 字典也可以 c = Counter()# 可以定義空容器然後update c.update(text) c2 = Counter() c2.update(dic) c3 = Counter(lis)# 也可以直接傳入物件 print(c) print(c2) print(c3) # 使用c.most_comman(n)獲取前n出現頻率最高的元素,列表元組型別 print(c.most_common(4)) 複製程式碼
更多工具使用以及python技巧,請關注公眾號:python學習開發。