主题
Python编程技巧
优雅,永不过时!
—— 卡蜜尔
分解序列到变量
有多个元素的序列或元组,如何将它里面的值同时赋给多个变量?
python
point = (4, 5)
x, y = p
print(x, y) # 4 5
data = ['Python', 50, 91.1, (2012, 12, 21)]
# 用不到的变量可以使用_占位
name, _, _, date = data
print(date) # (2012, 12, 21)
name, shares, price, (year, mon, day) = data
# 还可使用 *+变量名 保存部分序列
name, *tmp, date = data
print(tmp) # [50, 91.1]
提示
使用*
形式解压出的变量永远都是列表类型,即使原对象是元组或文本等其它可迭代对象。
获取第一个满足条件的元素
如果你想获取可迭代对象中满足条件的第一个元素,使用 next 函数将会非常优雅!
python
numbers = [3, 7, 8, 2, 21]
# 获取并立即返回列表中的第一个偶数
print(next(i for i in numbers if i % 2 == 0))
同时遍历序列
你有多个序列,现在想同时遍历获取元素:
python
list1 = [1, 2, 3]
list2 = [5, 6, 7, 8]
for index in range(min(len(list1), len(list2))):
print(list1[index], list2[index])
python
list1 = [1, 2, 3]
list2 = [5, 6, 7, 8]
for l1, l2 in zip(list1, list2):
print(l1, l2)
对于长度不同的序列,你想按长度最长的遍历,则可以使用 zip_longest 函数:
python
from itertools import zip_longest
list1 = [1, 2, 3]
list2 = [5, 6, 7, 8]
# fillvalue 为缺失数据的填充值
for l1, l2 in zip_longest(list1, list2, fillvalue=0):
print(l1, l2)
# 1 5
# 2 6
# 3 7
# 0 8
既然说到了 itertools 库,那就说说里面几个很优雅的工具函数。
优雅的itertools
chain
zip 是同时遍历,而 chain 则是链式遍历,即把多个可迭代对象平铺展开遍历输出。
python
list1 = [1, 2, 3]
list2 = [5, 6, 7, 8]
for i in chain(list1, list2):
print(i, end=" ") # 输出为:1 2 3 5 6 7 8
dropwhile
你需要丢弃可迭代对象中从头开始条件为 True 所有元素,直到遇到条件为 False 则开始输出。
python
seq = ["#1", "##","IG", "RNG", "#2"]
for data in dropwhile(lambda s: s.startswith("#"), seq):
print(data, end=" ") # 输出为:IG RNG #2
takewhile
与 dropwhile 相反:从头开始输出可迭代对象中条件为 True 的所有元素,直到遇到条件为 False 则停止迭代。
python
seq = ["#1", "##", "IG", "RNG", "#2"]
for data in takewhile(lambda s: s.startswith("#"), seq):
print(data, end=" ") # 输出为:#1 ##
itertools
库中还有一些其它的函数,但我个人比较少用,你要是有兴趣可以去研究研究。
动态加载对象
你想在代码运行时动态加载对象,则可以使用 importlib 内建库。scrapy 中的代码可以参考下:
python
from importlib import import_module
from typing import Any
from collections.abc import Callable
def load_object(path: str | Callable[..., Any]) -> Any:
"""Load an object given its absolute object path, and return it.
The object can be the import path of a class, function, variable or an
instance, e.g. 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware'.
If ``path`` is not a string, but is a callable object, such as a class or
a function, then return it as is.
"""
if not isinstance(path, str):
if callable(path):
return path
raise TypeError(
f"Unexpected argument type, expected string or object, got: {type(path)}"
)
try:
dot = path.rindex(".")
except ValueError:
raise ValueError(f"Error loading object '{path}': not a full path")
module, name = path[:dot], path[dot + 1 :]
mod = import_module(module)
try:
obj = getattr(mod, name)
except AttributeError:
raise NameError(f"Module '{module}' doesn't define any object named '{name}'")
return obj