Python 函数装饰器应用教程

目录
  • 一、什么是函数装饰器
  • 二、函数装饰器的执行时机
  • 三、变量作用域
  • 四、闭包
  • 五、保留函数的元数据
  • 七、使用lru_cache缓存函数执行结果
  • 八、使用singledispatch实现泛型函数
  • 九、通过参数控制函数装饰器的行为

一、什么是函数装饰器

1.函数装饰器是Python提供的一种增强函数功能的标记函数;

2.装饰器是可调用的函数对象,其参数是另一个函数(被装饰的函数);

我们可以使用修饰器来封装某个函数,从而让程序在执行这个函数之前与执行完这个函数之后,分别运行某些代码。这意味着,调用者传给函数的参数值、函数返回给调用者的值,以及函数抛出的异常,都可以由修饰器访问并修改。这是个很有用的机制,能够确保用户以正确的方式使用函数,也能够用来调试程序或实现函数注册功能,此外还有许多用途。

二、函数装饰器的执行时机

函数装饰器在被装饰函数编译解析之后直接执行,装饰器是按照从上到下执行的;

函数装饰器内部定义的返回函数依附在装饰器的执行环境中;

函数装饰器每次执行都会生成新的返回函数;

import sys

def dec1(m):
    print(f'{sys._getframe().f_code.co_name} is execute, arg {m.__name__}')
    def newm1():
        print(f'{sys._getframe().f_code.co_name}')

    return newm1;

@dec1
def m1():
    print(f'{sys._getframe().f_code.co_name}')

@dec1
def m11():
    print(f'{sys._getframe().f_code.co_name}')

if __name__ == '__main__':
    print(m1)
    print(m11)
    print(f'm1==m11:{m1==m11}')

# dec1 is execute, arg m1
# dec1 is execute, arg m11
# <function dec1.<locals>.newm1 at 0x7fdfa97d9160>
# <function dec1.<locals>.newm1 at 0x7fdfa97d91f0>
# m1==m11:False

三、变量作用域

Python中将变量声明和赋值操作合一,很容易导致函数局部变量覆盖函数外的变量

b=6
def f():
    print(b)

f()

# 6
b=6
def f():
    print(b)
    b = 9

f()

# UnboundLocalError: local variable 'b' referenced before assignment

通过生成的字节码可以看到两者对变量b的处理的差异,前者直接LOAD_GLOBAL,后者是LOAD_FAST,但是给b赋值却在print之后导致报错;

from dis import dis

b=6
def f():
    print(b)
    # b = 9

dis(f)

 # 5           0 LOAD_GLOBAL              0 (print)
 #              2 LOAD_GLOBAL              1 (b)
 #              4 CALL_FUNCTION            1
 #              6 POP_TOP
 #              8 LOAD_CONST               0 (None)
 #             10 RETURN_VALUE
from dis import dis

b=6
def f():
    print(b)
    b = 9

#  5          0 LOAD_GLOBAL              0 (print)
#             2 LOAD_FAST                0 (b)
#             4 CALL_FUNCTION            1
#             6 POP_TOP

#  6          8 LOAD_CONST               1 (9)
#             10 STORE_FAST               0 (b)
#             12 LOAD_CONST               0 (None)
#             14 RETURN_VALUE

可以使用global来强制声明b是全局变量,然后就可以重新赋值了;

b=6
def f():
    global b
    print(b)
    b = 9

f()

# 6

四、闭包

闭包是是指可以访问非在函数体内定义的非全局变量的函数;

通过函数的__code__及__closure__可以看到局部变量和自由变量及闭包的情况;

def makesum():
    sum = [0]

    def s(val):
        sum[0] += val
        return sum[0]

    return s

s = makesum()
print(s(1))
print(s(2))
print(s.__code__.co_varnames)
print(s.__code__.co_freevars)
print(s.__closure__)
print(s.__closure__[0].cell_contents)

# 1
# 3
# ('val',)
# ('sum',)
# (<cell at 0x7f63321f1b20: list object at 0x7f63321e8a00>,)
# [3]

基于三中Python变量作用域的缘故,上边的sum只能使用列表对象,python提供的nonlocal关键字可以直接使用int类型的变量;

def makesum():
    sum = 0

    def s(val):
        nonlocal sum
        sum += val
        return sum

    return s

s = makesum()
print(s(1))
print(s(2))
print(s.__code__.co_varnames)
print(s.__code__.co_freevars)
print(s.__closure__)
print(s.__closure__[0].cell_contents)

# 1
# 3
# ('val',)
# ('sum',)
# (<cell at 0x7f73e6a4ab20: int object at 0x7f73e6b47970>,)
# 3

五、保留函数的元数据

函数装饰器默认会使用返回的函数完全取代被装饰的函数,这样可能会导致序列化或者开发工具智能提示的问题;可以使用functools.wraps来保留原始函数的标准属性(name、module、__annotations__等);

import functools

def dec(func):
    def real():
        '''this is real function'''
        pass
    return real

def wrapsdec(func):
    @functools.wraps(func)
    def real():
        '''this is real function'''
        pass
    return real

@dec
def max(nums):
    '''this is max function'''
    pass

@wrapsdec
def sum(nums):
    '''this is sum function'''

print(max)
print(max.__name__)
print(max.__doc__)
print(help(max))
print()
print(sum)
print(sum.__name__)
print(sum.__doc__)
print(help(sum))

# <function dec.<locals>.real at 0x7f1bfd4241f0>
# real
# this is real function
# Help on function real in module __main__:
#
# real()
#     this is real function
#
# None
#
# <function sum at 0x7f1bfd424280>
# sum
# this is sum function
# Help on function sum in module __main__:
#
# sum(nums)
#     this is sum function
#
# None

六、支持关键字参数、位置参数

def dec(func):
    def real(*args, **kwargs):
        result = func(*args, **kwargs)
        return result

    return real

@dec
def sum(*nums, **kwargs):
    s = 0
    for n in nums:
        s = s + n

    for a in kwargs.values():
        s = s + a
    return s

print(sum(1,2,3,first=1))

七、使用lru_cache缓存函数执行结果

lru_cache内部使用函数的参数作为key,使用dict进行缓存执行结果,减少重复计算;

默认支持缓存128条记录,超过后采用LRU方式丢弃多余记录;

需要注意执行中不要改变参数,否则会影响字典key的有效性;

from functools import lru_cache

@lru_cache()
def fibonacci(n):
    print(f'fibonacci({n})')
    if n<2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(6))

# fibonacci(6)
# fibonacci(5)
# fibonacci(4)
# fibonacci(3)
# fibonacci(2)
# fibonacci(1)
# fibonacci(0)
# 8

八、使用singledispatch实现泛型函数

python不支持方法或者函数的重载,我们无法单独定义不同参数类型的同名函数;

singledispatch提供了这样一种能力,其通过注册具体的参数类型和关联的函数;

我们可以在自己的模块定义自己的类型,并实现自己的自定义函数;

import math
import numbers
from functools import singledispatch

@singledispatch
def absall(obj):
    return abs(obj)

@absall.register(numbers.Number)
def numabs(num):
    return abs(num)

@absall.register(numbers.Complex)
def cabs(c):
    return math.sqrt(c.real*c.real + c.imag* c.imag)

class Line:

    def __init__(self, startx, starty, endx, endy):
        self.startx = startx
        self.starty = starty
        self.endx = endx
        self.endy = endy

@absall.register(Line)
def lineabs(line):
    y =line.endy - line.starty
    x = line.endx - line.startx
    return math.sqrt(x*x + y*y)

print(absall(-1.1))
print(absall(3+4j))

l = Line(1,2,4,6)
print(absall(l))

# 1.1
# 5.0
# 5.0

九、通过参数控制函数装饰器的行为

函数装饰器本身不支持传递参数,解析源代码的时候,python会将被装饰的函数对象作为第一个参数传递给装饰器;

我们可以通过在函数装饰器外再嵌套一个函数工厂来承载装饰特定函数的时候设置的参数; 

def accesscontrol(checkuser=True, updatesession=True):

    def dec(func):
        def checkuserf():
            print('check user')
            return True

        def updatesessionf():
            print('update session')
            return True

        def access():
            if checkuser:
                checkuserf()

            if updatesession:
               updatesessionf()

            func()

        return access

    return dec

@accesscontrol()
def pushitem():
    print('pushitem')
    return True

@accesscontrol(checkuser=False)
def getitem():
    print('getitem')
    return True

# pushitem()
# print()
# getitem()
#
# check user
# update session
# pushitem
#
# update session
# getitem

以上就是Python 函数装饰器应用教程的详细内容,更多关于Python 函数装饰器的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python函数装饰器常见使用方法实例详解

    本文实例讲述了Python函数装饰器常见使用方法.分享给大家供大家参考,具体如下: 一.装饰器 首先,我们要了解到什么是开放封闭式原则? 软件一旦上线后,对修改源代码是封闭的,对功能的扩张是开放的,所以我们应该遵循开放封闭的原则. 也就是说:我们必须找到一种解决方案,能够在不修改一个功能源代码以及调用方式的前提下,为其加上新功能. 总结:原则如下: 1.不修改源代码 2.不修改调用方式 目的:在遵循1和2原则的基础上扩展新功能. 二.什么是装饰器? 器:指的是工具, 装饰:指的是为被装饰对象添加

  • Python函数装饰器的使用教程

    典型的函数装饰器 以下示例定义了一个装饰器,输出函数的运行时间: 函数装饰器和闭包紧密结合,入参func代表被装饰函数,通过自由变量绑定后,调用函数并返回结果. 使用clock装饰器: import time from clockdeco import clock @clock def snooze(seconds): time.sleep(seconds) @clock def factorial(n): return 1 if n < 2 else n*factorial(n-1) if _

  • Python函数装饰器实现方法详解

    本文实例讲述了Python函数装饰器实现方法.分享给大家供大家参考,具体如下: 编写函数装饰器 这里主要介绍编写函数装饰器的相关内容. 跟踪调用 如下代码定义并应用一个函数装饰器,来统计对装饰的函数的调用次数,并且针对每一次调用打印跟踪信息. class tracer: def __init__(self,func): self.calls = 0 self.func = func def __call__(self,*args): self.calls += 1 print('call %s

  • Python函数装饰器原理与用法详解

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

  • Python 函数装饰器详解

    目录 使用场景 授权(Authorization) 日志(Logging) 带参数的装饰器 在函数中嵌入装饰器 装饰器类 总结 装饰器(Decorators)是 Python 的一个重要部分.简单地说:他们是修改其他函数的功能的函数.他们有助于让我们的代码更简短,也更Pythonic(Python范儿).大多数初学者不知道在哪儿使用它们,所以我将要分享下,哪些区域里装饰器可以让你的代码更简洁.首先,让我们讨论下如何写你自己的装饰器. 这可能是最难掌握的概念之一.我们会每次只讨论一个步骤,这样你能

  • Python 函数装饰器应用教程

    目录 一.什么是函数装饰器 二.函数装饰器的执行时机 三.变量作用域 四.闭包 五.保留函数的元数据 七.使用lru_cache缓存函数执行结果 八.使用singledispatch实现泛型函数 九.通过参数控制函数装饰器的行为 一.什么是函数装饰器 1.函数装饰器是Python提供的一种增强函数功能的标记函数: 2.装饰器是可调用的函数对象,其参数是另一个函数(被装饰的函数): 我们可以使用修饰器来封装某个函数,从而让程序在执行这个函数之前与执行完这个函数之后,分别运行某些代码.这意味着,调用

  • python函数装饰器用法实例详解

    本文实例讲述了python函数装饰器用法.分享给大家供大家参考.具体如下: 装饰器经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计, 有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用.概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能. #! coding=utf-8 import time def timeit(func): def wrapper(a): start = time.clock() func

  • 如何实现一个python函数装饰器(Decorator)

    装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象.它经常用于为已有函数/类添加记录日志.计时统计.性能测试等. 首先定义一个倒计时函数,这个函数的功能非常简单,就是把n从当前值减少到0. def countdown(n): while n > 0: print('time' + str(n)) n -= 1 print(countdown.__name__) 程序输出: countdown 1.为函数增

  • Python函数装饰器的使用详解

    目录 装饰器 装饰器的定义 装饰器的意义 装饰器的使用 无参装饰器 有参装饰器 实例练习 总结 装饰器 装饰器的定义 关于装饰器的定义,我们先来看一段github上大佬的定义: Function decorators are simply wrappers to existing functions.In the context of design patterns,decorators dynamically alter the functionality of a function, met

  • python函数装饰器构造和参数传递

    目录 一.闭包函数 二.python装饰器构造 三.python装饰器叠加 四.python装饰器传参 1.装饰器单个参数传递 2.装饰器多个参数传递 3.装饰器的不定长参数 五.带返回值的装饰器 前言: 通过@语句调用一个函数去给另一个函数增加或修改一些功能的语法规则称之为Python装饰器.下面通过一个小案例来简单的理解什么是装饰器. def dog():     print('摇尾巴')     def cat():         print('喵喵喵')          call =

随机推荐