美文网首页Python is Best
Python——装饰器 闭包的函数

Python——装饰器 闭包的函数

作者: So_ProbuING | 来源:发表于2017-10-19 10:37 被阅读3次

闭包的定义

将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象称为闭包
我们知道函数在Python中是第一类对象,也就是说可以把它们当作参数传递给其他函数,放在数据结构中,以及作为函数的返回结果。
我们定义一个函数,它接受另一函数作为输入并调用它

foo.py
def call(func):
    return func

我们在另一个程序中去调用这个函数

cal_close.py
from python_book import foo
def helloworld():
    return 'hello world'
print(foo.call(helloworld))

我们把函数当作数据处理时,它将隐式地携带与定义该函数的周围环境相关的信息,这将影响到函数中自由变量的绑定方式。
我们在刚才的栗子中加入一个变量的定义

foo.py
x = 42
def call(func):
    return func

然后在调用函数的地方同样也加入自己的函数定义

cal_close.py
from python_book import foo

x = 37

def helloworld():
    return 'hello world is %d' % x
print(foo.call(helloworld))
>>> hello world is 37

在这个栗子中,函数helloworld()使用的x的值是在与他相同的环境中定义的,即使在foo.py中也定义了一个变量x,而且这里也是实际调用helloworld()函数的地方,但x的值与helloworld()函数执行时使用的x不同
事实上所有函数都拥有一个指向了定义该函数的全局命名空间的globals属性,始终对应于定义函数的闭包模块,闭包将捕捉内部函数执行所需的整个环境
我们可以看一个闭包函数的栗子

def countdown(n):
    def next():
        nonlocal n
        r = n
        n -= 1
        return r
    return next
# 使用
next = countdown(10)
while True:
    v = next()
    if not v: break
    print(v)

在Python中,装饰器是很重要的高级函数。要掌握装饰器我们必须掌握相关的知识

函数作用域

LEGB原则

函数即对象

在Python中,函数和我们之前的数据类型等都一样都是对象,而且函数是最高级的对象
在内存中和对象一样,也是将函数的指针存储在函数名中


函数在内存中的情况

既然函数和对象相同那么函数自然满足对象的几个条件
1.可以被赋值给其他变量

def foo():
    print('foo')
bar=foo
bar()
foo()
print(id(foo),id(bar))  #4321123592 4321123592

函数的函数名其实就是函数的指针,可以将函数的函数名赋值给其他的变量
2.其可以被定义在另外一个函数内(作为参数&作为返回值):

#*******函数名作为参数**********
def foo(func):
    print('foo')
    func()
 
def bar():
    print('bar')
 
foo(bar)
 
#*******函数名作为返回值*********
 
def foo():
    print('foo')
    return bar
 
def bar():
    print('bar')
 
b=foo()
b()

函数的嵌套及闭包

Python允许创建嵌套函数,通过在函数内部使用def关键字声明一个函数作为内部函数

看下面一个例子:

#想执行inner函数,两种方法
def outer():
     x = 1
     def inner():
         print (x) # 1
     # inner() # 2
     return inner
 
# outer()
in_func=outer()
in_func()

作为调用内部函数inner,有下面两种方法

in_func=outer() 
in_func()  
###########
inner()(已经加载到内存啦)

如果直接在外部调用内部函数inner就会报错,原因就是这里找不到这个引用变量

但是这里就会有一个问题,在内部函数被调用执行的时候,它的外部函数也就是outer()已经执行完毕了,那么为什么inner还是可以调用声明在外部outer函数中的变量x呢?

这里就涉及到我们说的闭包的概念,因为outer里return的inner是一个闭包函数,所以就会有这个x变量

我们也就可以抛出闭包的定义

如果在一个内部函数中,对在外部作用域(且不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包,内部函数可以称为闭包函数

在上面的例子中,inner就是内部函数,inner中引用了外部作用域的变量x(x在外部作用域outer,不在模块的全局作用域)。
所以这个inner函数就是闭包函数
如下函数,内部闭包函数的作用是给外部的函数增加字符串参数

>>> def saying(something):
...     def outsaying():
...             return "is inner read params %s" %something
...     return outsaying
... 
>>> a = saying('hello')
>>> a()
'is inner read params hello'

上面这个函数中 outsaying作为内部函数直接访问外部的变量所以是一个闭包函数,outsaying()函数可以得到参数something的值并且记录下来,return outsaying 这一行返回的是outsaying函数的一个复制(并没有直接调用)

装饰器概念

装饰器本质上来说是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也会一个函数对象。装饰器可以被用于:插入日志、性能测试、事物处理、缓存、权限校验等应用场景,我们有了装饰器我们就可以抽离大量与函数功能本身无关的雷同代码并继续重用。装饰器的作用就是为已经存在的对象添加额外的功能。

装饰器定义

装饰器是一个函数,只要用途是包装另一个函数或类。语法上使用特殊符号@表示装饰器
使用装饰器时,它们必须出现在函数或者类定义之前的单独行上,可以同时使用多个装饰器
···
@foo
@bar
@spam
def grok(x)
pass
···
上述代码等同于

def grok(x):
      pass
grok = foo(bar(spam(grok)))

装饰器也可以接受参数

@eventhandler('a')
def handle_a(msg):
        pass
@eventhandler('b')
def handle_b(msg):
        pass

如果装饰器提供参数,装饰器的语义如下所示:
def handle_button(msg):
tmp = eventhandle('a')# 使用提供的参数调用装饰器
handle_button = tmp(handle_button) # 调用装饰器返回的函数

@trace
def square(x):
      return x*x
等价:
def square(x):
      return x*x
square = trace(square)

现在在生产中有这样的函数

def foo():
  print('hello foo')
foo()

现在有一个新的需求,希望可以记录下函数的执行时间,在这种需求下我们最好不要修改源代码来实现这个功能,我们可以引入一个内置函数,而且为了不影响结构,我们还不应该改变函数的调用方式
这里我们可以引入一个内置函数

import time
 
def show_time(func):
    def wrapper():
        start_time=time.time()
        func()
        end_time=time.time()
        print('spend %s'%(end_time-start_time))
 
    return wrapper
 
 
def foo():
    print('hello foo')
    time.sleep(3)
 
foo=show_time(foo)
foo()

在上面这个例子中函数show_time就是装饰器。它将真正的业务方法func包裹在函数里面。

@符号是装饰器的语法糖,在定义函数的时候使用,避免多次的赋值操作

import time
 
def show_time(func):
    def wrapper():
        start_time=time.time()
        func()
        end_time=time.time()
        print('spend %s'%(end_time-start_time))
 
    return wrapper
 
@show_time   #foo=show_time(foo)
def foo():
    print('hello foo')
    time.sleep(3)
 
 
@show_time  #bar=show_time(bar)
def bar():
    print('in the bar')
    time.sleep(2)
 
foo()
print('***********')
bar()

这里:foo=show_time(foo)其实就是把内置函数wrapper引用的对象引用给了foo,而wrapper里的变量func之所以可以使用,就是因为wrapper是一个闭包函数

带参数的被装饰函数

import time
 
def show_time(func):
 
    def wrapper(a,b):
        start_time=time.time()
        func(a,b)
        end_time=time.time()
        print('spend %s'%(end_time-start_time))
 
    return wrapper
 
@show_time   #add=show_time(add)
def add(a,b):
 
    time.sleep(1)
    print(a+b)
 
add(2,4)
  • 不定长参数
#***********************************不定长参数
import time

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))

    return wrapper

@show_time   #add=show_time(add)
def add(*args,**kwargs):

    time.sleep(1)
    sum=0
    for i in args:
        sum+=i
    print(sum)

add(2,4,8,9)

装饰器带参数

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)

多层装饰器

def makebold(fn):
    def wrapper():
        return "<b>" + fn() + "</b>"
    return wrapper
 
def makeitalic(fn):
    def wrapper():
        return "<i>" + fn() + "</i>"
    return wrapper
 
@makebold
@makeitalic
def hello():
    return "hello alvin"
 
hello()

匿名函数: lambda()函数

Python中,lambda函数是用一个语句表达的匿名函数,可以用它来代替小的函数。

# 定义一个函数,参数接收一个列表,并遍历
>>> def edit_story(words,func):
...     for word in words:
...             print(func(word))
... 
>>> stairs = ['a','ab','abc','abcd']
>>> def add_str(word):
...     return word+'!!!'
...  
>>> edit_story(stairs,add_str)
a!!!
ab!!!
abc!!!
abcd!!!

相关文章

  • python装饰器

    装饰器简述 要理解装饰器需要知道Python高阶函数和python闭包,Python高阶函数可以接受函数作为参数,...

  • Python装饰器-专题笔记

    学会装饰器,Python更进阶 函数作用域到闭包到装饰器讲解,及闭包和装饰器的运用。 [√] 慕课网Meshare...

  • python之理解闭包和装饰器

    python之理解闭包和装饰器 1、闭包函数 1.1 python中函数都是对象 结果: 上面定义一个shut函数...

  • Python 2 - 高级用法 - 装饰器

    Python 2 - 高级用法 - 装饰器 一谈到 装饰器,就离不开闭包 闭包 闭包就是能够读取其他函数内部变量的...

  • Python 装饰器的诞生过程

    Python中的装饰器是通过利用了函数特性的闭包实现的,所以在讲装饰器之前,我们需要先了解函数特性,以及闭包是怎么...

  • 只需四步,让你了解Python装饰器的诞生过程

    Python中的装饰器是通过利用了函数特性的闭包实现的,所以在讲装饰器之前,我们需要先了解函数特性,以及闭包是怎么...

  • python中的函数

    关于python中的值传递和址传递 匿名函数 缺省函数 闭包 装饰器

  • 2020-012 python闭包与装饰器

    python闭包与装饰器 闭包 函数和对其周围状态(lexical environment,词法环境)的引用捆绑在...

  • chapter7 函数式编程

    闭包 匿名函数 装饰器 偏函数

  • Python装饰器与闭包!

    闭包是Python装饰器的基础。要理解闭包,先要了解Python中的变量作用域规则。 变量作用域规则 首先,在函数...

网友评论

    本文标题:Python——装饰器 闭包的函数

    本文链接:https://www.haomeiwen.com/subject/londuxtx.html