python装饰器用法

Python 装饰器用法

一、python装饰器工作原理

了解装饰器之前需要了解什么是闭包函数

必包函数,简而言之,就是函数的嵌套,一个函数嵌套一个函数.

所以Python的装饰器本质上是一个嵌套函数,它接受被装饰的函数(func)作为参数,并返回一个包装过的函数。这样我们可以在不改变被装饰函数的代码的情况下给被装饰函数或程序添加新的功能。有了装饰器,我们就可以抽离出大量与函数功能本身无关的代码,增加一个函数的重用性。

@语法糖 

它放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上。和这个函数绑定在一起。在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数作为参数传入它头顶上这顶帽子,这顶帽子我们称之为装饰器或装饰函数

图注解:如果在调用b()时,又想调用到a()的方法,就把a()作为一个装饰器,用@语法糖装饰到b()上,相当于b()作为参数传入到a()中实例出一个对象即b=a(b),可以理解@a = a(b)


装饰器调用顺序

执行单个装饰器内部调用顺序:

执行多个装饰器内部调用顺序:

使用装饰器遵循就近原则,由近到远;装饰器内内部调用遵循就远原则,由远到近

二、python有哪些装饰器及作用

1.函数装饰器

  函数是一个对象,意味着:

 能在函数中定义一个函数

 能作为参数传递

 能作为返回值

def cap(func):

    # *args表示所有的位置参数,**kwargs表示所有的关键字参数

    def wrapper(*args, **kwargs):

        print('要带帽子')

        return func(*args, **kwargs)

    return wrapper

@cap

def person():

    print('我是个人')

person()

这里在函数前加上 @cap 相当于在定义函数后执行了一条语句, cap_person = cap(person),再执行cap_person()


2.带参装饰器

foreign = "American"

def param(value):

    def param_decorator(func):

        # *args表示所有的位置参数,**kwargs表示所有的关键字参数

        def wrapper(*args, **kwargs):

            if value == "American":

                print("我要带黄帽子")

            elif value == "Chinese":

                print("我要带红帽子")

            return func(*args, **kwargs)

        return wrapper

    return param_decorator

@param(foreign)

def person_A():

    print('我是个美国人')

@param("Chinese")

def person_B():

    print('我是个中国人')

person_A()

person_B()

我们可以在装饰器外部再套上一层函数,用该函数的参数接收我们想要打印的数据,并将先前的 param_decorator 函数作为返回值。这就是之前学到的闭包的一种功能,就是用闭包来生成一个命名空间,在命名空间中保存我们要打印的值 value。



3.warps装饰器

一个函数不止有他的执行语句,还有着 __name__(函数名),__doc__ (说明文档)等属性,我们之前的例子会导致这些属性取的是装饰器的属性信息,而非执行的函数的属性信息。

一般通过 functools 模块下的 wraps 装饰器。

# -*- coding: UTF-8 -*-

import functools

from functools import wraps

import time

st = time.localtime().tm_sec

def wraps_decorator(func):

    # *args表示所有的位置参数,**kwargs表示所有的关键字参数

    # @wraps(func)

    @functools.wraps(func)  # 不使用内置的wraps时,实际的内存地址是指向了wrapper()函数

    def wrapper(*args, **kwargs):

"""我是个帽子"""

        print(st)

        return func(*args, **kwargs)

    return wrapper

@wraps_decorator

def Chinese():

""" 中国人带红帽子 """

@wraps_decorator

def American():

"""美国人带黄帽子"""

American()

print(American.__name__, American.__doc__)

Chinese()

print(Chinese.__name__, Chinese.__doc__)



4.内置装饰器

有三种我们经常会用到的装饰器, property、 staticmethod、 classmethod,主要用于类方法上

  a.property 装饰器

property 装饰器用于类中的函数,使得我们可以像访问属性一样来获取一个函数的返回值。

@property装饰器就是负责把一个方法变成属性调用,快速实现set和get方法

class ClassRoom(object):

    course = 'English'

    student = 'Lily'

    def __init__(self, score):

        self.score = score

    @property

    def score(self):

        return self._score

    @score.setter  # 由上面的装饰器衍生出来的装饰器,设置属性信息

    def score(self, value):

        self._score = value

        print(self._score)

    @score.getter  # 获取属性信息

    def score(self):

        print('课程:' + self.course + '学生:' + self.student + '分数:' + self._score)

        return self.course + self.student + self._score

s = ClassRoom("100")

s.score = "70"  # 给属性赋值

s.score  # 获取属性信息

代码中score()函数加上@property装饰器后就作为这个类的属性信息,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作.我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。

  b.staticmethod 装饰器

staticmethod 装饰器同样是用于类中的方法,这表示这个方法将会是一个静态方法,意味着该方法可以直接被调用无需实例化,但同样意味着它没有 self 参数,也无法访问实例化后的对象。

staticmethod 是类静态方法,其跟成员方法的区别是没有 self 参数,并且可以在类不进行实例化的情况下调用

class ClassRoom:

    course = 'English'

    teacher = 'Tom'

    student = 'Lily'

    @staticmethod  # 加了@staticmethod,静态方法,可直接被调用无需实例

    def sth():

        print("hello 把外部方法集成到类里")

ClassRoom.sth()

c.classmethod 装饰器

classmethod 依旧是用于类中的方法,这表示这个方法将会是一个类方法,意味着该方法可以直接被调用无需实例化,但同样意味着它没有 self 参数,也无法访问实例化后的对象。相对于 staticmethod 的区别在于它会接收一个指向类本身的 cls 参数。

classmethod 与成员方法的区别在于所接收的第一个参数不是 self (类实例的指针),而是cls(当前类的具体类型)

class ClassRoom(object):

    course = 'english'

    teacher = 'lily'

    student = 'tom'

    @staticmethod

    def get_info1():  # 加了@staticmethod,静态方法,可直接被调用无需实例

        print("---我是staticmethod---")

    def __init__(self, course, teacher, student):

        self.course = course

        self.teacher = teacher

        self.student = student

    @classmethod  # 类方法,可直接被调用无需实例,会接收自身的cls参数,好处:以后重构类的时候无需修改构造函数,只需处理额外的函数然后用classmethod装饰即可

    def get_info(cls):

        print("---我是classmethod---")

        print('课程:' + cls.course, '老师: ' + cls.teacher, '学生:' + cls.student)

    def out_info(self):  # 普通函数调用

        print("course: " + self.course, "teacher:" + self.teacher, "student: " + self.student)

# 普通函数输出,需要先对类进行实例化才能调用

Info = ClassRoom('english', 'lily', 'tom')

Info.out_info()

# staticmethod静态类方法,无需实例可直接调用

ClassRoom.get_info1()

# classmethod类方法,无需实例可直接调用

ClassRoom.get_info()

5.类装饰器

定义:类装饰器就是通过类来装饰函数

类能实现装饰器的功能, 是由于当我们调用一个对象时,实际上调用的是它的 __call__ 方法。

通过这个特性,我们便可以用类的方式来完成装饰器,功能与刚开始用函数实现的一致。

类装饰器包括类装饰函数以及类装饰类、在执行调用装饰方面,类装饰器与函数装饰器是有区别的 类装饰器在整体定义完成之后就立即执行装饰, 函数装饰器在整体定义完成后必须显样调用去执行装饰

# -*- coding: UTF-8 -*-

def info():

    print("test")

class Decorator:

    print('----1')

    def __init__(self, func):

        print('----2')

        self.func = func

    def __call__(self, *args, **kwargs):  # 把类的实例变成可调用对象(callable)

        print('hi')

        result = self.func(*args, **kwargs)  # 类装饰器装饰函数功能

        print('----3')

        return result

@Decorator  # @Decorator =》say_hello=Decorator(say_hello)

def say_hello():

    print('同学你好')

info()

say_hello()

三、python装饰器适用场景

Python的装饰器广泛应用于缓存、权限校验(如django中的@login_required和@permission_required装饰器)、性能测试(比如统计一段程序的运行时间)和插入日志等应用场景.

以下例子为日志应用场景的典型例子,统计一段程序的运行日志结果

# -*- coding: UTF-8 -*-

import functools

import logging

from functools import wraps

import datetime

def _logger():

    logger = logging.getLogger(__name__)

    logger.setLevel('DEBUG')

    handler = logging.StreamHandler()

    formatter = logging.Formatter(

        '[%(levelname)s][%(process)d][%(thread)d]--%(asctime)s--[%(filename)s %(funcName)s %(lineno)d]: %(message)s')

    handler.setFormatter(formatter)

    logger.addHandler(handler)

    return logger

def running_logger(logger):

    def log(func):

        @functools.wraps(func)

        def inner(*args, **kwargs):

            start_time = datetime.datetime.now()

            rs = func(*args, **kwargs)

            exec_time = str(datetime.datetime.now() - start_time)

            logger.info('[func: {}] cost: {}ms, results:{}.'.format(func.__name__, exec_time, rs))

        return inner

    return log

logger = _logger() 

@running_logger(logger)

def select_sort(list):

'''doc of sort'''

    for i in range(len(list) - 1):

        min_index = i

        for j in range(i + 1, len(list)):

            if list[j] < list[min_index]:

                min_index = j

        list[min_index], list[i] = list[i], list[min_index]

    return list

@running_logger(logger)

def bubble_sort(list):

    for i in range(len(list) - 1):

        for j in range(len(list) - 1 - i):

            if list[j] > list[j + 1]:

                list[j], list[j + 1] = list[j + 1], list[j]

    return list

@running_logger(logger)

def shell_sort(list):

    mid = len(list) // 2

    while mid > 0:

        for i in range(mid, len(list)):

            j = i

            cur = list[i]

            while j - mid >= 0 and cur < list[j - mid]:

                list[j] = list[j - mid]

                j -= mid

            list[j] = cur

        mid //= 2

    return list

select_sort([1, 51, 3, 8, 31, 36, 89, 30, 90, 45, 76, 32, 88, 90])

bubble_sort([10, 5, 32, 8, 31, 36, 89, 30, 34, 26, 66, 27, 98, 3])

shell_sort([10, 5, 32, 8, 31, 36, 89, 30, 34, 26, 66, 27, 98, 3])

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容