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

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

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


闭包:

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

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

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

# inner()函数就是一个闭包函数
def outer():
    x = 10
    def inner():
        return 5

    return inner
# f 其实就是 inner,但此处仍然可以执行inner,就是因为inner使用了外部变量 x,在return inner的时候把x也一起捆绑给了他,让他使用。但是如果单独调用inner函数就会报错。 f
= outer()  
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)。接下来的逻辑同上

(自己的理解,期待指正):之所以先执行time_logger(3),而不是执行@time_logger(3),是因为判断出 time_logger 函数里面并没有将函数 add 传入进去,而是传入了 3。因此就先执行了time_logger(3),并返回了闭包变量 show_time。

接下里试着执行 @show_time,发现此处没有传参数,但是 show_time 函数定义中却有一个参数 f,因此就会将函数 add 作为实参传给 f。这样就执行了 @show_time

???是不是将 函数add 传入后就是开始执行了装饰器语句 @show_time 

等我理解的更深入了会再修改,目前的理解就是这样。

猜你喜欢

转载自www.cnblogs.com/mlllily/p/10260499.html