Python装饰器和生成器

变量的作用链,闭包,装饰器,生成器。


变量的作用域链

a = 10

def func1():

    b=20

    def func2():

        c=30

        return a+b+c

    return func2()      #返回的是结果

print(func1())

利用闭包突破作用域链

闭包概念: 在函数体中定义内部函数,并且使用了外部函数的变量,然后把内部函数给返回,那么这个

内部函数就是闭包。

优点:可以避免污染全局环境,这样就可以在函数体外使用函数体中定义的变量。

缺点:数据长期驻留在内存中,造成内存极大的浪费。

a = 10

def func1():

    b=20

    def func2(x): #func2 就是一个闭包

        nonlocal b

        b=x

        c=30

        return b

    return func2    #返回的是函数,而不是结果

f2 = func1()

print(f2(1))

def func3():        #这个时候就产生了 两个b 没有相互影响。

    b = 40

    def func4():

        return b

    return func4

装饰器:

概念:是一个闭包,把一个函数作为一个参数然后返回一个替代的函数,本质上就是一个返回函数。

在不修改原函数的前提下增加函数的功能 ,最好的方法是使用装饰器,

def wrapper(f):

    def inner():

        print("***********")

        f()

    return inner

@wrapper

def func5():        #在打印一串型号

    print("i'm a good man")

# func5 = wrapper(func5)

func5()

复杂传入参数的装饰器。 在不修改say原参数下增加say功能,判断age是否合理。

def wrapper1(f):

    def inner(name, age):

        if age < 0:

            age = 0

        return f(name ,age)

    return inner

@wrapper1

def say(name, age):

    return "%s is man ,he is %d years old"%(name, age)

# say = wrapper1(say)

print(say("am", -18))

python 2.4开始 支持@将装饰器应用在函数上,只需要在函数定义前加上@装饰器

通用装饰器

def wrapper2(f):

    def inner(*args, **kwargs):

        #这里增加功能

        res = f(*args, **kwargs)

        #如果修改原函数的返回自,在这修改

        return res

    return inner

@wrapper2

def func6(name, age):

    print(name, age)

    return "*****"

带参数的装饰器

def wrapper3(c):#给装饰器带参数 需要三层

    def deco(f):

        def inner(*args, **kwargs):

            for  i in range(c):

                f(*args, **kwargs)

        return inner

    return deco

@wrapper3(5)

def func7():

    print("good man")

func7()

多个装饰器

def wrapper4(f):

    print("enter wrapper4")

    def inner1(*args, **kwargs):

        print("enter inner1")

        res = f(*args, **kwargs)

        print("exit inner1")

        return res

    print("exit wrapper4")

    return inner1

def wrapper5(f):

    print("enter wrapper5")

    def inner2(*args, **kwargs):

        print("enter inner2")

        res = f(*args, **kwargs)

        print("exit inner2")

        return res

    print("exit wrapper5")

    return inner2

def wrapper6(f):

    print("enter wrapper6")

    def inner3(*args, **kwargs):

        print("enter inner3")

        res = f(*args, **kwargs)

        print("exit inner3")

        return res

    print("exit wrapper6")

    return inner3

装饰时:从距离近的装饰器开始装饰(从下至上)

执行时:从距离远的装饰器内部函数开始执行(后进先出)

@wrapper4

@wrapper5

@wrapper6

def func(x, y):

    return x+y

print("***************")

func(1,2)

装饰器使用场景

1.参数、结果的检查。

2.缓存

3.计数

4.日志

5.统计 #把所有的数据统计在一起

6.权限管理

7.重试

def wrapper7(f):

    index = 0 #闭包,一直存在内存里

    def inner(*args, **kwargs):

        #这里增加功能

        nonlocal index

        index += 1

        res = f(*args, **kwargs)

        print("第%d次执行"%index)

        #如果修改原函数的返回自,在这修改

        return res

    return inner

@wrapper7

def func():

    print("hello world")

def func1():

    print("hello world")

func()

func1()

func()

func1()

#两个次数不互相影响。

retry装饰器

def retry(count = 3, wait = 0, exceptions = (Exception,)):

    import time

    def wrapper(f):

        def inner(*args, **kwargs):

            for i in range(count):

                try:

                    res = f(*args, **kwargs)

                except exceptions as e:

                    time.sleep(wait)

                    continue

                else:

                    return res

        return inner

    return wrapper

import random

@retry(5)

def connetSQL (ip,port, dbName, passwd):

    num = random.choice([1,2,3,4])

    print("********",num)

    if num <= 2:

        10/0

connetSQL("","","","")

可迭代对象

概念:可以直接作用域for循环的对象称可迭代对象(Iterable)

可以直接作用于for循环的数据类型:

    1.集合数据类型,如 list tuple dict set string等

    2.generator, 包含生成器 和带yield的 generator function

注意:可以使用isinstance()函数 判断一个对象是否是 Iterable对象

from collections import Iterable

print(isinstance([],Iterable))

print(isinstance({},Iterable))

print(isinstance((),Iterable))

print(isinstance("sunck",Iterable))

print(isinstance(100,Iterable))

print(isinstance((x for x in range(10)),Iterable))  #生成器

列表生成器

作用:是python内置的非常简单并且强大的可以用来生成list的生成式生成列表

缺点:只能生成简单的列表

print(list(range(11)))

需求:[1,4,9,16,25,36,49,64,81,100]

循环的方式 缺点:循环比较麻烦,

li = []

for i in range(1,11):

    li.append(i*i)

print(li)

列表生成式 生成列表

li2 = [x*x for x in range(1, 11)]

print(li2)

列表生成式的for循环后可以加判断

li3 = [x*x for x in range(1, 11) if x %2 == 0]

print(li3)

使用双层循环,生成全排列

li4 = [m + n for m in "ABC" for n in "123"]

print(li4)

生成器:

概念: 通过列表生成式,可以直接创建一个列表。所有的数据都会存到内存中,受内存的限制,

列表的容量是有限制的。如果列表中有10000个数据,如果我们只需要访问前几个元素,

后面的基本不会访问那么造成内存极大的浪费。

    如果我们列表中的元素可以按照某种算法推导出来,那么我们在循环遍历列表时,我们不断推导

后面的元素,从而节省大量内存。那么在python中,这种一边循环一边推导的机制成为生成器。

(generator)

创建生成器

1、修改列表生成式:将列表生成式的[]改为()即可。

g = (x * x for x in range(1, 6))

print(g)

print(type(g))

生成器的特变:可以通过next()函数 获得generator的下一个值

print(next(g))

当所有元素都拿出来后 再执行next会得到StopIteration异常

遍历

原理: generator保存的是算法,每次调用next()就计算生成器下一个元素的值,直到跑出StopIteration错误为止

#以后一般都是使用for循环来迭代生成器,不需要关系StopItera异常

for i in g:

    print(i)

2、函数实现生成器(yied语句)

推导的算法比较复杂,用列表生成式for循环无法实现的时候可以选择函数生成生成器

函数是按顺序执行,遇到return 或者最后一行函数语句就返回。如果想要把一个函数改为生成器函数,只需将函数的return改为yield

#变成 generator函数,在每次调用next()的时候 会遇到yield语句返回,如果再次执行next()那么它会从上次返回的yield语句处继续向下执行

def func():

    print("hello world")

    print("hello")

    print("world")

    yield 10

res = func()

print(res)

print(type(res))

ret = next(res)

print(ret)

菲波那切数列

def fib (count):

    index = 0

    x, y = 0, 1

    while index

        yield y

        x, y = y, x+y

        index += 1

g = fib(5)

print(g)

print(type(g))

for i in g:

    print(i)

迭代器:

from collections import Iterable

转为itertor对象

li5 = [1 ,2,3,4,5]

li = iter(li)

print(next(li))

为什么list dict str set 等数据类型不是Iterator

Iterator表示的是一个数据流, Iterator对象可以被 next()函数调用并范围一个数据,

直到抛出StopIteration错误,可以把数据流看成一个有序的序列,但是不能确定长度,只能通过

next()函数不断计算下一个数据,所以说Iterator的设计是惰性求值。

Iterator可以表示一个无线大的数据流,而list永远不可能存储无限的数据

你可能感兴趣的:(python)