python3【基础】-装饰器

  要理解充分理解python的装饰器,有充分理解下述三个知识点为前提:

  • python作用域规则
  • 函数即对象
  • 闭包 

一、python作用域规则:

  首先介绍python中的作用域规则。python的作用域规则遵循LEGB规则,这四个字母是什么意思呢?下面将逐一介绍:

  • L:local函数内部作用域
  • E:enclosing函数内部与内嵌函数之间
  • G:global全局作用域
  • B:build-in内置作用域

  上述四个作用域的优先级表现为L>E>G>B,具体在代码中是什么意思呢?我们举例来说明。

  1. local内部作用域

  有如下代码,在foo()函数内部和外部都有一个num变量,在函数内部调用num变量,python优先调用了内部的num。函数内部的num的仅仅在函数内部有效,即其生存周期为函数调用结束后内部的num变量失效。

num = 90

def foo():
    num = 100
    print("函数内部变量num的地址是:{_id}".format(_id=id(num)))
    print("内部num:{_num}".format(_num=num))

foo()
print("函数外部变量num的地址是:{_id}".format(_id=id(num)))
print("外部num:{_num}".format(_num=num))

# 执行上述代码获得如下结果:

 # 函数内部变量num的地址是:4305316064
 # 内部num:100
 # 函数外部变量num的地址是:4305315744
 # 外部num:90

  2. enclosing函数内部与内嵌函数之间

  python的函数支持嵌套定义,即在一个函数内部再定义函数,将函数看做一个对象并进行返回。这样,在调用外部函数的时候可以使用一个变量将内部的函数接收。

  我们在foo1内部定义了foo2函数,并将foo2返回。所谓的enclosing是指foo2和foo1之间的作用域。

  代码段1:

def foo1():
    num = 100

    def foo2():
        num = 90
        print("foo2-num_id:{_id}".format(_id=id(num)))
        print("foo2-num:{_num}".format(_num=num))

    print("foo1-num_id:{_id}".format(_id=id(num)))
    print("foo1-num:{_num}".format(_num=num))

    return foo2
foo2 = foo1()foo2()

# 执行上述代码得:

 # foo1-num_id:4305316064
 # foo1-num:100
 # foo2-num_id:4305315744
 # foo2-num:90

  代码段2:

def foo1():
    num = 100

    def foo2():
        # num = 90
        print("foo2-num_id:{_id}".format(_id=id(num)))
        print("foo2-num:{_num}".format(_num=num))

    print("foo1-num_id:{_id}".format(_id=id(num)))
    print("foo1-num:{_num}".format(_num=num))

    return foo2

foo2 = foo1()
foo2()

# 执行上述代码得:

 # foo1-num_id:4305316064
 # foo1-num:100
 # foo2-num_id:4305316064
 # foo2-num:100

  对比代码段1和代码段2可以看出,在foo2内部,调用num变量时,会优先调用内部的num变量,假如内部变量num不存在,则去外层寻找num。

  3. global全局作用域

  global作用域是指整个python文件。 1 num = 1 2 3 def foo1():

 4     num = 2
 5
 6     def foo2():
 7         num = 3
 8         print(num)
 9
10     print(num)
11
12     return foo2
13
14 print(num)
15 foo2 = foo1()
16 foo2()

  # 执行上述代码得:   1   2   3

  上述代码分别定了三个num变量,其中第1行中的num属于全部作用域范围。

  4. build-in内置作用域 

def Max(a,b):
    return max(a, b)

print(Max(1,2))

  上述代码中,编译器在编译Max函数的时候,发现Max函数包含max函数,而局部作用域和全局作用域都没有max函数,编译器就会去内置作用域中查找max。

二、函数即对象:

  在python这个世界里,函数和我们之前的[1,2,3],‘abc‘,8等一样都是对象,而且函数是最高级的对象(对象是类的实例化,可以调用相应的方法,函数时包含变量的对象的对象)。

  

  通过上图,咱们来理解一下python变量在内存中的情况。

  我们定义一个a=8,其实a存储的并不是‘8’这个值,而是‘8’在内存中的地址。将进行b=a这个操作时,是将a中包含‘8’的内存地址赋值给b,这样的话b也就指向8了。此时,a和b在值上表现出一样。同样的道理,我们定义一个函数foo(),它执行print(‘ok‘) 这个操作,此时,编译器在编译foo()这个函数的时候,会将foo这个函数名指向print(‘ok‘)这条语句在内存中的地址。在python中,函数是一个对象,既然是对象,我们就可以对其进行更改(赋值),执行bar=foo这个操作的意思就是bar这个变量指向print(‘ok‘)在内存中的地址。

  根据上述理解,我们可以得出下述两个结论:

  1.  函数名可以赋给其它变量

def foo():
    print(‘i am foo...‘)

bar = foo
bar()

# 执行上述代码,打印‘i am foo...‘

  2. 函数名可以作为函数的参数进行传递,类型字符串、列表等

def foo():
    print(‘i am foo...‘)

def func(f):
    f()

func(foo)

# 执行上述代码,打印‘i am foo...‘

  3. 函数名可以作为返回值返回

def foo1():
    def foo2():
        print(‘i am foo2‘)
    return foo2

foo = foo1()
foo()

# 执行上述代码,打印‘i am foo2...‘

三、闭包

  首先来看一个闭包的实例。

def outer(num):

    def inner():
        print(num)
    return inner

func = outer(10)
func()

  上述代码便是一个闭包的实例。我们来分析一下代码:

  1. 参数(变量)num的作用域为整个outer函数,其中在inner函数中也是有效的。

  2. inner函数(根据前面的内容,我们可以将其理解为一个变量),其作用域也在函数outer内部有效。

  但是,我们在代码中调用outer函数,此时它返回inner函数被func接收。我们再次执行func函数,它成功地打印了num的值,此时,便有一个疑问,num变量已经跑到它的作用域外面去了,为何它还是有效的?

  原来,上述代码构成了一个闭包函数。

  所谓的闭包函数,由两部分构成。一个是内部函数,另一个是定义内部函数时的环境,其中num这个值便是当时定义inner函数时的环境之一。

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

  return表面上虽然只返回了innner函数,但实际上它将当时定义inner函数时所处的环境一起返回(当然,这个环境并不是单纯的只有num变量,还有其它很多....)。

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

四、装饰器  

  说了这么多,终于可以说到装饰器了。

  装饰器本质上是一个函数,该函数用来处理其它函数,它可以让其它函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是函数对象。它经常用于切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为了已经存在的对象添加额外的功能

  我们来看一个装饰器的实例:

import time

def show_time(func):
    def inner():
        start = time.time()
        func()
        end = time.time()
        print("spend %s" % (end - start))
    return inner

@show_time  # foo = show_time(foo)
def foo():
    print(‘foo...‘)
    time.sleep(2)

@show_time  # bar = show_time(bar)
def bar():
    print(‘bar...‘)
    time.sleep(2)

  此时有两个函数foo()和bar(),我们想查看一下这两个函数的执行时间。此时我们定义一个show_time函数,该函数的参数是一个函数名字,返回值是一个函数。在函数内部,执行传入的函数并且打印其执行的时间。

  函数show_time就是装饰器,它把真正的业务方法func包裹在函数里面,看起来像foo被上下时间函数装饰了。在这个例子中,函数进入和退出时,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程。

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

五、带参数的装饰器

  1. 不定长参数的装饰器

#***********************************不定长参数
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)

  2. 装饰器本身含有参数

  

import time

def time_logger(flag=0):

    def show_time(func):

        def wrapper(*args, **kwargs):
            start = time.time()
            func(*args, **kwargs)
            end = time.time()
            print(‘spend %s‘ % (start - end))

            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(1, 2, 3, 4, 5)

  @time_logger(3)做了两件事情:

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

  2. @show_time add=show_time(add)

  上面的time_logger是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器(一个含有参数的闭包函数)。当我们调用@time_logger(3)时,python能够发现这一层的封装,并把参数传递到装饰器。

原文地址:https://www.cnblogs.com/liushiyi/p/8231779.html

时间: 2024-10-10 10:19:37

python3【基础】-装饰器的相关文章

Python3.5:装饰器的使用

在Python里面函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数,简单来说函数也是变量也可以作文函数的参数 >>> def funA(): ... print('i an funA') ... time.sleep(1) >>> f = now >>> f() i am funA 函数对象有一个__name__属性,可以拿到函数的名字: >>> funA.__name__ 'funA' >>&

python基础—装饰器

python基础-装饰器 定义:一个函数,可以接受一个函数作为参数,对该函数进行一些包装,不改变函数的本身. def foo(): return 123 a=foo(); b=foo; print(a) print(b) 123 <function foo at 0x000001608E2B97B8> 解释:把函数foo赋值给a和b,a 赋值的是调用后的函数,变量的值就是返回值.b 赋值的是调用前的函数,所以b 就是那个赋值的函数.函数本身+(),就是调用. callable(a) False

python3练习-装饰器

在廖雪峰的官方网站学习装饰器章节时,初步理解类似与面向切面编程.记录一下自己的课后习题解法. 问题: 请编写一个decorator,能在函数调用的前后打印出'begin call'和'end call'的日志.写出一个@log的decorator,使它既支持: @log def f(): pass 又支持: @log('execute') def f(): pass 示例代码(个人练习,如有问题欢迎斧正): # ! usr/bin/env python3 # -*- coding:utf-8 -

python基础-装饰器和偏函数

一.装饰器 1.概念:装饰器是一个闭包(内层函数引用外层函数的非全局变量,这个内层函数就可称之为闭包),把一个函数当做参数返回一个替代版的函数,本质上就是一个返回函数的函数. 2.作用:在不修改源代码的基础上,扩展代码的使用. 3.理解装饰器 #不带参数的源函数 def fun1(): print("I love python") fun1() #若我想在原来的基础上,先输出一行的*号,我们可以重新写一个函数 def fun2(): print("***********&qu

python基础---装饰器

1,装饰器: 1)为什么要用装饰器:因为你开发的程序一旦上市,就要遵守源代码开放并且尽量不能修改源代码,函数的调用方式也尽量不要修改,新的需求来了,每一       款软件都是需要更新的,在不修改源代码,不修改函数调用方式,同时还要增加新的功能,怎么实现呢?所以有了装饰器,来满足我们的条件. 2)什么是装饰器:顾名思义,装饰就是修饰,器相当于函数 3)装饰器定义:其本质就是一个函数,功能是为其他函数添加新的一个功能. 2,举例: 1) 现在来建立一个最简单的函数,这组函数值打印"welcome

python3之装饰器

1.装饰器 装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓存.权限验证等场景,装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用.概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能. 装饰器的实现原理: #无参数装饰器原理: def login(funct): print('passed us

python基础===装饰器@property

以下来自Python 3.6.0 Document: class property(fget=None, fset=None, fdel=None, doc=None) Return a property attribute. fget is a function for getting an attribute value. fset is a function for setting an attribute value. fdel is a function for deleting an

python装饰器总结

一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.简单的说装饰器就是一个用来返回函数的函数. 它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓存.权限校验等场景.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用. 概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能. 二.为什么需要装饰器 1.先来看一个简单例子

由浅入深,走进Python装饰器-----第二篇:进阶--函数装饰函数

上一篇:由浅入深,走进Python装饰器-----第一篇:基础 装饰器的使用种类: # 第一种 @函数 被装饰函数 # 第二种 @函数 被装饰类 # 第三种 @类 被装饰类 # 第四种 @函数 被装饰函数 本篇介绍第一种 @函数 被装饰函数 1.1 对带参数的原函数进行修饰 # 默认将old函数的参数传给outer里面的第一层函数 def outer(f): def inner(var): print("1 我是outer函数,接收外部传进来的old :",f) print("