Python進階之道

alphardex發表於2019-04-27

repo:github.com/alphardex/p…

如果說優雅也有缺點的話,那就是你需要艱鉅的工作才能得到它,需要良好的教育才能欣賞它。 —— 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)不存在,則有以下三步:

  1. 呼叫list來新建一個列表
  2. 把這個新列表作為值,new_key作為它的鍵放到字典中
  3. 返回這個列表的引用

語言專屬特性

下劃線_的幾層含義

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__
複製程式碼

相關文章