[Python 实战] - No.9 Python闭包和装饰器

一、闭包:

闭包就是内层函数引用了外部函数的变量,然后返回内层函数的情况。闭包的特点就是我们返回的函数,引用了外部函数的局部变量,如果我们希望按照我们所想,来正确的使用这个闭包的话,那就要确定我们引用的局部变量在函数返回以后不能更改。

一个最简单的闭包:

def outer(arg):
    def inner():
        return 'Using args:' + arg
    return inner

f = outer('hello')
print f()
例如上面的例子,我们定义了一个outer()函数,同时在里面定义了一个inner()函数。inner()函数使用了outer()中的参数arg,然后我们返回inner函数。所以总的来说,outer函数的返回值是内部定义的inner,同时inner使用了outer()中的参数。

那么什么叫外部的参数不能改变呢?

比如,我们定义一个函数getFunc(),这个函数返回了三个函数,分别定义1+1,2+2,3+3

def getFaunc():
    L = []
    for i in range(1,4):
        def f():
            return i+i
        L.append(f)
    return L
f1,f2,f3 = getFaunc()
print f1(),f2(),f3()
结果发现,最后的结果均是3+3。因为在我们添加完第三个函数的时候,i=3,内部函数使用到的外部参数改变了,所以我们的闭包没有正确运行

更改为如下所示:

def getFaunc():
    L = []
    for i in range(1,4):
        def f(i):
            def g():
                return i+i
            return g
        tmp = f(i)
        L.append(tmp)
    return L
f1,f2,f3 = getFaunc()
print f1(),f2(),f3()
区别就是,在第二段代码中,我们执行了f(i)函数,这就使得相对的g中的变量i固定为当前f执行时的i,从而不会导致外部的变量改变这种情况


二、Python装饰器

Python装饰器,顾名思义就是给函数起到装饰功能,这就意味着:首先是仅是装饰而不会更改函数的任何代码,第二个就是就是增加原有函数的功能。

举个例子,我们有一个函数是打印当天的日期:

import time


def getDate():

    return  time.strftime('%Y-%m-%d',time.localtime(time.time()))

print getDate()
如果我们希望在打印日期的时候,在服务器上打印当前的时间,而又不希望更改代码。这时候就可以使用装饰器

import time


def getDate():

    return  time.strftime('%Y-%m-%d',time.localtime(time.time()))

def getDateAndTime(f):  # 装饰器函数,将原函数作为参数传入装饰器函数
    def newGetDate():   #在内部声明一个新函数
        print '[SERVER] Current time: '+time.strftime('%H-%M-%S',time.localtime(time.time())) #增加的新功能
        return f() # 返回原函数执行
    return newGetDate #返回新函数

getDate = getDateAndTime(getDate) #屏蔽掉原有函数

print getDate()
装饰器看上去很复杂,自底向上拆成以下步骤很好理解:

1. 首先,要屏蔽掉原有函数,将原有函数传入装饰器函数,获得新函数,所以我们需要构造装饰器函数

2. 在定义的装饰器函数内部,我们需要定义一个新的函数,扩展功能,并作为装饰器函数的返回值


3. 在新函数的内部,扩展新的函数。同时将原函数执行,并作为 返回值返回。

在python中使用这种方法来实现切面。同时,python提供了@这样的语法糖,让我们可很好的使用装饰器,而不用每次都是使用getDate = getDateAndTime(getDate),来屏蔽原有函数

import time

def getDateAndTime(f):
    def newGetDate():
        print '[SERVER] Current time: '+time.strftime('%H-%M-%S',time.localtime(time.time()))
        return f()
    return newGetDate

@getDateAndTime
def getDate():

    return  time.strftime('%Y-%m-%d',time.localtime(time.time()))

print getDate()
与之前相比,我们只需要将@+装饰器函数标注在原有函数上,就实现屏蔽原函数的作用。除此之外,python装饰器也可以带上参数

import time

def log(arg):
    def getDateAndTime(f):
        def newGetDate():
            print '['+arg+']' +'Current time: '+time.strftime('%H-%M-%S',time.localtime(time.time()))
            return f()
        return newGetDate
    return getDateAndTime

@log('SERVER')
def getDate():

    return  time.strftime('%Y-%m-%d',time.localtime(time.time()))

print getDate()
在原有的基础上,多套了一层函数来接收参数。

上面代码可以解释为:

getDateAndTime = log('SERVER')

getDate = getDateAndTime(getDate)


P.S. 文章不妥之处还望指正


猜你喜欢

转载自blog.csdn.net/tjuyanming/article/details/78312017