人生苦短,我用Python06:装饰器,递归,迭代器,生成器,反射,异常处理
目录:
装饰器
装饰器的目的是在函数执行前或函数执行后添加需要执行的内容
def outer(func):
def inter():
print('111')
r = func()
print('333')
return r
return inter
@outer
def f():
print('222')
- outer函数加在到内存中
- 通过@outer的方式,把f函数为参数传递到outer函数中,并使用outer函数替换
只要函数应用装饰器,那么函数就被重新定义,重新定义为装饰器的内层函数
对于传参的装饰器
def outer(func):
def inter(a1,a2):
print('111')
r = func(a1+a2)
print('333')
return r
return inter
@outer
def f(a1,a2):
print(a1+a2)
如果多个装饰器,先执行外层的,再执行内层的
递归
以斐波那契数列举例。
def f(a1, a2):
if a1 > 10000:
return
print(a1)
a3 = a1 + a2
f(a2, a3)
f(0, 1)
执行结果
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
如果只需要拿到最后一个数,那么需要把递归的结果进行返回
def f(a1, a2):
a3 = a1 + a2
if a3 > 10000:
return a2
a = f(a2, a3)
return a
print(f(0, 1))
执行结果
6765
注意的点是,一定要有来接收递归函数的返回值,在刚才的代码中就是a = f(a2, a3)
迭代器
迭代器是访问集合元素的一种方式,从集合的第一个对象进行访问,直到所有的元素访问完成。
迭代器还有一个优点,不需要提前准备好整个迭代过程中的元素,直到迭代到某个元素,所以之前和之后可以不存在,可以遍历一个巨大或者无限的集合。
特点是:
- 访问的时候不需要关心迭代器内部的结构,直接通过next()方法不断的获取下一个内容
- 顺序是从头到尾,无法随机进行访问
- 只能往下访问,不能回退
- 可以用较少的内存来循环比较大的集合
对于python2.x
Python 2.6.6 (r266:84292, Aug 18 2016, 15:13:37)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = iter([1,2,3,4,5])
>>> a
<listiterator object at 0x7f8ed9c83210>
>>> a.next()
1
>>> a.next()
2
>>> a.next()
3
>>> a.next()
4
>>> a.next()
5
>>> a.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
对于python3.x
Python 3.5.0 (default, Jun 4 2017, 02:43:17)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-18)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = iter([1,2,3,4,5])
>>> a
<list_iterator object at 0x7fef3bb5e4e0>
>>> a.__next__()
1
>>> a.__next__()
2
>>> a.__next__()
3
>>> a.__next__()
4
>>> a.__next__()
5
>>> a.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
生成器
生成器是通过yield来实现。
对比range和xrange
print range(10)
print xrange(10)
执行结果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
xrange(10)
这个只能在python2中,因为在python3中
生成器使用
def xrange():
print('01')
yield 1
print('02')
yield 2
print('03')
yield 3
r = xrange()
a1 = r.__next__()
print(a1)
a2 = r.__next__()
print(a2)
a3 = r.__next__()
print(a3)
执行结果
(env35) [root@why ~]# python shengchengqi2.py
01
1
02
2
03
3
xrange的实质
def xrange(n):
start = 0
while True:
if start > n:
return
yield start
start += 1
for i in xrange(10):
print(i)
执行结果
0
1
2
3
4
5
6
7
8
9
10
反射
反射是通过字符串的形式导入模块,去模块中导入函数,并执行
导入模块
创建模块
(env35) [root@why ~]# vi why.py
def f():
return "This is f of why"
导入模块
(env35) [root@why ~]# vi fanshe.py
inp = input("input module:") #输入要导入的模块名
print(inp)
print(type(inp))
md = __import__(inp) #导入模块
r = md.f() #执行模块方法
print(r)
执行结果
(env35) [root@why ~]# python fanshe.py
input module:why
why
<class 'str'>
This is f of why
如果导入的是嵌套的模块,可以通过参数实现
__import__('lib.db.mysql',fromlist=True)
通过反射导入方法
反射
刚才可以导入模块,但是没有办法选择方法
import why
f = getattr(why, "f")
r = f()
print(r)
执行结果
(env35) [root@why ~]# python fanshe2.py
This is f of why
通过字符串来使用模块和方法
why_import = __import__("why")
f_import = getattr(why_import, "f")
r = f_import()
print(r)
模块是通过__import__导入,而方法是通过getattr的方式获得,这种方法被广泛的应用于web框架的路由系统中。
另外不只是方法,属性也同样可以。
其他方法
获取的默认值
import why
r = getattr(why,'age', 24)
print r
执行结果
24
判断方法是否存在
import why
r = hasattr(why, 'f')
print r
执行结果
True
在内存添加属性
import why
r = hasattr(why, 'age')
print r
setattr(why, 'age', 24)
r = hasattr(why, 'age')
print r
print getattr(why, 'age')
执行结果
False
True
24
在内存添加方法
import why
setattr(why, 'f2', lambda a: a + 1)
f2_import = getattr(why, 'f2')
r = f2_import(1)
print r
执行结果
2
在内存中删除方法
import why
delattr(why, 'f')
r = hasattr(why, 'f')
print r
执行结果
False
总结
- 根据字符串的形式去模块中寻找对象,方法
- 根据字符串的形式去模块中判断是否存在对象,方法
- 根据字符串的形式去模块中设置对象或方法
- 根据字符串的形式去模块中删除对象或方法
- 根据字符串的形式去导入模块
异常处理
异常的处理主要目的是增加程序的友好性,另一方面也防止程序因为错误而退出。
try:
pass
except Exception,ex:
pass
简单的捕获异常
输入为int类型
inp = 1
try:
num = int(inp)
print num
except Exception as e:
print(e)
执行结果
1
直接执行了try中的流程,而没有执行except中的流程
输入的不为int类型
inp = "why"
try:
num = int(inp)
print num
except Exception as e:
print(e)
执行结果
invalid literal for int() with base 10: 'why'
不做异常捕捉
inp = "why"
num = int(inp)
print num
执行结果
ValueError: invalid literal for int() with base 10: 'why
不做异常的话,就会程序直接报错而退出了。
对于Exception,可以捕获所有的异常,而每一项异常需要通过特定的异常来进行捕获
- AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
- IOError 输入/输出异常;基本上是无法打开文件
- ImportError 无法引入模块或包;基本上是路径问题或名称错误
- IndentationError 语法错误(的子类) ;代码没有正确对齐
- IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
- KeyError 试图访问字典里不存在的键
- KeyboardInterrupt Ctrl+C被按下
- NameError 使用一个还未被赋予对象的变量
- SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
- TypeError 传入对象类型与要求的不符合
- UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它
- ValueError 传入一个调用者不期望的值,即使值的类型是正确的
以上是常见的异常,还有别的异常 ArithmeticError,AssertionError,AttributeError,BaseException,BufferError,BytesWarning,DeprecationWarning,EnvironmentError,EOFError,Exception,FloatingPointError,FutureWarning,GeneratorExit,ImportError,ImportWarning,IndentationError,IndexError,IOError,KeyboardInterrupt,KeyError,LookupError,MemoryError,NameError,NotImplementedError,OSError,OverflowError,PendingDeprecationWarning,ReferenceError,RuntimeError,RuntimeWarning,StandardError,StopIteration,SyntaxError,SyntaxWarning,SystemError,SystemExit,TabError,TypeError,UnboundLocalError,UnicodeDecodeError,UnicodeEncodeError,UnicodeError,UnicodeTranslateError,UnicodeWarning,UserWarning,ValueError,Warning,ZeroDivisionError
通过特殊异常捕获
我们通过valueError来不过刚才的异常
inp = "why"
try:
num = int(inp)
print num
except ValueError as e:
print(e)
执行结果
invalid literal for int() with base 10: 'why'
但是不通过valueError来捕获,例如通过KeyError来捕获异常
inp = "why"
try:
num = int(inp)
print num
except KeyError as e:
print(e)
执行结果
Traceback (most recent call last):
File "err.py", line 3, in <module>
num = int(inp)
ValueError: invalid literal for int() with base 10: 'why'
使用KeyError未能正常的捕获异常,进而程序直接退出
捕捉多种异常
对于try代码块中会出现任何的异常,可以进行多种异常捕捉,示例代码
inp = "why"
try:
num = int(inp)
print num
except KeyError as e:
print(e)
except ValueError,e:
print(e)
except Exception,e:
print("have error")
执行代码
invalid literal for int() with base 10: 'why'
异常捕捉的过程,是按照顺序进行捕获,类似防火墙对异常进行捕获
异常的整体流程
try:
# 主代码块
pass
except KeyError,e:
# 异常时,执行该块
pass
else:
# 主代码块执行完,执行该块
pass
finally:
# 无论异常与否,最终执行该块
pass
主动触发异常
可以通过if判断的方式,如果不成立主动触发异常
try:
raise Exception('This is a error')
except Exception,e:
print e
执行结果
This is a error
自定义异常
自定义异常需要继承Exception类
class WHYException(Exception):
def __init__(self, msg):
self.message = msg
def __str__(self):
return self.message
try:
raise WHYException('我的异常')
except WHYException,e:
print e
断言
断言就可以直接在出问题的地方进行崩溃。
判定规则就是布尔值为真,触发异常则为假,就会触发异常
示例
assert 2==1,'2!=1'
执行结果
Traceback (most recent call last):
File "err2.py", line 1, in <module>
assert 2==1,'2!=1'
AssertionError: 2!=1
对于实质,可以理解为
try:
if 2==1:
pass
else:
raise Ex