1. 闭包

  • 说明:在一个函数中又定义了一个函数,并且内部函数可以引用外部函数的参数和局部变量,当外部函数返回内部函数时,相关参数和变量都保存在返回的函数中,这种称为闭包。

一个闭包的实际例子:

"""闭包的例子"""
def line_conf(a,b):
    def line(x):
        return a*x + b
    return line
line1 = line_conf(1,1)
line2 = line_conf(4,5)
print(line1(5))
print(line2(5))

结果:
6
25

  • 闭包的优缺点:
    优点:(1)闭包具有提高代码可复用性的作用(2)减少了代码的可移植性。
    缺点:由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存。

2. 装饰器

假设我们需要增强一个函数的功能,但又不希望修改函数的定义,在代码运行期间动态增加的方式,称为装饰器(Decorator)

  • 装饰器的作用:
  • 引入日志
  • 函数执行时间统计
  • 执行函数前预备处理
  • 执行函数后清理功能
  • 权限校验等场景
  • 缓存

一个装饰器的例子:

def w1(func):
    def inner():
        print("----权限验证----")
        func()
    return inner
@w1
def f1():
    print("----f1----")

运行结果:
—-权限验证—-
—-f1—-

整个运行过程如下:
(1). def w1(func):—–> 将w1函数加载到内存
(2). @w1: —-> 执行w1函数,并将@W1下面的函数作为w1函数的参数,即@w1等价于w1(f1)inner()函数,将执行完的W1函数返回值赋值给@w1下面的函数的函数名f1即将w1的返回值再重新赋值给f1.

  • 一个装饰器或多个装饰器使用:
# 定义函数:完成包裹数据
def makeBold(func):
    def wrapped():
        return "<b>" + func() + "</b>"
    return wrapped

# 定义函数:完成包裹数据
def makeItalic(func):
    def wrapped():
        return "<i>" +func() + "</i>"

@makeBold
def test1():
    return "hello world-1"

@makeItalic
def test2():
    return "hello world-2"

@makeBold
@makeItalic
def test3():
    return "hello world-3"

print(test1())
print(test2())
print(test3())
  • 装饰器–无参数的函数:
"""无参数的函数"""
from time import ctime,sleep

def timefun(func):
    def wrapped_func():
        print("---%s called at %s" %(func.__name__,ctime()))
        func()
    return wrapped_func
# foo = timefun(foo) 
# foo 先作为参数赋值给func后,foo接收指向timefun返回的wrapped_func
@timefun
def foo():
    print("我是无参数函数")

foo()
sleep(2)
foo()
# 调用foo(),即等价调用wrapped_func()
# 内部函数wrapped_func被引用,所以外部函数的func变量(自由变量)并没有释放
# func里保存的是原foo函数对象

运行结果:
— foo called at Sun Aug 13 17:12:21 2017
我是无参函数
— foo called at Sun Aug 13 17:12:21 2017
我是无参函数

  • 装饰器–有参数函数
import time

def f(func):
    def wrapped_func(a,b)
        print(a,b)
        func(a,b)
    return wrapped_func

@f
def test(a,b):
    print(a+b)
test(3,5)
time.sleep(2)
test(2,4)

运行结果:
3,5
8
2,4
6

  • 装饰器函数-不定长参数
import time
def f(func):
    def wrapped_func(*args, **kwargs):
        fun(*args,**kwargs)
    return wrappend_func

@f
def test1(a,b,c):
    print(a+b+c)


test(3,5,7)
time.sleep(2)
test(2,4,9)
  • 装饰器–有return
import time 
def f(func):
    def wrapped_f():
        print("====%s called ====="%func.__name__)
        f()
    return wrapped_f

@f
def test():
    print("I am test")

@f
def getInfo():
    return  "---hello---"

test()
time.sleep(2)
test()
print(getInfo())

执行结果:
====test called =====
I am test
====test called =====
I am test
====getInfo called =====
None

如果装饰器为 return func() 运行结果:
====test called =====
I am test
====test called =====
I am test
====getInfo called =====
—hello—

  • 装饰器–带参数,在原有装饰器的基础上,设置外部变量
import time

def f_arg(pre="hello"):
    def f(func):
        def wrapped_f():
            print("===%s call ==="%func.__name__)
            return func
        return wrapped_func
    return f

@f_arg("java")
def test1():
    print("I am test1")

@f-arg("python")
def test2():
    print("I am test2")

test1()
time.sleep(2)
test1()

test2()
time.sleep(2)
test2()

运行结果:
====test1 called ===
====test1 called ===
====test2 called ===
====test2 called ===
====>>>>>>> 可以理解为 test() = f_arg(“java”)(test)()

装饰过程:
(1).调用f_arg(“hello”)
(2).将步骤1得到的返回值,即f返回,然后f()
(3).将f(test1)的结果返回,即wrapped_f
(4).让test1 = wrapped_f , 即test1现在指向wrapped_f

  • 装饰器–类装饰器
"""类装饰器"""
class Test(object):
    def __init__(self,func):
        print("==>>>初始化")
        print("==>>>func name is  %s"%func.__name__)
        self.__func = func

    def __call__(self):
        print("====装饰器中的功能====")
        self.__func()

@Test
def test():
    print("====test====")

test()

运行结果:
==>>>初始化
==>>>func name is test
====装饰器的功能====
====test====

说明:
(1). 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象。并且会把test这个函数名当做参数传递到__init__方法中。即在__init__方法中的属性self.__func指向了test指向的函数。
(2). test指向了用Test创建出来的实例对象
(3). 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
(4). 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体。