python代码规范

Python Language Rules

参考:Google Python Style Guide
Imports:
在使用import时应该只针对包和模块, 这样可以区分x.Obj 中, x是模块而Obj是对象。

  • Use import x for importing packages and modules.
  • Use from x import y where x is the package prefix and y is the module name with no prefix.
  • Use from x import y as z if two modules named y are to be imported or if y is an inconveniently long name.
  • Use import y as z only when z is a standard abbreviation (e.g., np for numpy).

注意:不要使用相对路径的import,即使模块属于同一个包中,也要使用包的全名,这样可以避免无意的导入一个包两次。

Packages:
导入每一个模块都要用模块的全部路径名。
正确:

# Reference absl.flags in code with the complete name (verbose).
import absl.flags
from doctor.who import jodie

FLAGS = absl.flags.FLAGS
# Reference flags in code with just the module name (common).
from absl import flags
from doctor.who import jodie

FLAGS = flags.FLAGS

错误:
假定这个文件和jodie .py 都在doctor/who/目录下

# Unclear what module the author wanted and what will be imported.  The actual
# import behavior depends on external factors controlling sys.path.
# Which possible jodie module did the author intend to import?
import jodie

Exceptions:
异常被允许但应该仔细使用。

  • raise应该这样使用, raise MyError('Error message') or raise MyError(),而不应该这样使用raise MyError, 'Error message'
  • 使用内置的异常类,如ValueError, 如果要判断一个异常的发生要使用raise, 不要使用assert
Yes:
  def connect_to_next_port(self, minimum):
    """Connects to the next available port.

    Args:
      minimum: A port value greater or equal to 1024.
    Raises:
      ValueError: If the minimum port specified is less than 1024.
      ConnectionError: If no available port is found.
    Returns:
      The new minimum port.
    """
    if minimum < 1024:
      raise ValueError('Minimum port must be at least 1024, not %d.' % (minimum,))
    port = self._find_next_open_port(minimum)
    if not port:
      raise ConnectionError('Could not connect to service on %d or higher.' % (minimum,))
    assert port >= minimum, 'Unexpected port %d when minimum was %d.' % (port, minimum)
    return port
    ```
    No:
  def connect_to_next_port(self, minimum):
    """Connects to the next available port.

    Args:
      minimum: A port value greater or equal to 1024.
    Returns:
      The new minimum port.
    """
    assert minimum >= 1024, 'Minimum port must be at least 1024.'
    port = self._find_next_open_port(minimum)
    assert port is not None
    return port
  • 当异常继承自库或包时,异常的名字应该以Error结尾,而不应该foo.FooError
  • 最小化try/except块中的代码量。尝试的主体越大,异常被一行您不希望引发异常的代码引发的可能性就越大。在这些情况下,try/except块隐藏了一个真正的错误。
  • 无论try块中是否有异常发生,都可以使用finally语句执行代码, 着经常是有用的对于cleanup 或者关闭文件
  • 当捕捉一个异常时用as
try:
 raise Error()
except Error as error:
 pass

Global variables:
避免使用全局变量,因为在模块import时可能会改变模块的行为。允许使用模块级的常量, 如MAX_HOLY_HANDGRENADE_COUNT = 3。如果需要,应该在模块级别声明全局,并通过在名称前面加上一个_u使其成为模块内部。外部访问必须通过公共模块级功能完成。

Comprehensions & Generator Expressions:
可以用于简单的案例。每个部分必须适合一行:mapping expression, for clause, filter expression。不允许使用多个for子句或ilter expression。当事情变得更复杂时,请使用循环。

Yes:
  result = [mapping_expr for value in iterable if filter_expr]

  result = [{'key': value} for value in iterable
            if a_long_filter_expression(value)]

  result = [complicated_transform(x)
            for x in iterable if predicate(x)]

  descriptive_name = [
      transform({'key': key, 'value': value}, color='black')
      for key, value in generate_iterable(some_input)
      if complicated_condition_is_met(key, value)
  ]

  result = []
  for x in range(10):
      for y in range(5):
          if x * y > 10:
              result.append((x, y))

  return {x: complicated_transform(x)
          for x in long_generator_function(parameter)
          if x is not None}

  squares_generator = (x**2 for x in range(10))

  unique_names = {user.name for user in users if user is not None}

  eat(jelly_bean for jelly_bean in jelly_beans
      if jelly_bean.color == 'black')
No:
result = [complicated_transform(
             x, some_argument=x+1)
         for x in iterable if predicate(x)]

result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]

return ((x, y, z)
       for x in xrange(5)
       for y in xrange(5)
       if x != y
       for z in xrange(5)
       if y != z)

Default Iterators and Operators:
使用默认的iterators(如字典, 列表)或operators(如in或not in)\

Yes:  for key in adict: ...
      if key not in adict: ...
      if obj in alist: ...
      for line in afile: ...
      for k, v in adict.items(): ...
No:   for key in adict.keys(): ...
     if not adict.has_key(key): ...
     for line in afile.readlines(): ...

Lambda Functions:
只在一行时使用,在字符超过60-80时使用函数, 对于常用操作,如乘法, 使用operator中定义的函数,如使用operator.mul 而不是lambda x, y: x * y.

Conditional Expressions:
只在一行时使用,x = 1 if cond else 2

Default Argument Values:
默认值在多数情况下都可以,但默认值不能是可变对象,因为如果函数中对该元素进行了更改,那么默认值也会随之更改。

Yes: def foo(a, b=None):
         if b is None:
             b = []
Yes: def foo(a, b: Optional[Sequence] = None):
         if b is None:
             b = []
Yes: def foo(a, b: Sequence = ()):  # Empty tuple OK since tuples are immutable
		...
No:  def foo(a, b=[]):
         ...
No:  def foo(a, b=time.time()):  # The time the module was loaded???
         ...
No:  def foo(a, b=FLAGS.my_thing):  # sys.argv has not yet been parsed...
         ...

Properties:
当对类中简单属性进行读或设置时,可以使用property特性,增加可读性。

Yes: import math

     class Square(object):
         """A square with two properties: a writable area and a read-only perimeter.

         To use:
         >>> sq = Square(3)
         >>> sq.area
         9
         >>> sq.perimeter
         12
         >>> sq.area = 16
         >>> sq.side
         4
         >>> sq.perimeter
         16
         """

         def __init__(self, side):
             self.side = side

         @property
         def area(self):
             """Gets or sets the area of the square."""
             return self._get_area()

         @area.setter
         def area(self, area):
             return self._set_area(area)

         def _get_area(self):
             """Indirect accessor to calculate the 'area' property."""
             return self.side ** 2

         def _set_area(self, area):
             """Indirect setter to set the 'area' property."""
             self.side = math.sqrt(area)

         @property
         def perimeter(self):
             return self.side * 4

True/False evaluations:
对于可能的应用场景,使用隐含的False, 因为Python中0, None, [], {}, ''都为False

  • 当比较None时,不要使用==, !=, 而要使用is , not is
  • 当使用if x, 意味着if x is not None.
  • 不要使用==比较布尔量和False, 应该使用if not x, 如果要区分FalseNone使用if not x and x is not None
  • 对于序列(string, list, tuple), 使用if seq if not seq来判断序列是否为空,而不应该使用if len(seq) if not len(seq)
  • '0'字符的布尔量为True
  • 已知结果为整数了,要跟0进行比较
Yes: if not users:
         print('no users')

     if foo == 0:
         self.handle_zero()

     if i % 10 == 0:
         self.handle_multiple_of_ten()

     def f(x=None):
         if x is None:
             x = []
No:  if len(users) == 0:
        print('no users')

    if foo is not None and not foo:
        self.handle_zero()

    if not i % 10:
        self.handle_multiple_of_ten()

    def f(x=None):
        x = x or []

Deprecated Language Features:
使用字符串方法而不是使用string模块,使用函数调用语法而不是使用apply , 使用list推导或for循环而不是使用含有filter maplambda表达式,使用for循环而不是使用reduce

Yes: words = foo.split(':')

     [x[1] for x in my_list if x[2] == 5]

     map(math.sqrt, data)    # Ok. No inlined lambda expression.

     fn(*args, **kwargs)

No:  words = string.split(foo, ':')

     map(lambda x: x[1], filter(lambda x: x[2] == 5, my_list))

     apply(fn, args, kwargs)

Lexical Scoping(作用域):
嵌套的python函数可以引用封闭函数中定义的变量,但不能分配给它们。对块中某个名称的任何赋值都将导致python将对该名称的所有引用视为局部变量,即使在赋值之前使用。如果发生全局声明,则将名称视为全局变量。

i = 4
def foo(x):
    def bar():
        print(i, end='')
    # ...
    # A bunch of code here
    # ...
    for i in x:  # Ah, i *is* local to foo, so this is what bar sees
        print(i, end='')
    bar()

Sofoo([1, 2, 3]) will print 1 2 3 3, not 1 2 3 4.

Function and Method Decorators:
在装饰器有明显优势时使用装饰器,避免使用staticmethod 有限制的使用classmethod, 装饰器是在执行import时执行的.
只有在编写命名构造函数或类特定的例程(修改必要的全局状态,如进程范围的缓存)时才使用@classmethod。
除非强制使用@staticmethod以便与现有库中定义的API集成,否则不要使用。改为编写模块级函数。
Power Features:
避免使用,如custom metaclasses, access to bytecode, on-the-fly compilation, dynamic inheritance, object reparenting, import hacks, reflection (e.g. some uses of getattr()), modification of system internals, etc.
标准库或类中使用这些特性是可以的,如abc.ABCMeta, collections.namedtuple, dataclasses, and enum

Python代码规范通常遵循PEP 8(Python Enhancement Proposal 8)标准,下面是一些常见的Python代码规范: 1. 缩进:使用4个空格进行缩进,不要使用制表符。 2. 行长度:每行代码应尽量控制在79个字符以内,可以使用括号进行换行。 3. 空行:在函数和类定义之间、函数内的逻辑块之间使用空行进行分隔,以提高可读性。 4. 导入语句:每个导入语句应独占一行,按照标准库、第三方库和本地库的顺序进行分组。 5. 命名规范:变量名、函数名和模块名应使用小写字母,单词之间使用下划线进行分隔。类名应使用驼峰命名法。 6. 注释:使用注释来解释代码的功能、实现思路等。注释应该清晰、简洁,并且避免使用无意义的注释。 7. 函数和方法:函数和方法的命名应该清晰、简洁,并且能够准确描述其功能。函数和方法的参数应该有明确的命名,并且避免使用单个字符作为参数名。 8. 类:类的命名应该使用驼峰命名法,并且首字母大写。类应该有一个简洁明确的目的,并且遵循单一职责原则。 9. 异常处理:在可能发生异常的地方进行适当的异常处理,并且避免使用裸露的except语句。 10. 其他规范:避免使用全局变量,尽量使用局部变量;避免使用魔术数字,使用常量代替;避免使用复杂的表达式,尽量拆分为多个简单的表达式。 以上是一些常见的Python代码规范,遵循这些规范可以提高代码的可读性和可维护性。如果你还有其他问题,请继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值