人生苦短,我用Python06:装饰器,递归,迭代器,生成器,反射,异常处理

时间:July 12, 2017 分类:

目录:

装饰器

装饰器的目的是在函数执行前或函数执行后添加需要执行的内容

def outer(func):

    def inter():
        print('111')
        r = func()
        print('333')
        return r

    return inter


@outer
def f():
    print('222')
  1. outer函数加在到内存中
  2. 通过@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)

迭代器

迭代器是访问集合元素的一种方式,从集合的第一个对象进行访问,直到所有的元素访问完成。

迭代器还有一个优点,不需要提前准备好整个迭代过程中的元素,直到迭代到某个元素,所以之前和之后可以不存在,可以遍历一个巨大或者无限的集合。

特点是:

  1. 访问的时候不需要关心迭代器内部的结构,直接通过next()方法不断的获取下一个内容
  2. 顺序是从头到尾,无法随机进行访问
  3. 只能往下访问,不能回退
  4. 可以用较少的内存来循环比较大的集合

对于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

总结

  1. 根据字符串的形式去模块中寻找对象,方法
  2. 根据字符串的形式去模块中判断是否存在对象,方法
  3. 根据字符串的形式去模块中设置对象或方法
  4. 根据字符串的形式去模块中删除对象或方法
  5. 根据字符串的形式去导入模块

异常处理

异常的处理主要目的是增加程序的友好性,另一方面也防止程序因为错误而退出。

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