1.Python传参之 * /
Keyword-only argument 是一种只能通过关键字指定而不能通过位置指定的参数。这迫使调用者必须指明,这个值是传给哪一个参数的。在函数的参数列表中,这种参数位于 * 符号的右侧。
Positional-only argument 是这样一种参数,它不允许调用者通过关键字来指定,而是要求必须按位置传递。这可以降低调用代码与参数名称之间的耦合程度。在函数的参数列表中,这些参数位于 / 符号的左侧。
在参数列表中, 位于 / 与 * 之间的参数,可以按位置指定,也可以用关键字来指定。这也是Python普通参数的默认指定方式。
注:以上特性由python3.8引入
2.Python推导式的使用原则
常见的有列表推导式、字典推导式
列表推导式:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
squares = [i**2 for i in a]
even_squares = [i**2 for i in a if i % 2 == 0]
一些原则:
- 控制推导逻辑的子表达式不要超过两个
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat_matrix = [i for row in matrix for i in row]
- 推导的时候每层循环可以使用多个if条件,如果这些if条件在同一层循环内,那么他们之间默认是and关系。
- 考虑使用生成器表达式改写数据量较大的列表推导
it = (len(i) for i in range(100000))
返回的it为一个迭代器,可以使用next(it)取得其中的数据
当然,生成器表达式还有另外一个强大的功能,就是可以组合使用,比如使用刚才生成器表达式返回的it作为输入,编写一条新的生成器表达式:
roots = ((x, x**0.5 for x in it))
这条生成器表达式形成的roots迭代器每次遍历时候,会引发连锁反应:它也推进内部迭代器it以判断是否还能在it上继续迭代,如果可以,就把it所返回的值传入(x, x**0.5)里面求出结果。
注:需要注意的是生成器表达式返回的生成器即迭代器是有状态的,跑完一轮之后,就不能继续使用了,需要重新生成才能再次使用。
4. 通过yield from 把多个生成器连接起来使用
多个生成器连续使用时候一般我们可以这么做:
例子:编写一个图形程序,让他在屏幕上移动,从而形成动画效果,假设实现这么一段动画,图片先快速移动一段时间,然后暂停,接下来慢速移动一段时间,为了把移动和暂停表示出来,可以定义下面两个生成器函数,让他们分别给出图片在当前时间内时间段内应该保持的速度
def move(period, speed):
for _ in range(period):
yield speed
def pause(period):
for _ in range(period):
yield 0
def animate():
for delta in move(4, 5.0):
yield delta
for delta in pause(3):
yield delta
for delta in move(2, 3.0):
yield delta
def render(delta):
print(f'Delta: {delta:.1f}')
def run(func):
for delta in func():
render(delta)
if __name__ == '__main__':
run(animate)
# Delta: 5.0
# Delta: 5.0
# Delta: 5.0
# Delta: 5.0
# Delta: 0.0
# Delta: 0.0
# Delta: 0.0
# Delta: 3.0
# Delta: 3.0
这种写法问题在于,animate函数里有很多重复的地方,代码比较繁琐。解决此问题可以使用yield from来实现
def animate_composed():
yield from move(4, 5.0)
yield from pause(3)
yield from move(2, 3.0)
run(animate_composed)
# 结果相同
使用yield from的好处在于代码看上去更清晰、直观。同时yield from的速度更快,相比较普通yield语句的for循环速度
接下来使用python内置的timeit模块编写运行一个micro-benchmark来测试下两者速度差别:
import timeit
def child():
for i in range(1_000_000):
yield i
def slow():
for i in child():
yield i
def fast():
yield from child()
def run_comparison():
baseline = timeit.timeit(
stmt='for _ in slow(): pass',
globals=globals(),
number=50
)
print(f'Manual nesting {baseline:.2f}s')
comparison = timeit.timeit(
stmt='for _ in fast(): pass',
globals=globals(),
number=50
)
print(f'Composed nesting {comparison:.2f}s')
reduction = -(comparison - baseline) / baseline
print(f'{reduction:.1%} less time')
if __name__ == '__main__':
run_comparison()
# Manual nesting 2.41s
# Composed nesting 2.29s
# 5.0% less time