Skip to content
0

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