乐趣区

关于python:Python-代码工程实践-循环

应用生成式改良循环语句

@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,你永远有更好的抉择。

防止应用递归

除非递归的实现形式比循环的形式更加简洁搞笑,并且易读,否则不要应用递归。不可否认的是,递归是高效的解决某些问题的办法,然而我的倡议是,除非对本人的代码和算法能力有充沛的自信,并且打算进行充沛的测试(单元测试或者其余测试),否则不要应用递归,它可能会带来保护上的艰难。

退出移动版