Python 的一些高级特性

好的代码应当是简洁易懂易维护的,所以代码不是越多越好,也不是越复杂越好。
在工作中,如果刻意得去炫技,故意写一些复杂的、难以看懂的代码,会导致这代码只有你自己能看懂,甚至一段时间之后,你自己也会看不懂。
等你离职了,下一个接手项目的人在看代码的时候会非常痛苦,心里一定在骂你。
要达到这样的要求,除了给代码写注释,最好掌握一些 Python 内置的、高效的高级特性。

切片

切片的语法是这样:[start:end:d]
其中:

  • start:起始下标(能取到)
  • end:结束下标(取不到)
  • d:差值(默认为 1)

截取 str、list 或 tuple 的部分元素是很常见的需求。

demo_str = "python"
demo_list = ["p", "y", "t", "h", "o", "n"]
demo_tuple = ("p", "y", "t", "h", "o", "n")

用切片取前三个元素:

demo_str[0:3] # 'pyt'
demo_list[:3] # ['p', 'y', 't']

切片的取值区间是左闭右开[0:3] 是区间 [0, 3)。第一个索引如果是 0,可简写成这样:[:3]

下标当然也可以是负数,比如:

demo_str[-1:-3:-1] # 'no'

起始下标为 -1,对应元素 'n';结束下标为 -3,对应元素 'h',取不到,所以取到元素 'n'。差值 -1 的正负号其实可以理解为取值的方向,负号就是按照负方向取值。

根据这个例子可以写出一个反转字符串的功能:

demo_str[-1::-1] # 'nohtyp'

迭代

迭代就是遍历。
判断一个对象是否可迭代可以这样:

from collections.abc import Iterable

isinstance("python", Iterable) # True
isinstance(123, Iterable) # False

遍历的时候想要拿到下标可以这样:

for index, value in enumerate("python"):
    print(index)
    print(value)

列表推导式

如果要得到一个这样的列表:[0, 1, 2, 3, 4],常规的方法是这样:

demo_list = []
for i in range(5):
    demo_list.append(i)
print(demo_list) # [0, 1, 2, 3, 4]

列表推导式其实就是把这个代码简写成一行:

demo_list = [i for i in range(5)]
print(demo_list) # [0, 1, 2, 3, 4]

可以对每个追加的值做处理:

demo_list = [i + 1 for i in range(5)]
print(demo_list) # [1, 2, 3, 4, 5]

如果对于有判断条件的也是可以简写的,比如要得到 [1, 3, 5, 7, 9]

demo_list = []
for i in range(11):
    if i % 2 != 0:
        demo_list.append(i)
print(demo_list) # [1, 3, 5, 7, 9]

列表推导式写法:

demo_list = [i for i in range(11) if i % 2 != 0]
print(demo_list) # [1, 3, 5, 7, 9]

不支持 else 条件。

两层循环也是可以的:

demo_list = []
for i in range(2):
    for j in range(2):
        demo_list.append(i + j)
print(demo_list) # [0, 1, 1, 2]

用列表推导式简写为:

demo_list = [i + j for i in range(2) for j in range(2)]
print(demo_list) # [0, 1, 1, 2]

三层及以上的循环虽然也可以,但是写出来太长,不如不用。

生成器

当列表元素有几百万时,会很占内存,性能很差。平时练习时基本不会遇到,但实际的项目中这种情况很常见。
这时最好有一种算法,能够推导出列表的每个值,这样既可以拿到元素又可以节省内存,提高性能,这个算法就是生成器。

把列表推导式的 [] 改为 () 就创建了一个生成器。

demo_str = [i for i in range(5)]
print(demo_str) # [0, 1, 2, 3, 4]
demo_str = (i for i in range(5))
print(demo_str) # <generator object <genexpr> at 0x7f2918149f20>

依次访问生成器元素

next(demo_str) # 0
next(demo_str) # 1
next(demo_str) # 2
next(demo_str) # 3
next(demo_str) # 4
next(demo_str) # 报错:StopIteration

遍历生成器

for i in demo_str:
    print(i)
# 0
# 1
# 2
# 3
# 4

生成器只能访问一次,再次访问就没有值了。

可以将函数改写成生成器,拿生成斐波那契数列实验:

从第三个数开始,每个数都等于前两个数之和。

常规函数:

def fib(first:int, second:int, max_value:int):
    print(first)
    print(second)
    num = second
    while num < max_value:
        first, second = second, num
        num = first + second
        if num >= max_value:
            break
        print(num)
# fib(1, 1, 10)
# 1
# 1
# 2
# 3
# 5
# 8

改写成生成器:

def fib(first:int, second:int, max_value:int):
    yield first
    yield second
    num = second
    while num < max_value:
        first, second = second, num
        num = first + second
        if num >= max_value:
            break
        yield num

result = fib(1, 1, 10)
for i in result:
    print(i)
# 1
# 1
# 2
# 3
# 5
# 8

判断一个对象是否是生成器

from collections.abc import Generator

demo_list = (i for i in range(5))
print(isinstance(demo_list, Iterator)) # True

判断是否是迭代器是 isinstance(demo_list, Iterator)

IteratorIterable 不是一回事:

list、str、dict、生成器都是 Iterable,但 list、str、dict 不是 Iterator

(本文完)

上一篇:单片机学习(三)开发板动态数码管的控制


下一篇:2021-07-30