编写高质量代码:改善Python程序的91个建议.1
人生苦短,睡觉最好!
-U 是--upgrade的缩写,如果以已经安装就升级到最新版
先得安装一下
输出的没毛病
我们实验一下
我提前把代码改过
pep8
--show-source
--show-pep8
.\search2.py
--show-source
--show-pep8
一个是显示哪里不符合
一个是显示正确的写法
笔记
Python的库
sphinx,生成文档注释
注释
就是先写注释,再写逻辑.对于不用的代码要不要保留的.注意空行的使用,保持上下文语言的理解性,调用者在上,被调用者在下
def A():
B()
def B():
pass
避免过长的代码行,最好不要超过80个字符.逗号和分号前不要使用空格
函数的设计原则
1.函数的设计要短小,嵌套的层次不宜过深(控制在三层以内)
2.函数的声明应该做到合理,简单,易于使用
3.函数参数参数设计应该考虑向下兼容
4.一个函数只能做一件事,尽量保证函数语句粒度的一致性. \
函数的向下兼容
通过加入默认参数来避免这种退化,做到向下兼容,不要在函数中定义可变对象作为默认值,使用异常替换返回错误,保证通过单元测试
常量
Python内部没有常量的功能.通过命名风格标识,通过定义的类来实现
import sys
class _const:
class ConstError(TypeError):
pass
class ConstCaseError(ConstError):
pass
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't change const.%s" % name
if not name.issuper():
raise self.ConstError, 'const name'"%s" is not all uppercas'% name
self.__dict__[name] = value
sys.modules[__name__] = _const()
以上代码就完成了,使用的时候:
import const
const.COMPANY = "IBM"
这样处理过后,代码一旦赋值就不会被更改.最好集中在一起统一管理
import const
import sys
class _const:
class ConstError(TypeError):
pass
class ConstCaseError(ConstError):
pass
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't change const.%s" % name
if not name.issuper():
raise self.ConstError, 'const name'"%s" is not all uppercas'% name
self.__dict__[name] = value
sys.modules[__name__] = _const()
const.MY_CONST = 1
const.MY_SCOND_CONST = 2
const.MY_THIRD_CONST*2
最后的文件就是这样的
from constant import const
print(const.MY_SCOND_CONST)
print(const.MY_THIRD_CONST)
调用的时候就是这样的写法
assert(断言)语句
x = 1
y = 2
assert x == y,"not equals"
我们先写一个,看看是什么样的
AssertionError Traceback (most recent call last)
<ipython-input-1-12234b9328bc> in <module>
1 x = 1
2 y = 2
----> 3 assert x == y,"not equals"
AssertionError: not equals
以上为打印的结果
x = 1
y = 2
# assert x == y,"not equals"
if __debug__ and not x==y:
raise AssertionError("not equals")
其实等价的语句为这样
AssertionError Traceback (most recent call last)
<ipython-input-2-a11717d6503d> in <module>
3 # assert x == y,"not equals"
4 if __debug__ and not x==y:
----> 5 raise AssertionError("not equals")
AssertionError: not equals
这个是等价的输出.
需要知道一点,断言是影响性能的,需要在后面加上**-O**这样的执行开关
python -O filename.py
这样就可以了.
以及注意,如果本身的异常可以处理就不要用断言了,不要用断言来检查用户的输入,应该使用条件判断,不符合的时候打印一些错误提示.当函数有返回值的时候可以使用断言.判断业务逻辑的时候可以使用断言
数据交换
x,y = y,x
直接这样写就好了.那里面是什么样的原理呢?
x,y = y,x的实现机制--元组
假如x=2,y=3。运行时,首先构造一个元组(y,x),然后构造另一个元组(x,y),接着用元组(y,x)赋值给(x,y),元组赋值过程从左到右,依次进行。根据元组的特性,此时构造的元组的两个元素并不是x和y,而是这两个变量所指向的地址空间里的内容。如果此时再另x=y,即x=3,在地址空间中会另开辟出一块空间存储3,x进而指向这块空间,而元组中的值仍然保持不变,即元组中的x仍然等于2。因此,再实现y=x的赋值时,y的值为2,从而实现交换变量的值。
简单来说,对于“x,y=y,x”,有y,x所构成的元组(y,x)其实应该表示为(3,2),然后进行赋值就可以交换变量的值了。
import dis
def swep1():
x=2
y=3
x, y = y, x
def swep2():
x =2
y =3
temp = x
x = y
y = temp
# print('swep1()')
# swep1()
dis.dis(swep1)
dis.dis(swep2)
此为用字节码分析的源码
3 0 LOAD_CONST 1 (2)
2 STORE_FAST 0 (x)
4 4 LOAD_CONST 2 (3)
6 STORE_FAST 1 (y)
5 8 LOAD_FAST 1 (y)
10 LOAD_FAST 0 (x)
12 ROT_TWO
14 STORE_FAST 0 (x)
16 STORE_FAST 1 (y)
18 LOAD_CONST 0 (None)
20 RETURN_VALUE
8 0 LOAD_CONST 1 (2)
2 STORE_FAST 0 (x)
9 4 LOAD_CONST 2 (3)
6 STORE_FAST 1 (y)
10 8 LOAD_FAST 0 (x)
10 STORE_FAST 2 (temp)
11 12 LOAD_FAST 1 (y)
14 STORE_FAST 0 (x)
12 16 LOAD_FAST 2 (temp)
18 STORE_FAST 1 (y)
20 LOAD_CONST 0 (None)
22 RETURN_VALUE
执行结果,第二段代码生成了三个load_fast和三store_fast的指令,而rot_two是交换两个栈的最顶层元素,它比执行一个load_fast+store_fast快
惰性运算
Lazy evaluation常被译为“延迟计算”或“惰性计算”,指的是仅仅在真正需要执行的时候才计算表达式的值。人的自然性情,在多的情况下,倾向于快。计算机性能优越的情况下,程序员倾向快速编写出可运行的代码,质量的问题会凸显。延迟计算这样的方法,是需要花费心力的,Lazy evaluation 其实并不 lazy .而且资源永远都是有限的,而需求永远都是无法满足的。性能效率是永恒的话题。
短路求值
短路求值。几乎所有语言都有的特性,这说明了这个优化的重要性。但觉得这不是lazy evaluation的重点。lazy evaluation还是一种“能拖就拖,假装自己完成了每项工作流程,但其实只是记到小本本上了,直到老师要求交作业时实在没法拖时才真正开始写”的感觉。但是老师不收作业的时候,它就可以不用写了~lazy的目的是万一最后不需要计算就省资源.因此在编程过程中,如果对于or条件表达式应该将值为真可能性较高的变量写在or的前面,而and则应该推后。
无限的数据结构
生成器可以节省空间,因为它的操作结果是“一个”而不是“一组”。常见的生成器函数包含yield和无限循环,而且会被多次调用,每次调用走到yield语句即返回,下次再被调用则从yield 语句的下一个语句开始执行,直到yield语句返回,循环往复因此可以“无限”. itertools模块包含创建有效迭代器的函数,可以用各种方式对数据进行循环操作,此模块中的所有函数返回的迭代器都可以与for循环语句以及其他包含迭代器(如生成器和生成器表达式)的函数联合使用。
创建一个迭代器,生成项的方式类似于切片返回值:iterable[start : stop : step],将跳过前start个项,迭代在stop所指定的位置停止,step指定用于跳过项的步幅。与切片不同,负值不会用于任何start,stop和step,如果省略了start,迭代将从0开始,如果省略了step,步幅将采用1.
def islice(iterable, *args):
# islice('ABCDEFG', 2) --> A B
# islice('ABCDEFG', 2, 4) --> C D
# islice('ABCDEFG', 2, None) --> C D E F G
# islice('ABCDEFG', 0, None, 2) --> A C E G
s = slice(*args)
it = iter(xrange(s.start or 0, s.stop or sys.maxint, s.step or 1))
nexti = next(it)
for i, element in enumerate(iterable):
if i == nexti:
yield element
nexti = next(it)
#如果start为None,则迭代从零开始。如果step为None,则该步骤默认为1。
#在版本2.5中已更改:默认设置为start和step,不接受None值。
枚举二三事
谈起枚举的话,最经典的就是季节和星期了.,它能够以更接近自然语言的方式来表达数据,可是在Python内没有被加进来,之前也申请过~
1.使用类属性
class Season:
Spring = 0
Summer = 1
Autumn = 2
Winter = 3
print(Season.Spring)
让我们再简化一点,让他看起来更像是一个枚举~
class Season:
Spring,Summer,Autumn,Winter = range(4)
或者...为什么不写一个函数呢?
def enum(*posarg,**kwargs):
return type("Enum",(object,),dictt(zip(posarg,xrange(len(posarg))),**kwargs))
Seasons = enum("Spring", "Summer", "Autumn", "Winter1=1")
Seasons.Spring