Python从入门到进阶(三)——编码规范与模块管理

1 编码规范

1 代码编码格式

  • 声明编码格式在脚本中是必需的
  • 国际惯例,文件编码和 Python 编码格式全部为 utf-8 。例如:在 Python 代码的开头,要统⼀加上如下代码:
# -- coding:utf-8 --
  • 如果Python源码文件没有声明编码格式,Python解释器会默认使用ASCII编码。但出现非ASCII编码的字符,Python解释器就会报错,因此非 ASCII 字符的字符串,请需添加u前缀。
  • 若出现 Python编码问题,可按照以下操作尝试解决:
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

2 分号

不要在行尾加分号,也不要用分号将两条命令放在同一行

3 行的最大长度

每行不超过80个字符
以下情况除外:
1.长的导入模块语句
2.注释里的URL
不要使用反斜杠连接行
Python会将圆括号,中括号和花括号中的行隐式的连接起来.
圆括号,方括号或花括号以内的表达式允许分成多个物理行,无需使用反斜杠.
例如:

onth_names = ['Januari', 'Februari', 'Maart',      # These are the
               'April',   'Mei',      'Juni',       # Dutch names
               'Juli',    'Augustus', 'September',  # for the months
               'Oktober', 'November', 'December']   # of the year

隐式的行拼接可以带有注释.后续行的缩进不影响程序结构.后续行也允许为空白行
如果需要,可以在表达式外围增加一对额外的圆括号.
如果一个文本字符串在一行放不下,可以使用圆括号来实现隐式行连接

x = ('这是一个非常长非常长非常长非常长 '
     '非常长非常长非常长非常长非常长非常长的字符串')

4 缩进规则

  • Python采用代码缩进和冒号(:)来区分代码块之间的层次
  • 在Python中,对于类定义,函数定义,流程控制语句等,行尾的冒号和下一行的缩进,表示下一行代码块的开始,而缩进的结束则表示此代码块的结束.
  • Python中实现对代码的缩进,可以使用空格或者Tab键实现.但无论是手动敲空格,还是使用Tab键,通常情况下都是采用4个空格长度作为一个缩进量(默认情况下,一个Tab键就表示4个空格).
  • 对于Python缩进规则,Python要求属于同一作用域中的各行代码,它们缩进量必须一致,但具体缩进量为多少,并不做硬性规定.

正确示例:

if a==0:
	print('正确') # 缩进4个空白占位
else:			  # 与if对齐
	print('错误') # 缩进4个空白占位

错误示例:

if a==0:
    print("正确") 
else:              
    print("错误")   
 print("end")       # 错误的是这行代码前面加了一个空格

# 或者
def long_function_name(a=1,b=2,c=3,d=4):
	return a+b+c+d
	
foo = long_function_name(
  var_one, var_two, var_three, # 2 个空格是禁止的
  var_four)						# 2 个空格是禁止的

5 注释

  • Python中使用#进行注释,#号后面要空一格
  • 最需要写注释的是代码中那些技巧性的部分:对于复杂的操作,应该在其操作开始前写上若干行注释;对于不是一目了然地代码,应该在其行尾添加注释.
  • 为了提高可读性,注释和代码隔开一定的距离,注释应该至少离开代码两个空格,块注释后面最好多留几行空格再写代码
  • 当代码更改时,优先更新对应的注释.
  • 如果一个注释是一个短语或者句子,它的第一个单词应该大写,除非它是以小写字母开头的标识符.
  • 如果注释很短,结尾的句号可以省略,块注释一般由完整句子的一个或多个段落组成,并且每句话结束有个句号.
  • 在句尾结束的时候应该使用两个空格.

Python中有三种形式的注释:

  1. 行注释
    由#和一个空格开始.行内注释和代码至少要有两个空格分隔
print('请输入一个数字n:')
n=int(input())
print('请输入一个数字m:')
m=int(input())
t=n/2	# t是n的一半
# 循环,条件为t*m/(n+1) 小于n
while(t*m/(n+1)<n):
	t=0.5*m+n/2  # 重新计算t值
print(t)
  1. 块注释
  • 块注释通常适用于跟随它们的某些(或全部)代码,并缩进到与代码相同的级别
  • 块注释的每一行开头使用一个 # 和一个空格(除非块注释内部缩进文本)。
  • 块注释内部的段落通常只有一个 # 的空行分隔。
def FuncName(parameter1,parameter2):

"""

描述函数要做的事情

:param parameter1: 参数一描述(类型、用途等)

:param parameter2: 参数二描述

:return: 返回值描述

"""

# We use a weighted dictionary search to find out where i is in
# the array.  We extrapolate position based on the largest num
# in the array and the array size and then do binary search to
# get the exact number.

if i & (i-1) == 0:        # true if i is a power of 2

3.文档注释

  • 要为所有的公共模块,函数,类和方法编写文档说明
  • 非公共的方法没有必要,但是应该有一个描述方法具体作用的注释.这个注释应该在def那一行之后
  • 多行文档注释使用的结尾三引号应该是自成一行
class SampleClass(object):
    """Summary of class here.

    Longer class information....
    Longer class information....

    Attributes:
        likes_spam: A boolean indicating if we like SPAM or not.
        eggs: An integer count of the eggs we have laid.
    """

    def __init__(self, likes_spam=False):
        """Inits SampleClass with blah."""
        self.likes_spam = likes_spam
        self.eggs = 0

    def public_method(self):
        """Performs operation blah."""

6 空行

  • 顶层函数和类定义,前后用两个空行隔开
  • 编码格式声明、模块导入、常量和全局变量声明、顶级定义和执行代码之间空两行
  • 类里面方法定义用一个空行隔开
  • 在函数或方法内部,可以在必要的地方空一行以增强节奏感,但应避免连续空行
class Class01:
    pass
 
 
class Class02:
    def function_01(self):
        pass
 
    def function_02(self):
        pass

使用必要的空行可以增加代码的可读性,通常在顶级定义(如函数或类的定义)之间空两行,而方法定义之间空一行,另外在用于分隔某些功能的位置也可以空一行。

7 制表符还是空格

  • 不要混用制表符和空格,因为如果混用了,虽然在编辑环境中显示两条语句为同一缩进层次,但因为制表符和空格的不同会导致 Python 解释为两个不同的层次。
  • 在调用 Python 命令行解释器时使用 -t 选项,可对代码中不合法的混合制表符和空格发出警告,使用 -tt 时警告将变成错误,这些选项是被高度推荐的。但是强烈推荐仅使用空格而不是制表符。

空格使用规则:

  • 在二元运算符两边各空一格,比如赋值(=)、比较(==, <, >, !=, <>, <=, >=, in, not in, is, is not), 布尔(and, or, not),算术操作符两边的空格可灵活使用,但两侧务必要保持一致
  • 不要在逗号、分号、冒号前面加空格,但应该在它们后面加(除非在行尾)
  • 函数的参数列表中,逗号之后要有空格
  • 函数的参数列表中,默认值等号两边不要添加空格
  • 左括号之后,右括号之前不要加添加空格
  • 参数列表, 索引或切片的左括号前不应加空格
  • 当’='用于指示关键字参数或默认参数值时,不要在其两侧使用空格

正确示例代码:

spam(ham[1], {eggs: 2}, [])

if x == 4:
    print x, y
x, y = y, x

dict['key'] = list[index]

def complex(real, imag=0.0): return magic(r=real, i=imag)

8 命名规范

模块名命名

  • 模块尽量使用小写命名,首字母保持小写,尽量不要用下划线(除非多个单词,且数量不多的情况)
# 正确

import decoder

import html_parser

# 不推荐

import Decoder

变量命名

  • 不要使用字母I (小写的L), O (大写的O), I (大写的I)作为单字符的变量名。在有些字体里面,这些字符无法与数字0和1区分。如果想用I, 可使用L代替。
  • 变量名尽量小写, 如有多个单词,用下划线隔开。
count=0
this_is_var=0

常量或者全局变量命名

  • 全部大写,如有多个单词,用下划线隔开
  • 全大写+下划线式驼峰
MAX_CLIENT=100

函数命名

  • 函数名应该小写,如有多个单词,用下划线隔开
  • 大小写混合仅在为了兼容原来主要以大小写混合风格的情况下使用,保持向后兼容.
  • 私有函数在函数前加一个下划线_
def run():
	pass

def run_with_env():
	pass


class Person():
	def _private_func():
		pass


类命名

  • 类名使用驼峰(CamelCase)命名风格,首字母大写,私有类可用一个下划线开头。
  • 在接口被文档化并且主要被用于调用的情况下,可以使用函数的命名风格代替。
  • 对于内置的变量命名有一个单独的约定:大部分内置变量是单个单词(或者两个单词连接在一起),首字母大写的命名法只用于异常名或者内部的常量。
class Farm():
	pass

class AnimalFarm(Farm):
	pass

class _PrivateFarm(Farm):
	pass

类里面函数和方法参数

  • 不要中英文混编

  • 不要有a、b、c这种没有意义的命名

  • 不要怕名字长就随便缩写,比如person_info 缩写成pi

  • 不要用大小写区分变量类型,比如a是int类型,A是String类型

  • 不要使用容易引起混淆的变量名

  • bool变量⼀般加上前缀 is_ 如:is_success

  • 变量名不要用系统关键字,如 dir type str等等
    以下用下画线作前导或结尾的特殊形式是被公认的:

  • _single_leading_underscore(以一个下画线作前导):例如,“from M import *”不会导入以下画线开头的对象。

  • single_trailing_underscore_(以一个下画线结尾):用于避免与 Python 关键词的冲突,例如“Tkinter.Toplevel(master, class_=‘ClassName’)”。

  • __double_leading_underscore (双下画线):从 Python 1.4 起为类私有名。

  • double_leading_and_trailing_underscore:特殊的(magic) 对象或属性,存在于
    用户控制的(user-controlled)名字空间,例如:initimportfile

9 引号用法规则

  • 自然语言使用双引号
  • 机器标识使用单引号
  • 正则表达式使用双引号
  • 文档字符串 (docstring) 使用三个双引号
    字符串引号规则:
  • 单引号和双引号字符串是相同的。当一个字符串中包含单引号或者双引号字符串的时候,使用和最外层不同的符号来避免使用反斜杠,从而提高可读性。
  • 在同一个文件中,保持使用字符串引号的一致性。在字符串内可以使用另外一种引号,以避免在字符串中使用。

正确使用示例:

Tim('Why are you hiding your eyes?')
Bob("I'm scared of lint errors.")
Juy('"Good!" thought a happy Python reviewer.')
  • 当且仅当代码中使用单引号来引用字符串时,才可能会使用三重’''为非文档字符串的多行字符串来标识引用
  • 文档字符串必须使用三重双引号 ‘’‘’‘’

10 模块导入规则

  • 导入应该放在文件顶部,位于模块注释和文档字符串之后,模块全局变量和常量之前
  • 导入应该按照从最通用到最不通用的顺序分组:标准库导入,第三方导入,应用程序指定导入,分组之间空一行
  • 模块名称要短,使用小写,并避免使用特殊符号,比如点和问号
  • 尽量保持模块名简单,以无需分开单词最佳(不推荐在两个单词之间使用下划线)
  • 每个导入应该独占一行

正确使用例子:

import os
import numpy
import sys

from types import StringType,ListType
  • 从一个包含类的模块中导入类时,通常可以写成这样:
from MyClass import MyClass
from foo.bar.YourClass import YourClass

模块导入建议

示例评价
from modu import *差,不清楚从模块中导入了哪些内容
from modu import sqrt稍好
import modu import modu.sqrt最佳,调用的时候直接使用modu.sqrt能比较清楚的知道当前方法属于哪个模块
import os import sys推荐
from subprocess import Popen,PIPE推荐

11 Main

主功能应该放在一个main()函数中
在Python中,pydoc以及单元测试要求模块必须是可导入的。代码应该在执行主程序前总是检查 if name == ‘main’, 这样当模块被导入时主程序就不会被执行。

def main():
	...

if __name__=='__main__':
	main()

12 函数设计规范

  • 函数设计的主要目标就是最大化代码重用和最小化代码冗余。精心设计的函数不仅可以提高程序的健壮性,还可以增强可读性、减少维护成本。
  • 函数设计要尽量短小,嵌套层次不宜过深。 所谓短小, 就是尽量避免过长函数, 因为这样不需要上下拉动滚动条就能获得整体感观, 而不是来回翻动屏幕去寻找某个变量或者某条逻辑判断等。 函数中需要用到 if、 elif、 while 、 for 等循环语句的地方,尽量不要嵌套过深,最好能控制在3层以内。不然有时候为了弄清楚哪段代码属于内部嵌套, 哪段属于中间层次的嵌套, 哪段属于更外一层的嵌套所花费的时间比读代码细节所用时间更多。
  • 尽可能通过参数接受输入,以及通过return产生输出以保证函数的独立性。
  • 尽量减少使用全局变量进行函数间通信。
  • 不要在函数中直接修改可变类型的参数。
  • 函数申明应该做到合理、 简单、 易于使用。 除了函数名能够正确反映其大体功能外, 参数的设计也应该简洁明了, 参数个数不宜太多。 参数太多带来的弊端是: 调用者需要花费更多的时间去理解每个参数的意思,测试的时候测试用例编写的难度也会加大。
  • 函数参数设计应该考虑向下兼容。

2 模块管理

1 模块 包

模块module:
**模块 module:**一般情况下,是一个以.py为后缀的文件。其他可作为module的文件类型还有".pyo"、“.pyc”、“.pyd”、“.so”、“.dll”,但Python初学者几乎用不到。
module 可看作一个工具类,可共用或者隐藏代码细节,将相关代码放置在一个module以便让代码更好用、易懂,让coder重点放在高层逻辑上。
module能定义函数、类、变量,也能包含可执行的代码。module来源有3种:
①Python内置的模块(标准库);
②第三方模块;
③自定义模块。

包 package: 为避免模块名冲突,Python引入了按目录组织模块的方法,称之为 包(package)。包 是含有Python模块的文件夹。
在这里插入图片描述
这里的B文件夹下有 init .py时,意为B文件夹是一个包(package),其下的多个模块(module)构成一个整体,而这些模块(module)都可通过同一个包(package)导入其他代码中。
其中 init .py文件 用于组织包(package),方便管理各个模块之间的引用、控制着包的导入行为。
该文件可以什么内容都不写,即为空文件(为空时,仅仅用import [该包]形式 是什么也做不了的),存在即可,相当于一个标记。
但若想使用from pacakge_1 import *这种形式的写法,需在__init__.py中加上:all= [‘file_a’, ‘file_b’] # package_1下有file_a.py和file_b.py,在导入时__init__.py文件将被执行。
但不建议在__init__.py中写模块,以保证该文件简单。不过可在 init .py导入我们需要的模块,以便避免一个个导入、方便使用。

其中__all__ 是一个重要的变量,用来指定此包(package)被import *时,哪些模块(module)会被import进【当前作用域中】。不在 all 列表中的模块不会被其他程序引用。可以重写__all__,如__all__= [‘当前所属包模块1名字’, ‘模块1名字’],如果写了这个,则会按列表中的模块名进行导入。

在模糊导入时,形如from package import *,*是由__all__定义的。

精确导入,形如 from package import *、import package.class。
path 也是一个常用变量,是个列表,默认情况下只有一个元素,即当前包(package)的路径。修改 path 可改变包(package)内的搜索路径。

当我们在导入一个包(package)时(会先加载__init__.py定义的引入模块,然后再运行其他代码),实际上是导入的它的__init__.py文件(导入时,该文件自动运行,助我们一下导入该包中的多个模块)。我们可以在__init__.py中再导入其他的包(package)或模块 或自定义类。

2 sys.modules、命名空间、模块内置属性

2.1 sys.modules

官方解释:链接
sys.modules 是一个 将模块名称(module_name)映射到已加载的模块(modules) 的字典。可用来强制重新加载modules。Python一启动,它将被加载在内存中。
当我们导入新modules,sys.modules将自动记录下该module;当第二次再导入该module时,Python将直接到字典中查找,加快运行速度。

它是个字典,故拥有字典的一切方法,如sys.modules.keys()、sys.modules.values()、sys.modules[‘os’]。但请不要轻易替换字典、或从字典中删除某元素,将可能导致Python运行失败。

import sys
print(sys.modules)#打印,查看该字典具体内容。

2.2 命名空间

如同一个dict,key 是变量名字,value 是变量的值。

  • 每个函数function 有自己的命名空间,称local namespace,记录函数的变量。
  • 每个模块module 有自己的命名空间,称global namespace,记录模块的变量,包括functions、classes、导入的modules、module级别的变量和常量。
  • build-in命名空间,它包含build-in function和exceptions,可被任意模块访问。
    某段Python代码访问 变量x 时,Python会所有的命名空间中查找该变量,顺序是:
  1. local namespace 即当前函数或类方法。若找到,则停止搜索;
  2. global namespace 即当前模块。若找到,则停止搜索;
  3. build-in namespace Python会假设变量x是build-in的函数函数或变量。若变量x不是build-in的内置函数或变量,Python将报错NameError。
  4. 对于闭包,若在local namespace找不到该变量,则下一个查找目标是父函数的local namespace。
def func(a=1):
	b=2
	print(locals())  # 打印当前函数(方法)的局部命名空间
	'''
	locs=locals() # 只读,不可写 将报错.
	locs['c']=3
	print(c)
	'''
	return a+b

func()
glos=globals()
glos['d']=4
print(d)
print(globals)  # 打印当前模块namespace_test.py的全局命名空间

内置函数locals(),globals()返回一个字典,区别:前者只读,后者可写
命名空间 在from module_name import 、import module_name中的体现:from关键词是导入模块或包中的某个部分。

  1. from module_A import X:会将该模块的函数/变量导入到当前模块的命名空间中,无须用module_A.X访问了。
  2. import module_A:modules_A本身被导入,但保存它原有的命名空间,故得用module_A.X方式访问其函数或变量。

2.3 模块内置属性

  1. __name__直接运行本模块,name__值为__main;import module,__name__值为模块名字。
  2. file 当前 module的绝对路径
    3.dict
    4.doc
    5.package
    6.path

3 绝对导入 相对导入

文件结构如下:
在这里插入图片描述

3.1 绝对导入

所有的模块import都从“根节点”开始。根节点的位置由sys.path中的路径决定,项目的根目录一般自动在sys.path中。如果希望程序能处处执行,需手动修改sys.path。
例1:test1.py中导入B包/B1子包/b2.py模块

import sys,os
BASE_DIR=os.path.dirname(os.path.abspath(__file__))  # 存放test1.py所在的绝对路径
sys.path.append(BASE_DIR)

from B.B1 import b2  # 导入B包子包B1中的模块b2

3.2 相对导入

只关心相对自己当前目录的模块位置就好。不能在包(package)的内部直接执行(会报错)。不管根节点在哪儿,包内的模块相对位置都是正确的。
PythonApplication1.py代码

# from . import test1  # 这种导入方式会报错
import test1  # 正确
test1.print_b2()

test1.py代码

def print_b2():
	print('b2')

运行PythonApplication1.py,打印:b2

3.3 单独导入包(package)

单独import某个包名称时,不会导入该包中所包含的所有子模块.
test1.py导入同级目录B包的子包B1包的b2模块,执行b2模块的print_b2方法
test1.py代码

import B
B.B1.b2.print_b2()

运行test1.py,会报错
解决办法:
B/init.py代码

from . import B1  # 其中的.表示当前目录

B/B1/init.py

from . import b2

此时,执行test1.py,成功打印:b2

3.4 额外

  1. 一个.py文件调用另一个.py文件中的类
    如a.py(class A),b.py(class B),a.py调用b.py中类B用:
    from b import B
  2. 一个.py文件中的类 继承另一个.py文件中的类 如a.py(class A),
    b.py(class B) a.py中类A继承b.py类B
from b import B
class A(B):
pass
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值