用Python编写干净的代码
#编程 #生产率 #python #codequality

Python是一种编程语言,具有高水平的灵活性。同行是,开发人员可以轻松地使用不同的技巧,这些技巧将导致源代码中的异质性,从而降低其可读性和可维护性。与任何编程语言一样,团队中的define best practices很重要,以使源代码保持一致,避免错误并节省code reviews期间的时间。

Promyze上,我们最近与合作伙伴Arolla(重播是in French)在上进行了网络研讨会,如何在Python中编写干净的代码? /p>

nb:请注意,我们声称以下做法始终有效,而且没有示例总是不好的。相信自己;)

#1使用计数器计算出现

当您想计算列表,元组或另一个可触觉的元素出现时,在运行时使用collection库的计数器更有效:

from collections import Counter
array = [1, 1, 2, 3, 4, 5, 3, 2, 3, 4, 2, 1, 2, 3]
counts = Counter(array)
print(counts)
# Will print => Counter({2: 5, 3: 4, 1: 3, 4: 2, 5: 1})

#2使用“在”中简化语句

关键字 in 是一种优雅,可读和可维护的方法,可以在序列中检查特定元素的存在:

detectives = ["Sherlock Holmes", "Hercule Poirot", "Batman"]
person = "Batman"

# Don't
if person == "Batman" or person == "Hercule Poirot" or person == "Sherlock Holmes":
    print("That person is a detective")

# Do 
if person in detectives:
    print("That person is a detective")

#3在断言中预期的是实际的

主张将更容易按以下顺序阅读:

def test_big_stuff():
    actual_result = ...
    expected_result = ...

    assert actual_result == expected_result

#4 相关时使用属性

有时,当我们创建一个类时,我们将拥有一个字段,其价值源于一个或多个其他字段。例如,在class person 中,我们可以拥有一个full_name字段,该字段将first_namelast_name的值串联。

在这种情况下,重要的是通过用注释 @property 定义属性来保护复合字段的内容。回到我们的班级 person 的示例,这将阻止用户通过编写person.full_name = ...从郊外设置full_name的值。

class Person:
      def __init__(self, first_name, last_name):
            self.first_name = first_name
            self.last_name = last_name

      @property
      def full_name(self):
          return f"{self.first_name} {self.last_name}"

#5使用完全合格的绝对进口

这使代码更可读取和可维护,因此,当修改代码时,更容易弄清代码中的每个对象来自何处。

性能方面,它基本上与导入完整模块(ex。import foo)的基本相同,因为Python始终加载完整的模块,无论我们是否只是导入该模块的对象。

也就是说,如果我们编写from foo.bar import Bar时,python会加载整个模块foo.bar,然后继续选择Bar

from foo.bar import Bar
from spam.eggs import Eggs

def main():
    bar = Bar()
    eggs = Eggs()

#6使用迭代器而不是明确列表

避免在不相关时创建新列表。

def get_max():
    iterable = ["a", "bbb", "c"]
    # Don't
    max_len = max([len(x) for x in iterable])
    # Do
      max_len = max(len(x) for x in iterable)
    assert max_len == 3

get_max()

#7使用列表综合

a 列表理解 是一种通过从现有迭代中转换元素(例如列表,元组或字典)来创建新列表的一种方式,我们希望要过滤一些元素并对每个元素执行操作。

# Don't
def get_even_nums_squared():
    nums = [1, 2, 3, 4, 5, 6]
    res = []
    for num in nums:
        if num % 2 == 0:
            res.append(num * num)
    return res

# Do
def get_even_nums_squared():
    nums = [1, 2, 3, 4, 5, 6]
    return [x * x for x in nums if x % 2 == 0]

#8更喜欢使用仅关键词的参数

多次,尤其是在函数或方法的参数之间没有逻辑顺序时,建议通过指定参数名称来调用函数或方法(例如,make_coffee(with_sugar=True, with_milk=True))。

可以在调用函数/方法时强制命名参数。我们可以通过在参数开始时使用*来做到这一点。

这避免了许多可能的问题和混乱。

但是,这不是一直要做的事情,而是在有意义的时候。

而不是:

def make_coffee(with_sugar=False, with_milk=False):
    pass

make_coffee(True, True)

我们更喜欢:

def make_coffee(*, with_sugar=False, with_milk=False):
    pass

make_coffee(with_milk=True, with_sugar=True)

#9使用abcmeta进行抽象课程

,如果您与不是Python专家但对Java或C#更熟悉的开发人员合作,则这种做法可能很重要。他们对课堂和方法的抽象概念更加满意。

ABCMeta 是一种在Python中的元类(创建类的类)。它代表“抽象基类元”。

而不是:

class Fooer:
    def foo(self):
        raise NotImplementedError() 

class Spam(Fooer):
    def foo(self):
        print("spamming")

我们更喜欢:

from abc import ABCMeta, abstractmethod

class Fooer(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass

class Spam(Fooer):
    def foo(self):
        print("spamming foos")

#10使用main()函数

避免全局变量,通常是源代码外部函数。

而不是:

from server import Server

HOST = "127.0.0.1"
PORT = 8080

SERVER = Server()

if __name__ == "__main__":
    SERVER.start(HOST, PORT)

我们更喜欢:

from server import Server

def main():
    host  = "127.0.0.1"
    port = 8080

    Server = Server()
    Server.start(host, port)

if __name__ == "__main__":
    main()

#11 请勿将空列表作为默认参数

这可能导致出乎意料且非常奇怪的行为。

而不是:

def add_player_to_team(player, team=[]):
    team.append(player)
    print(team)

我们更喜欢::

def add_player_to_team(player, team=None):
    if team is None:
        team = []

    team.append(player)
    print(team)

#12 更喜欢f弦而不是字符串串联

f-string允许以比字符串串联提供的更自然(公认的烦人)方式写句子。

做:

first_name = "Jake"
last_name = "Sully"
age = 28

message = f"{first_name} {last_name} is {age} years old now"
print(message)

不要:

first_name = "Jake"
last_name = "Sully"
age = 28

message = first_name + " " + last_name + " is " + str(age) + " years old now"
print(message)

#13 更喜欢枚举()而不是范围(len())当您要保留峰值项目的索引

这种做法有助于代码可读性及其性能,同时仍保持索引。性能增益是因为 enumerate()创建了该集合的迭代器,该迭代器比通过每个项目循环

更有效

不要:

ages = [1, 2, 18, 24, 8]
for i in range(len(ages)):
    if ages[i] >= 18:
        print(f"I'm client n°{i+1} and I'm {age} years old, I'm an adult now.")

做:

ages = [1, 2, 18, 24, 8]
for i, age in enumerate(ages):
    if age >= 18:
        print(f"I'm client n°{i+1} and I'm {age} years old, I'm an adult now.")

所有这些最佳实践都可以从我们的IDE和代码评论插件中定义。我们与Vscode,Jetbrains Suite和Eclipse兼容。因此,如果您使用VSCODE或PYCHARM编码Python,则可以选择它!然后,您创建的每种练习都将在专门的研讨会期间作为团队进行验证,最终结果看起来像:

An example of best practice in Promyze

您可以提供句法模式,以在编码或审查代码时提供建议,并在Promyze的入职研讨会期间使用此练习。

整个目录也可在我们的公共Hub of best practices上获得,用户可以在各种域上共享实践并在Promyze中使用它们。

您现在可以开始创建实践for free