改善python程序--函数的几个原则

函数的几个原则

  • 函数设计尽量短小,嵌套层次不易过深(控制在3层以内).函数名和参数清晰简短
  • 函数参数设计向下兼容
  • 一个函数做一件事

遵循异常处理的几点基本原则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try:
<statements> # Run this main action first
except <namel>:
〈statements〉 #当try中发生namel的异常时处理
except (name2, name3):
<statements> #当try中发生name2或name3中的某一个异常的时候处理
except <name4> as <data>
<statements> #当try中发生name4的异常时处理,并获取对应实例
except:
<statements> #其他异常发生时处理
else:
〈statements〉 #没有异常发生时执行
finally:
<statements> #不管有没有异常发生都会执行

异常处理通常需要遵循以下几点基本原则:

  • 注意异常的粒度,不推荐在try中放入过多的代码。
  • 谨慎使用单独的except语句处理所有异常,最好能定位具体的异常。
  • 注意异常捕获的顺序,用户也可以继承自内建异常构建自己的异常类

避免finally中可能发生的陷阱

无论try语句中是否有异常抛出,finally语句总会被执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def FinallyTest ():
print 'i am starting'
while True:
try:
print "I am running"
raise IndexError ("r") # 抛出异 IndexError 异常
except NameError, e:
print 'NameError happended %s',e
break
finally:
print 'finally executed'
break #finally语句中有break语句
>>>FinallyTest()
i am starting
I am running
finally executed

当try块中发生异常的时候,如果在except语句中找不到对应的异常处理,异常将会被临时保存起来,当finally执行完毕的时候,临时保存的异常将会再次被拋出,但如果finally语句中产生了新的异常或者执行了 return或者break语句,那么临时保存的异常将会被丢失,从而导致异常屏蔽。

记住函数传参既不是传值也不是传引用

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
>>> def change_me (org_list):
... print id(org_list)
... new_list = org_list
... print id (new_list)
... if len(new_list)>5:
... new_list = ['a','b','c']
... for i,e in enumerate(new_list):
... if isinstance(e,list):
... new_list[i] = e # 将元素为list类型的替换为***
... print new_list
... print id(new_list)
>>> testl = [l,[1,2,3],[2,1],6]
>>> change_me (testl) #testl 的元素个数小于 5
35260216
35260216
[1,***,***,6]
35260216
>>> print testl
[1,***,***,6]
>>> test2 = [1,2,3,4,5,6,[1]] # testl 中元柰的个数大子>5
>>> change_me (test2)
35260136
35260136
['a','b','c']
35250664 #new_list 的 id 值发生 了改变
>>> print test2 #test2并没有发生改变
[1,2,3,4,5,6,[1]]

C/C++中执行b=a的时候,在内存中申请一块内存并将a的值复制到该内存中;当执行b=7之后是将b对应的值从5修改为7.Python中赋值并不是复制,b=a操作使得b与a引用同一个对象,而b=7则是将b指向对象7. python参数是传对象(call by object)或者说传对象的引用(call_by-object-reference).函数参数在传递的过程中将整个对象传入,对可变对象的修改在函数外部以及内部都可见,调用者和被调用者之间共享这个对象,而对于不可变对象,由于并不能真正被修改,因此,修改往往是通过生成一个新对象然后陚值来实现的.

深入理解str()和repr()的区别

  • str()主要面向用户,其目的是可读性;而repr()面向的是Python解释器,其目的是准确性
  • 在解释器中直接输入a时默认调用repr()函数,而print a则调用str()函数
  • repr()的返回值一般可以用eval()函数来还原对象,如:obj == eval(repr(obj))
    这两个方法分别调用内建的repr()和str()方法,如果类中没有定义str()方法,则默认会使用repr()方法的结
    果来返回对象的字符串表示形式

分清staticmethod和classmethod的适用场景

Python中的静态方法(staticmethod)和类方法(classmethod)都依赖于装饰器 (decorator)来实现。

1
2
3
4
5
6
7
8
9
# 其中静态方法的用法如下:
>>> class C(object):
... @staticmethod
... def f (argl, arg2,...):
#而类方法的用法如下:
>>> class C(object):
... @classmethod
... def f (self, argl, arg2,...):

其中静态方法没有常规方法的特殊行为,如绑定、非绑定、隐式参数等规则,而 类方法的调用使用类本身作为其隐含参数,但调用本身并不需要显示提供该参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> class A(object):
... def instance_method(self,x):
... print "calling instance method instance_method(%s,%s)" %(self,x)
...
... @classmethod
... def class_method(els,x):
... print "calling class_method(%s,%s)" %(els,x)
...
... @staticmethod
... def static_method(x):
... print "calling static_method(%s)" %x
>>> a = A()
>>> a.instance_method("test")
# 输出 calling instance method instance_method(<_main_.A object at OxOOD66B50>, test)
>>> a.class_method("test")
# 输出 calling class_method (<class main .A>, "test")
>>> a.static_method("test")
# 输出 calling static_method(test)

假设有水果类Fruit,它用属性total表示总量,Fruit中已经有方法set()来设置总量,print_total()方法来打印水果数置.类Apple和类Orange继承自Fruit.

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
31
32
33
34
35
36
37
38
39
40
41
42
>>> class Fruit(object):
... total = 0
... @classmethod
... def print_total(els):
... print els.total #print id(Fruit.total) iprint id(els.total)
...
... @classmethod
... def set(els, value):
... #print "calling class_method(%s,%s)"%(els,value)
... els.total = value
... @classmethod
... def Init_Product(cls,product_info):
... area, category, batch = map(int, product_info.split('-'))
... fruit = els(area, category, batch)
... return fruit
>>> class Apple(Fruit):
... pass
>>> class Orange(Fruit):
... Pass
>>> app1=Apple()
>>> app1.set(200)
>>> app2 = Apple()
>>> org1 = Orange()
>>> orgl.set(300)
>>> org2 = Orange()
>>> app1.print_total() #output 200
>>> orgl.print_total() #output 300
# 删除上面代码中的注释语句后运行程序会得到以下结果:
calling class_method(<class '_main_.Apple'>,200)
calling class_method(<class '_main_.Orange'>,300)
200
12184820 >Fruit类的类变量
12186364 >动态生成的Apple类的类变量
300
12184820 >Fruit类的类变量
13810996 >动态生成的Orange类的变量

其他

  • 尽量使用浮点数进行除法
  • is和==的区别:is的作用是检查两个变量是否在同一个内存块中,而==是比较两个变量是否相等
  • 尽量少用from package import module,而用import package.module
  • 常量None的数据类型为NoneType,遵循单例模式,是唯一的
  • 当用户定义的类中定义了nonzero()方法和len()方法,并且该方法返回整数0或者布尔值False的时候,该对应为None
  • 对于对象进行非空判断时,先调用内部方法nonzero(),如果没有nonzero(),则调用len(),如果也没有,返回True
  • 连接字符串应优先使用join而不是+