15 python 初学(闭包,函数装饰器)

这一部分很重要,一定要透彻理解。可参考大神博客: 

http://www.cnblogs.com/yuanchenqi/articles/5830025.html

 


闭包:

如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数被认为是闭包。

闭包 = 函数块 + 定义函数时的环境

inner 是内部函数,对 x 进行引用,内部函数 inner 就是一个闭包

!!!当闭包执行完后,仍然能够保持住当前的运行环境。比如说,如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果。

 

# 个人理解:下面的 inner()函数不是一个闭包函数,因为他并没有使用 outer 中的外部变量。 
#  不理解的一点:在外部直接调用 inner()函数不可行 (是因为找不到),但是执行outer(),将 inner 作为返回值给f,f在外部就可以执行。这又是因为什么呢???
# ------------闭包有一个属性__closure__。 如果 inner 函数没有使用外部变量,那就不属于闭包的范畴。 f.__closure__返回为None。
def outer(): x = 10 def inner(): return 5 return inner

f
= outer()  # f.__closure__返回为None。不是闭包
print(f())


# 下面的 inner()函数是一个闭包函数,因为 inner 函数和外部环境变量 x 结合在了一起。
def outer(): x = 10 def inner():
 print(x) return 5 return inner

# f 其实就是 inner,但此处仍然可以执行inner,就是因为inner使用了外部变量 x,在return inner的时候把x也一起捆绑给了他,让他使用。但是如果单独调用inner函数就会报错。
f = outer()   # f.__closure__返回为:(<cell at 0x000000FB7467D768: int object at 0x00007FFB0FD7B470>,)

print(f())

 

闭包应用:装饰器

闭包举例:棋盘。参考上面博客链接

 

开放封闭原则:

封闭原则:代码写好后不允许再进行修改

开放原则:在不修改代码的条件下可以进行功能扩展

 

装饰器:

 

# 需求:打印出每个函数执行时间

# 这样写的话,每个需要打印执行时间的函数都要写一遍相同的代码,代码重复。
def foo():
    start = time.time()
    print('foo....')
    end = time.time()
    print('spend: %s' %(end - start))

foo()


# 改进方法1 :抽出相同的代码,写成函数print_time,将 foo 函数作为参数传入
# 问题:在这种情况下,我不能调用函数本身去实现打印时间了,而是要通过print_time函数。
# 这样如果其它地方之前是调用了这个函数来打印时间,现在改进了之后,就都要改一遍调用方式,很麻烦
def foo():
    print('foo....')
    time.sleep(2)

def print_time(f):
    start = time.time()
    f()
    end = time.time()
    print('spend: %s' % (end - start))

print_time(foo)

# 改进方法2:在 print_time 函数内定义一个内部函数 inner(),将 print_time 返回这个内部函数,重新赋值给函数变量 foo。
# 这样通过直接调用 foo()就可以实现打印时间的功能了。
# 改进方法3:更加高级一点的方法,使用装饰器(其实我觉得每次写 foo = print_time(foo) 也是一个很浪费时间的重复工作,装饰器可以解决)
# 通过给一个函数使用装饰器,每次就可以直接通过调用函数来实现打印时间的功能了。

def print_time(f):
    def inner():
        start = time.time()
        f()
        end = time.time()
        print('spend: %s' % (end - start))

    return inner


@print_time   # 等价于语句:foo = print_time(foo)
def foo():
    print('foo....')
    time.sleep(2)


# 改进方法2 部分
# foo = print_time(foo)

foo()

 

带参数的被装饰函数:即 foo 是一个有参数的函数,那么如何写这个装饰器呢?

由浅入深,从简单的两个参数到不定长参数。

两个参数实现举例:需要注意的地方

1. inner(a, b)函数的参数要和foo(a, b)函数的参数保持一致。a, b 参数会自动对应 foo(a, b)的参数

2. inner(a, b)函数内部一定要调用实现 f(a,b)函数。

3. (我自己的理解与想象,未证实)foo 函数 使用装饰器 @print_time 时,因为 print_time 函数定义的有参数 f,因此就会自动帮我们将 foo 函数变量作为参数传入。

def print_time(f):
    def inner(a, b):
        start = time.time()
        f(a, b)
        end = time.time()
        print('spend: %s' % (end - start))

    return inner


@print_time   # 等价于语句:foo = print_time(foo)
def foo(a, b):
    print(a + b)
    time.sleep(2)


foo(2,3)

# 5
# spend: 2.0001039505004883

 

不定长参数:

import time

def print_time(f):
    def inner(*args, **kwargs):
        start = time.time()
        f(*args, **kwargs)
        end = time.time()
        print('spend: %s' % (end - start))

    return inner


@print_time   # 等价于语句:foo = print_time(foo)
def foo(*args, **kwargs):
    sum = 0
    for i in args:
        sum += i
    print(sum)
    time.sleep(2)


foo(2, 3, 4, 5)
View Code

 

带参数的装饰器:

被装饰函数可以带参数,那么装饰器可以带参数吗?答案是肯定的。

看一下实现举例(函数直接复制的):

import time


def time_logger(flag=0):
    def show_time(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()

            func(*args, **kwargs)

            end_time = time.time()

            print('spend %s' % (end_time - start_time))

            if flag:
                print('将这个操作的时间记录到日志中')

        return wrapper

    return show_time


@time_logger(3)
def add(*args, **kwargs):
    time.sleep(1)

    sum = 0

    for i in args:
        sum += i

    print(sum)


add(2, 7, 5)

 

在上面这个函数中,装饰器变为了 @time_logger(3),他做的主要工作有哪些呢,即他实现了一个什么功能呢?

1. 执行time_logger(3),得到闭包变量 show_time,里面保存环境变量 flag。

2. 执行 @show_time,相当于实现语句 add = show_time(add)。接下来的逻辑同上

 

 

posted on 2019-01-12 17:54  mlllily  阅读(184)  评论(0编辑  收藏  举报