使用生成式改进循环语句
@dataclass
class Employee:
name: str
age: int
salary: int
address: str = field(default="")
role: str = field(default="")
def find_developers(employees: Sequence[Employee]) -> List[Employee]:
developers = []
for employee in employees:
if employee.role == "developer":
developers.append(employee)
return developers
列表生成式
使用列表生成式进行重构:
def find_developers(employees: Sequence[Employee]) -> List[Employee]:
return [e for e in employees if e.role == "developer"]
生成式的优势不仅在于语法简单,将多行代码合并成一行,它的性能更是比使用 for 循环语句要好。具体好多少,可以自行搜索,也可以自己测试。
请记住一点,当对列表或者序列进行简单变形或者过滤时,使用列表生成式由于使用for循环。当循环条件较为复杂时,请继续使用 for 循环,保持代码的可读性。
生成器推导式
使用生成器推导式,只需要将[]
替换成()
即可,比如
def find_developers(employees: Sequence[Employee]) -> List[Employee]:
return (e for e in employees if e.role == "developer")
注意!生成器的使用特点是“迭代时计算”,也就是说,当生成器被创建时,内存中是没有新数据的,只存有转换方式而已。只有当其被迭代时,才会从内存中逐个读取数据,根据转换方式进行计算。所以,生成器的优势是根据需要占用内存,而不是提前在内存中准备好数据。
当需要提前在内存中准备好数据时,不应该使用生成器推导式,而应该使用列表推导式。
当多次对生成器进行迭代时,可能会出现生成器枯竭问题。看下面代码
developers = find_developers(employees=employees)
for developer in developers:
print(developer.name)
for developer in developers:
print(developer.salary)
developers
变量是一个生成器,我们对其进行了两次迭代,第一次迭代会预期的打印出名字,但是第二次迭代时什么都不会执行。所以,请记住一点,不要对一个生成器进行多次迭代。
生成器枯竭的问题在实际工作中,还算容易发现,但是另一种问题,就更难发现,比如下面的:(假设employees是按照 id 从小到大排序后的序列)
developers = find_developers(employees=employees)
for developer in developers:
if developer.salary > 10000:
print(developer.name)
break
for developer in developers:
if developer.age > 30:
print(developer.name)
break
这里有两次循环,我们期望在第一次循环中找到第一个工资大于 10000 的 developer,第二次循环中,我们期望找到所有员工中第一个年龄大于 30 的developer。但是实际情况是,第一次循环按照预期找到了第一个工资大于 10000 的developer,但是第二个循环,找到的是在这个工资大于 10000 的 developer 之后,第一个年龄大于 30 的 developer,而这跟预期是不符合的。
使用高阶函数
上面的例子中,生成器推导式可以使用高阶函数 filter
来替代
def find_developers(employees: Sequence[Employee]) -> Generator[Employee]:
return filter(lambda e: e.role == "developer", employees)
我们可以对其返回值进行迭代。我们也可以定义自己的高阶函数来实现更加灵活的过滤规则
def find_developers(employees: Sequence[Employee], predicate: Callable) -> Generator[Employee]:
return (e for e in employees if predicate(e))
find_developers(employees, lambda e: e.role == "developer")
find_developers(employees, lambda e: e.role == "developer" and e.age > 30)
同样的,我们可以使用map函数实现对可迭代对象的变形操作,在这里不提供示例了。
尽量避免使用while True
while True
是无尽循环,在使用时需要格外注意退出条件,否则会一直执行下去,除非这是预期的行为。大多数情况下,除非是编写常驻进程的程序,否则不应该期望程序不会退出。所以在这个大前提下,应该尽量避免使用while True
来进行循环。
如果是需要进行轮询操作,推荐通过 retry 机制(我们项目的框架中提供retry装饰器,想自己实现一个也很简单),并且设置明确的执行次数。
记住一点,除非明确的知道程序应该无休止的运行下去,否则不要使用while True
,你永远有更好的选择。
避免使用递归
除非递归的实现方式比循环的方式更加简洁搞笑,并且易读,否则不要使用递归。不可否认的是,递归是高效的解决某些问题的方法,但是我的建议是,除非对自己的代码和算法能力有充分的自信,并且计划进行充分的测试(单元测试或者其他测试),否则不要使用递归,它可能会带来维护上的困难。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。