如果說優雅也有缺點的話,那就是你需要艱鉅的工作才能得到它,需要良好的教育才能欣賞它。 —— Edsger Wybe Dijkstra
筆者精心整理了許多實用的Python tricks,歡迎各位Pythonistia參考。
基本
f-string
name = 'alphardex'
f'Ore wa {name} desu, {4 * 6} sai, gakusei desu.'
# 'Ore wa alphardex desu, 24 sai, gakusei desu.'
複製程式碼
三元運算子
# if condition:
# fuck
# else:
# shit
fuck if condition else shit
複製程式碼
字串的拼接,反轉與分割
letters = ['h', 'e', 'l', 'l', 'o']
''.join(letters)
# 'hello'
letters.reverse()
# ["o", "l", "l", "e", "h"]
name = 'nameless god'
name.split(' ')
# ['nameless', 'god']
複製程式碼
判斷元素的存在性
'fuck' in 'fuck you'
# True
'slut' in ['bitch', 'whore']
# False
'company' in {'title': 'SAO III', 'company': 'A1 Pictures'}
# True
複製程式碼
函式
匿名函式
類似ES6的箭頭函式,函式的簡化寫法,配合map、filter、sorted等高階函式食用更佳
注:在Python中,一般更傾向於用列表推導式來替代map和filter
# def foo(parameters):
# return expression
foo = lambda parameters: expression
複製程式碼
map - 對映
numbers = [1, 2, 3, 4, 5]
list(map(lambda e: e ** 2, numbers))
# [1, 4, 9, 16, 25]
複製程式碼
filter - 過濾
values = [None, 0, '', True, 'alphardex', 666]
list(filter(lambda e:e, values))
# [True, "alphardex", 666]
複製程式碼
sort - 排序
tuples = [(1, 'kirito'), (2, 'asuna'), (4, 'alice'), (3, 'eugeo')]
sorted(tuples, key=lambda x: x[1])
# [(4, 'alice'), (2, 'asuna'), (3, 'eugeo'), (1, 'kirito')]
sorted(tuples, key=lambda x: x[1], reverse=True)
# [(1, 'kirito'), (3, 'eugeo'), (2, 'asuna'), (4, 'alice')]
tuples.sort()
tuples
# [(1, 'kirito'), (2, 'asuna'), (3, 'eugeo'), (4, 'alice')]
複製程式碼
其中,key這個引數接受一個函式,並將其運用在序列裡的每一個元素上,所產生的結果將是排序演算法依賴的對比關鍵字
在這個例子中,key裡面的函式獲取了每個元素的字串,排序演算法將會基於字母順序進行排序
排序預設是升序,把reverse設定為True,就能按降序排序
sorted會新建一個列表作為返回值,而list.sort則是就地排序
其他騷操作
from functools import reduce
# 求1到100的積
reduce(lambda x, y: x * y, range(1, 101))
# 求和就更簡單了
sum(range(101))
# 5050
複製程式碼
扁平化列表
from functools import reduce
li = [[1,2,3],[4,5,6], [7], [8,9]]
flatten = lambda li: [item for sublist in li for item in sublist]
flatten(li)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 或者直接用more_itertools這個第三方模組
# from more_itertools import flatten
# list(flatten(li))
複製程式碼
星號和雙星號
資料容器的合併
l1 = ['a', 'b']
l2 = [1, 2]
[*l1, *l2]
# ['a', 'b', 1, 2]
d1 = {'name': 'alphardex'}
d2 = {'age': 24}
{**d1, **d2}
# {'name': 'alphardex', 'age': 24}
複製程式碼
函式引數的打包與解包
# 打包
def foo(*args):
print(args)
foo(1, 2)
# (1, 2)
def bar(**kwargs):
print(kwargs)
bar(name='alphardex', age=24)
# {'name': 'alphardex', 'age': 24}
# 解包
t = (10, 3)
quotient, remainder = divmod(*t)
quotient
# 商:3
remainder
# 餘:1
複製程式碼
資料容器
列表
推導式
筆者最愛的語法糖:)
even = [i for i in range(10) if not i % 2]
even
# [0, 2, 4, 6, 8]
複製程式碼
同時迭代元素與其索引
用enumerate即可
li = ['a', 'b', 'c']
print([f'{i+1}. {elem}' for i, elem in enumerate(li)])
# ['1. a', '2. b', '3. c']
複製程式碼
元素的追加與連線
append在末尾追加元素,extend在末尾連線元素
li = [1, 2, 3]
li.append([4, 5])
li
# [1, 2, 3, [4, 5]]
li.extend([4, 5])
li
# [1, 2, 3, [4, 5], 4, 5]
複製程式碼
測試是否整體/部分滿足條件
all測試所有元素是否都滿足於某條件,any則是測試部分元素是否滿足於某條件
all([e<20 for e in [1, 2, 3, 4, 5]])
# True
any([e%2==0 for e in [1, 3, 4, 5]])
# False
複製程式碼
同時迭代2個以上的可迭代物件
用zip即可
subjects = ('nino', 'miku', 'itsuki')
predicates = ('saikou', 'ore no yome', 'is sky')
print([f'{s} {p}' for s, p in zip(subjects, predicates)])
# ['nino saikou', 'miku ore no yome', 'itsuki is sky']
複製程式碼
去重
利用集合的互異性
li = [3, 1, 2, 1, 3, 4, 5, 6]
list(set(li))
# [1, 2, 3, 4, 5, 6]
# 如果要保留原先順序的話用如下方法即可
sorted(set(li), key=li.index)
# [3, 1, 2, 4, 5, 6]
複製程式碼
解包
此法亦適用於元組等可迭代物件
最典型的例子就是2數交換
a, b = b, a
# 等價於 a, b = (b, a)
複製程式碼
用星號運算子解包可以獲取剩餘的元素
first, *rest = [1, 2, 3, 4]
first
# 1
rest
# [2, 3, 4]
複製程式碼
字典
遍歷
d = {'name': "alphardex", 'age': 24}
[key for key in d.keys()]
# ['name', 'age']
[value for value in d.values()]
# ['alphardex', 24]
[f'{key}: {value}' for key, value in d.items()]
# ['name: alphardex', 'age: 24']
複製程式碼
排序
import operator
data = [{'rank': 2, 'author': 'alphardex'}, {'rank': 1, 'author': 'alphardesu'}]
data_by_rank = sorted(data, key=operator.itemgetter('rank'))
data_by_rank
# [{'rank': 1, 'author': 'alphardesu'}, {'rank': 2, 'author': 'alphardex'}]
data_by_rank_desc = sorted(data, key=lambda x: x['rank'], reverse=True)
# [{'rank': 2, 'author': 'alphardex'}, {'rank': 1, 'author': 'alphardesu'}]
複製程式碼
反轉
d = {'name': 'alphardex', 'age': 24}
{v: k for k, v in d.items()}
# {'alphardex': 'name', 24: 'age'}
複製程式碼
缺失鍵處理
get返回鍵值,如果鍵不在字典中,將會返回一個預設值
d = {'name': 'alphardex', 'age': 24}
d.get('sex', 'male')
# male
複製程式碼
setdefault返回鍵值,如果鍵不在字典中,將會新增它並設定一個預設值
d = {'name': 'alphardex', 'age': 24}
# if 'sex' not in d:
# d['sex'] = 'male'
d.setdefault('sex', 'male')
# male
d
# {'name': 'alphardex', 'age': 24, 'sex': 'male'}
複製程式碼
分組
from collections import defaultdict
people = [('alphardex', 'male'), ('koizumi moeka', 'female'), ('alphardesu', 'male'), ('satou hinata', 'female')]
gender_group = defaultdict(list)
for name, gender in people:
gender_group[gender].append(name)
gender_group
# defaultdict(<class 'list'>, {'male': ['alphardex', 'alphardesu'], 'female': ['koizumi moeka', 'satou hinata']})
複製程式碼
例項化一個defaultdict時,通常要傳入一個可呼叫物件(這裡以list為例),它會在__getitem__找不到鍵時被呼叫,讓__getitem__返回某種預設值
向字典插入資料,如果鍵(假設為new_key
)不存在,則有以下三步:
- 呼叫list來新建一個列表
- 把這個新列表作為值,
new_key
作為它的鍵放到字典中 - 返回這個列表的引用
語言專屬特性
下劃線_的幾層含義
repl中暫存結果
>>> 1 + 1
# 2
>>> _
# 2
複製程式碼
忽略某個變數
filename, _ = 'eroge.exe'.split('.')
filename
# 'eroge'
for _ in range(2):
print('wakarimasu')
# wakarimasu
# wakarimasu
複製程式碼
i18n國際化
_("This sentence is going to be translated to other language.")
複製程式碼
增強數字的可讀性
1_000_000
# 1000000
複製程式碼
上下文管理器
用於資源的獲取與釋放,以代替try-except語句
常用於檔案IO,鎖的獲取與釋放,資料庫的連線與斷開等
# try:
# f = open(input_path)
# data = f.read()
# finally:
# f.close()
with open(input_path) as f:
data = f.read()
複製程式碼
可以用@contextmanager來實現上下文管理器
from contextlib import contextmanager
@contextmanager
def open_write(filename):
try:
f = open(filename, 'w')
yield f
finally:
f.close()
with open_write('onegai.txt') as f:
f.write('Dagakotowaru!')
複製程式碼
靜態型別註解
給函式引數新增型別,能提高程式碼的可讀性和可靠性,大型專案的最佳實踐之一
from typing import List
def greeting(name: str) -> str:
return f'Hello {name}.'
def gathering(users: List[str]) -> str:
return f"{', '.join(users)} are going to be raped."
print(greeting('alphardex'))
print(gathering(['Bitch', 'slut']))
複製程式碼
多重繼承
在django中經常要處理類的多重繼承的問題,這時就要用到super函式
如果單單認為super僅僅是“呼叫父類的方法”,那就錯了
其實super指的是MRO中的下一個類,用來解決多重繼承時父類的查詢問題
MRO是啥?Method Resolution Order(方法解析順序)
看完下面的例子,就會理解了
class A:
def __init__(self):
print('A')
class B(A):
def __init__(self):
print('enter B')
super().__init__()
print('leave B')
class C(A):
def __init__(self):
print('enter C')
super().__init__()
print('leave C')
class D(B, C):
pass
d = D()
# enter B
# enter C
# A
# leave C
# leave B
print(d.__class__.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
複製程式碼
首先,因為D繼承了B類,所以呼叫B類的__init__,列印了enter B
列印enter B
後的super尋找MRO中的B的下一個類,也就是C類,並呼叫其__init__,列印enter C
列印enter C
後的super尋找MRO中的C的下一個類,也就是A類,並呼叫其__init__,列印A
列印A
後回到C的__init__,列印leave C
列印leave C
後回到B的__init__,列印leave B
特殊方法
在django中,定義model的時候,希望admin能顯示model的某個欄位而不是XXX Object,那麼就要定義好__str__
每當你使用一些內建函式時,都是在呼叫一些特殊方法,例如len()呼叫了__len__(), str()呼叫__str__()等
以下實現一個數學向量類,裡面有6個特殊方法
from math import hypot
class Vector:
# 例項建立
def __init__(self, x=0, y=0):
self.x = x
self.y = y
# 字串表示形式
def __repr__(self) -> str:
return f'Vector({self.x}, {self.y})'
# 數值轉換 - 絕對值
def __abs__(self) -> float:
return hypot(self.x, self.y)
# 數值轉換 - 布林值
def __bool__(self) -> bool:
return bool(abs(self))
# 算術運算子 - 加
def __add__(self, other: Vector) -> Vector:
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
# 算術運算子 - 乘
def __mul__(self, scalar: float) -> Vector:
return Vector(self.x * scalar, self.y * scalar)
v = Vector(3, 4)
# repr(v) => v.__repr__()
v
# Vector(3, 4)
# abs(v) => v.__abs__()
abs(v)
# 5.0
# bool(v) => v.__bool__()
bool(v)
# True
# v1 + v2 => v1.__add__(v2)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v1 + v2
# Vector(4, 6)
# v * 3 => v.__mul__(3)
v * 3
# Vector(9, 12)
複製程式碼
想了解所有的特殊方法可查閱官方文件,以下列舉些常用的:
字串表示形式:__str__, __repr__
數值轉換:__abs__, __bool__, __int__, __float__, __hash__
集合模擬:__len__, __getitem__, __setitem__, __delitem__, __contains__
迭代列舉:__iter__, __reversed__, __next__
可呼叫模擬:__call__
例項建立與銷燬:__init__, __del__
運算子相關:__add__, __iadd__, __mul__, __imul__
複製程式碼