应用生成式改良循环语句
@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
,你永远有更好的抉择。
防止应用递归
除非递归的实现形式比循环的形式更加简洁搞笑,并且易读,否则不要应用递归。不可否认的是,递归是高效的解决某些问题的办法,然而我的倡议是,除非对本人的代码和算法能力有充沛的自信,并且打算进行充沛的测试(单元测试或者其余测试),否则不要应用递归,它可能会带来保护上的艰难。