如果说优雅也有缺点的话,那就是你需要艰巨的工作才能得到它,需要良好的教育才能欣赏它。 —— 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__
复制代码