Python Learn (七)

本文主要讲解Python 函数中的作用域闭包装饰器

Python 中的函数作用域

* L > E > G > B

* L:local 函数内部作用域

* E:enclosing 函数内部与内嵌函数之间

* G:global 全局作用域

* B:build-in 内置作用域

Python 中的闭包

  • 闭包的作用:封装、代码复用
#coding:UTF-8
def fun_150(val):
    passline = 90
    if val > passline:
        print ('pass')
    else:
        print ('failed')
        
def fun_100(val):
    passline = 60
    if val > passline:
        print ('pass')
    else:
        print ('failed')
        
def set_passline(passline):
    def cmp(val):
        if val > passline:
            print ('pass')
        else:
            print ('failed')
        return cmp
        
f_100 = set_passline(60)
f_150 = set_passline(90)

根据上面的代码我们可以看出函数 cmp()其实就是一个闭包,闭包的优越性就是可是在外部引用不同的变量使得我们的代码具有高可复用性。通过上面的示例我们发现其实 f_100fun_100的效果是一致的。

闭包就是函数对我们作用域 enclosing 的一个使用,变量放到 clouser 属性中 ,这个时候我们可以拿过来直接使用,这就是闭包的一个基本使用情况。

上面是闭包的第一种使用情况,下面讲解闭包的第二种使用情况。

分别构建两个函数,一个求和函数my_sum()和一个求平均值函数 my_average()

#coding:UTF-8
def my_sum(*arg):
    return sum(*arg)
def my_average(*arg):
    return sum(arg)/len(arg)
>>>my_sum(1,2,3,4,5)
15
>>>my_average(1,2,3,4,5)
3

但是但我们运行下面的方法就会出现错误:

>>>my_sum(1,2,3,4,5,'6')
>>>my_average()

因为此时的分母为0了,求和中不能支持 int 类型和 str 类型的数值相加。所以我们就需要加上判断条件。但我们加上判断条件后发现代码是一样的,这样不符合我们的代码规范,并且代码就没有高可复用的特性了。

#coding:UTF-8
def my_sum(*arg):
    if len(arg) == 0
        return 0
    for val in arg:
        if not isinstance(val,int):
            return 0 
        return sum(*arg)
def my_average(*arg):
    if len(arg) == 0
        return 0
    for val in arg:
        if not isinstance(val,int):
            return 0 
        return sum(arg)/len(arg)

我们想到 Python 的特性之一有一个就是高阶函数的使用,可以使用函数为变量。因此我们创建一个新的函数dec(func)来保存相同的功能代码。

#coding:UTF-8
def dec(func):
    def in_dec(*arg):
        if len(arg) == 0
            return 0
        for val in arg:
            if not isinstance(val,int):
                return 0 
        return func(*arg)
    return in_dec

这样我们就可以直接调用函数来进行操作了。

#coding:UTF-8
def my_sum(*arg):
    return sum(*arg)
def my_average(*arg):
    return sum(arg)/len(arg)
def dec(func):
    def in_dec(*arg):
        if len(arg) == 0
            return 0
        for val in arg:
            if not isinstance(val,int):
                return 0 
            return func(*arg)
        return in_dec
>>>my_sum1 = dec(my_sum)
>>>my_average1 = dec(my_average)
        
>>>my_sum1(1,2,3,4,5)
15
>>>my_sum1(1,2,3,4,5,'6')
0
>>>my_average1(1,2,3,4,5)
3
>>>my_average1()
0

这个时候代码执行的流程是先执行的 in_dec() 里面的内容然后再执行 func(*arg)的内容,即函数真正需要做不同运行的部分,然后再返回值。

Python 中的装饰器

  1. 装饰器用来装饰函数;
  2. 返回一个函数对象;
  3. 被装饰函数标识符指向返回的函数对象;
  4. 语法糖 @deco

装饰器的实质就是对闭包的一种使用。

#coding:UTF-8
def dec(func):
    def in_dec(*arg):
        if len(arg) == 0
            return 0
        for val in arg:
            if not isinstance(val,int):
                return 0 
            return func(*arg)
        return in_dec

@dec
def my_sum(*arg):#my_sum = in_dec
    return sum(*arg)
def my_average(*arg):
    return sum(arg)/len(arg)
>>>my_sum1 = dec(my_sum)
>>>my_average1 = dec(my_average)
       
>>>my_sum1(1,2,3,4,5)
15
>>>my_sum1(1,2,3,4,5,'6')
0
>>>my_average1(1,2,3,4,5)
3
>>>my_average1()
0

但我们看到@的语法糖的时候要注意代码的执行过程,例如下面的代码中的执行过程是:

def deco(func):
    def in_deco(x,y):
        print ('in deco')
        func(x,y)
    print ('deco')
    return in_deco
@deco
def bar(x,y):
    print ('in bar',x+y)

调用过程:

1. @deco(bar) => in_deco 
2. bar => in_deco
3. bar() = in_deco() = bar()