改善Python程序-编程惯用用法

将常量定义到一个文件下

(1) 常量名使用所有字母大写,用下划线连接各个单词
(2) 自定义类来实现常量功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class _const:
class ConstError(TypeError):pass
class ConstCaseError(ConstError):pass
def __setattr__(self,name,value):
if self.__dict__.has_key(name):
raise self.ConstError, "Cann't change const.%s" %name
elif not name.issupper():
raise self.ConstCaseError, "const name %s is not all uppercase." %name
self.__dict__[name] = value
import sys
sys.modules[__name__] = _const()
import const
const.My_CONSTANT = 1

利用断言判断错误

1
2
3
4
5
6
7
8
9
10
# 基本语法
assert expression1["," expression2]
# 计算expression1查看返回为False还是True, 当为False则引发AssertionError
>>>x = 1
>>>y = 2
>>>assert x == y, "not equals"
TrackBack (....):
File ....
AssertionError: not equals
  • 断言用在逻辑无法到达的地方或常态下为真,对于本身异常能够处理则不用断言
  • 需要判断返回值是否为合理时使用断言

充分利用Lazy evaluation特性–在真正需要结果时才执行代码

  • 避免不必要的计算,if x and y==>只有x为真才会计算y;if x or y:当x为真时不会计算y
  • 节约空间
    1
    2
    3
    4
    5
    6
    7
    8
    def fib():
    a, b = 0, 1
    while True:
    yield a
    a,b = b, a+b
    >>> from itertools import islice
    >>> print list(islice(fib(),5))
    [0,1,1,2,3]

不推荐使用type进行类型检测

1
2
3
4
5
6
7
8
# type 类型检测
if type(n) is types.ListType:
# 使用isinstance检测
# isinstance原型 isinstance(object,classinfo)
>>>isinstance(2,float)
False
>>>isinstance((2,3),(str,list,tuple)) # 多类型列表

谨慎使用eval()

1
2
3
4
5
6
7
>>>eval('1+2')
3
>>>eval("'A'+'B'")
AB
# eval()原型
eval(expression[,globals[,locals]])

eval计算expression并输出结果,其中globals是字典,代表全局空间(当没有builtins时,默认为当前空间),locals类似globals代表局部空间

构建合理的包层次管理module

包简易上可以等同于目录下有init.py的文件夹

1
2
3
4
5
6
from Package.Module1 import Test
# 另一种使用方式
#Package的__init__.py中添加
from Module1 import Test
#则可以直接使用
from Package import Test

from Package import *无法将Package下所有module导入,仅是执行了init.py,所以需要对init.py做修改:

1
__all__ = ['module1','module2']

使用with自动关闭资源

1
2
3
4
5
6
with 表达式 [ as 目标 ]
代码块
# 范例
with open(text.txt,'w') as f
f.read("text")

with的上下文管理器机制是这样一个对象:它定义程序运行时需要建立的上下文,处理程序的进入和退出,实现了上下文管理协议,即在对象中定义enter()和exit()方法。with语句支持嵌套,支持多个with子句.
用户也可以定义自己的上下文管理器来控制程序的运行,只需要实现上下文协议便能够和with语句一起使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> class MyContextManager(object):
...     def __enter__(self):#
...             print "entering..."
...     def __exit__(self,exception_type, exception_value, traceback):
...             print "leaving..."
...             if exception_type is None:
...                     print "no exceptions!"
...                     return False
...             elif exception_type is ValueError:
...                     print "value error!!!"
...                     return True
...             else:
...                     print "other error"
...                     return True
...
>>>
>>> with MyContextManager():
...     print "Testing..."
...     raise(ValueError)
...
entering...
Testing...
leaving...
value error!!!

else子句简化循环使用

1
2
3
4
for/while condition:
...
else:
...

当循环“自然”终结(循环条件为假)时else从句会被执行一次,而当循环是由break语句中断时,else子句就不被执行。
在Python的异常处理中,try块没有抛出任何异常时,执行else块。

1
2
3
4
5
6
7
8
9
10
def save(db, obj):
try:
# save attr1
db.execute('a sql stmt', obj.attr1)
# save attr2
db.execute('another sql stmt', obj.attr2)
except DBError:
db.rollback()
else:
db.commit()

格式化字符串时尽量使用.format方式而不是%

%操作符的基本形式为:%[转换标记][宽度[.精确度]] 转换类型

1
2
3
4
5
6
# 直接格式化字符或者数值。
>>> print "your score is %06. If*' % 9.5 your score is 0009.5 >»
# 以字典的形式格式化。
>>> itemdict = {'itemname':'circumference', 'radius':3, 'value': math.pi*radius*2}
>>> print "the %(itemname)s of a circle with radius %(radius)f is %(value)0.3f " % itemdict
the circumference of a circle with radius 3.000000 is 18.850

.format方式的基本语法为:[[填充符]对齐方式][符号][#][0][宽度][,][.精确度][转换类型]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 使用位置符号。
>>> "The number {0:,} in hex is: {0:#x},the number {1} in oct is {1:#0}".format(4746,45)
'The number 4,746 in hex is: 0x128a,the number 45 in oct is 055'
#其中{0}表示format方法中对应的第一个参数,{1}表示format方法对应的第二个参数.依次递推
# 使用名称。
>>> print "the max number is {max},the min number is {min},the average number is {average:0.3f}".format(max=189,min=12.6,average=23.5)
the max number is 189,the min number is 12.6,the average number is 23.500
# 通过属性。
>>> class Customer(object):
... def __init__(self,name,gender,phone):
... self.name = name
... self.gender = gender
... self.phone = phone
... def __str__(self):
... #通过str ()函数返回格式化的结果
... return 'Customer({self.name),{self.gender),{self.phone})'.format(self=self)
>>> str (Customer("Lisa", "Female", "67889"))
'Customer(Lisa,Female,67889)'
# format方式可以方便地作为参数传递
>>> weather=[("Monday", "rain"), ("Tuesday", "sunny"), ("Wednesday", "sunny")]
>>> formatter = "Weather of '{0[0]}' is '{0[1]}'.".format
>>> for item in map (formatter, weather): #format方法作为第一个参数传递给 map 函数
'Monday' is 'rain'
'Tuesday' is 'sunny'
'Wednesday' is 'sunny'
'Thursday' is 'rain'
'Friday' is 'cloudy'

慎用变长参数

使用*args来实现可变参数列表

*args用于接受一个包装为元组形式的参数列表来 传递非关键字参数,参数个数可以任意

1
2
3
4
5
6
7
8
9
>>> def SumFun(*args):
... result = 0
... for x in args[0:]:
... result += x
... return result
>>> print SumFun(2,4)
>>> print SumFun(1,2,3,4,5)
>>> print SumFun()

使用**kwargs接受字典形式的关键字参数列表

字典的键值对分别表示不可变参数的参数名和值

1
2
3
4
5
6
>>> def category_table (**kwargs):
... for name, value in kwargs.items():
... print '{0} is a kind of {1}'.format(name, value)
>>> category_table(apple = 'fruit', carrot = 'vegetable ',Python = 'programming language1')
>>> category_table(BMW = 'Car')

复杂使用

1
2
3
4
5
6
7
8
>>> def set_axis(x,y, xlabel="x", ylabel="y", *args, **kwargs):
... pass
>>> setaxis(2,3, "testl", "test2""test3", my_kwarg="test3")
>>> set_axis(2,3, my_kwarg="test3")
>>> set_axis(2,3, "testl",my_kwarg="test3")
>>> setaxis("testl", "test2", xlabel="new_x",ylabel = "new_y", my_kwarge="test3")
>>> set_axis(2,"testl", "test2",ylabel = "newy", my_kwarg="test3")

在4种不同形式的参数同时存在的情况下,会首先满足普通参数,然后是默认参数.如果剩余的参数个数能够覆盖所有的默认参数,则默认参数会使用传递时候的值,如标注①处的函数在调用的时候xlabel和ylabel的值分别为“testl”和“test2”;如果剩余参数个数不够,则尽最大可能满足默认参数的值,标注②中xlabel值为“testl”,而ylabel则使用默认参数y.除此之外其余的参数除了键值对以 外所有的参数都将作为args的可变参数,kwargs则与键值对对应.

可变长参数适合在下列情况下使用

  • 为函数添加一个装饰器
  • 如果参数的数目不确定,可以考虑使用变长参数
  • 用来实现函数的多态或者在继承情况下子类需要调用父类的某些方法的时候