Python 装饰器(Decorator)

Stella981
• 阅读 413

Python  装饰器(Decorator)

装饰模式有很多经典的使用场景,例如插入日志、性能测试、事务处理等等,有了装饰器,就可以提取大量函数中与本身功能无关的类似代码,从而达到代码重用的目的。下面就一步步看看Python中的装饰器。

装饰器本身是一个Python函数,他可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个额外的(函数)对象。

什么时候使用装饰器?

一般在开发过程中,要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,这时就要使用装饰器了。

使用装饰器来修饰函数

  1. 不改变原有的程序,并且可以添加新的功能
  2. 可以提高程序的可重复利用性,并增加了程序的可读性。

要了解装饰器,需要了解一些概念

作用域

在python中,作用域分为两种:全局作用域和局部作用域。

 全局作用域是定义在文件级别的变量,函数名。而局部作用域,则是定义函数内部。

 关于作用域,我们要理解两点:

    a.在全局不能访问到局部定义的变量

    b.在局部能够访问到全局定义的变量,但是不能修改全局定义的变量(当然有方法可以修改)

    关于作用域的问题,只需要记住两点就行:

    全局变量能够被文件任何地方引用,但修改只能在全局进行操作;如果局部没有找到所需的变量,就会往外进行查找,没有找到就会报错。

闭包

详情见  Python 闭包(Closure)

装饰器

  装饰器:外部函数传入被装饰函数名,内部函数返回装饰函数名。

  特点:1.不修改被装饰函数的调用方式 2.不修改被装饰函数的源代码

实例:

# FileName : PyDecorator.py
# Author   : Adil
# DateTime : 2018/4/19 19:04
# SoftWare : PyCharm

'''
装饰器
'''

def outer(some_func):    #  将foo函数(地址)赋值给some_func
    def inner():
        print("before some_func")
        ret = some_func()   #  这里调用foo 返回 1
        print(ret)
        return ret + 1     # 返回 ret + 1 = 1 + 1 = 2
    return inner   # 返回 inner 函数(地址)

def foo():
    return 1

decorated = outer(foo) # 将函数 foo 作为参数传给 outer ,返回 inner 函数对象(函数地址)。
print(decorated)   #  一个替代版函数,函数地址
print(decorated()) # 返回 ret + 1 = 1 + 1 = 2

函数装饰器 @ 符号的应用

Python2.4支持使用标识符@将装饰器应用在函数上,只需要在函数的定义前加上@和装饰器的名称。

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

@outer        # 替代后后面的操作 decorated = outer(foo) # 将函数 foo 作为参数传给 outer ,返回 inner 函数对象(函数地址)。
def foo():
    print("foo")
    return 1

在上面的例子里我们是将原本的方法用装饰后的方法代替:

def outer(some_func):    #  将foo函数(地址)赋值给some_func
    def inner():
        print("before some_func")
        ret = some_func()   #  这里调用foo 返回 1
        print(ret)
        return ret + 1     # 返回 ret + 1 = 1 + 1 = 2
    return inner   # 返回 inner 函数(地址)

@outer
def foo():
    print("foo")
    return 1

foo()
print(foo())



# 执行结果
# before some_func
# foo
# 1
# before some_func
# foo
# 1
# 2

被装饰的函数有参数

def decoratorF(funa):
    def middle(param):
        print(param,end=',')
        funa(param)
    return middle

@decoratorF
def testF(param):
    print("我是 Yang")

testF("Hello")

# 执行结果
# Hello,我是 Yang

更通用的装饰器

有了这招新的技能,我们随随便便就可以写一个能够记录下传递给函数参数的装饰器了。先来个简单地把日志输出到界面的例子:

含多个参数的装饰器

def testL(funa):

    def inner(*args,**kwargs):
        print("paramers are : %s,%s" %(args,kwargs))
        return funa(*args, **kwargs)
    return inner

@testL
def testf(x,y,**k):
    print("testf")

testf('a','b',z='c',t= 'x')

# 打印结果
# paramers are : ('a', 'b'),{'z': 'c', 't': 'x'}
# testf

一个函数被多个装饰器装饰,执行顺序从下往上。

def timer(funt):
    def get_time(*args,**kwargs):
        start_time = time.time()
        funt(*args, **kwargs)
        stop_time = time.time()
        print('execute time is %s'%(stop_time-start_time))
    return get_time



def testL(funa):

    def inner(*args,**kwargs):
        print("paramers are : %s,%s" %(args,kwargs))
        return funa(*args, **kwargs)
    return inner

@timer
@testL
def testf(x,y,**k):
    print("testf")

testf('a','b',z='c',t= 'x')

# 打印结果
# paramers are : ('a', 'b'),{'z': 'c', 't': 'x'}
# testf
# execute time is 0.0

类装饰器

装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。

使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。

在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 call() 方法,那么这个对象就是callable的。

class ClassTest(object):
    def __init__(self,func):
        self._func =func

    def __call__(self):
        print('class decorator running')
        self._func()
        print('class decorator ending')

@ClassTest        # fun_Test = ClassTest(fun_Test)
def fun_Test():
    print('fun_Test')

fun_Test()     # ClassTest(fun_Test)()

# 执行结果
# class decorator running
# fun_Test
# class decorator ending


class TestClass(object):
    def __init__(self):
        pass
    def __call__(self, func):
        def _call(*args,**kwargs):
            print('class decorator running')
            func(*args,**kwargs)
            return func(*args,**kwargs)
        return _call

class Bar(object):
    @TestClass()
    def bar(self,test,ids):    #  bar = TestClass()(bar)
        print('bar')

Bar().bar('Yang','123')

# 执行结果
# class decorator running
# bar
# bar

class Fee(object):
    def __init__(self,func):
        self._func = func

    def __call__(self, name):
        print 'hello',
        self._func(name)

@Fee
def foo(gold):
    print gold,
    print 'lxshen'

foo('大家好')

输出:
hello 大家好 lxshen

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

Python内置装饰器

在Python中有三个内置的装饰器,都是跟class相关的:staticmethod、classmethod 和property。

  • staticmethod 是类静态方法,其跟成员方法的区别是没有 self 参数,并且可以在类不进行实例化的情况下调用
  • classmethod 与成员方法的区别在于所接收的第一个参数不是 self (类实例的指针),而是cls(当前类的具体类型)
  • property 是属性的意思,表示可以通过通过类实例直接访问的信息
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
半臻 半臻
2年前
Python基础5——装饰器
13、装饰器其本质:在不需要做任何代码变动的前提下,增加额外的功能。装饰器返回的是一个函数对象。相当于把函数作为参数传递进去,然后对函数进行装饰其本质就是一个闭包作用:1.给原来的函数增加额外的功能2.把原来的函数作为参数传递进去13.1基本用法标准版的装饰器pythondefwrapper(func):func为原来的函数名defin
Irene181 Irene181
2年前
恶补了 Python 装饰器的六种写法,你随便问~
大家好,我是明哥。今天给大家分享一下关于装饰器的知识点,内容非常干,全程高能,认真吸收看完,一定会对装饰器有更深的理解。Hello,装饰器装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰
Karen110 Karen110
2年前
浅析装饰器的那些事儿
一、装饰器的简单定义外层函数返回里层函数的引用,里层函数引用外层函数的变量。二、装饰器的作用通俗来讲装饰器的作用就是在不改变已有函数代码前提下,为该函数增加新的功能。defrun():print('我会跑')fun()现在我想在原有函数的基础上新增一个功能:我会唱歌。这个时候利用装饰器则轻松可以帮我们实现这个功能。三、实例理解(1)不
Stella981 Stella981
2年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Stella981 Stella981
2年前
Python进阶笔记(2)
'''装饰器装饰器(Decorators)是Python的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短。如果已经接触过FLASK的,想想路由功能。如果没有接触过FLASK的,建议学习下。''''''各种推导式(compre
Stella981 Stella981
2年前
Python装饰器用法实例总结
一、装饰器是什么python的装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。简单的说装饰器就是一个用来返回函数的函数。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离
Stella981 Stella981
2年前
Python中函数装饰器及练习
1)装饰器的理解:1、作用:在不改变原函数的基础上,给函数增加功能   2、返回值:把一个函数当作参数,返回一个替代版的函数3、本质:返回函数的函数4、应用场景:计时器、记录日志、用户登陆认证、函数参数认证2)无参函数装饰器  实例:被装饰的函数没有参数     执行结果为:  
3A网络 3A网络
1年前
Golang 常见设计模式之装饰模式
Golang常见设计模式之装饰模式想必只要是熟悉Python的同学对装饰模式一定不会陌生,这类Python从语法上原生支持的装饰器,大大提高了装饰模式在Python中的应用。尽管Go语言中装饰模式没有Python中应用的那么广泛,但是它也有其独到的地方。接下来就一起看下装饰模式在Go语言中的应用。简单装饰器我们通过一个简单的例子来
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这