Python学习之装饰器与类的装饰器详解

目录
  • 装饰器
    • 装饰器的定义
    • 装饰器的用法
  • 类中的装饰器
    • 类的装饰器-classmethod
    • 类的装饰器-staticmethod
    • 类的装饰器-property

通过学习装饰器可以让我们更好更灵活的使用函数,通过学会使用装饰器还可以让我们的代码更加优雅。

在我们的实际工作中,很多场景都会用到装饰器,比如记录一些日志、或者屏蔽一些不太合法的程序执行从而使我们的代码更加安全。

装饰器

什么是装饰器?虽然对这个次感到陌生,但是完全不需要担心。

首先,装饰器也是一种函数;只不过装饰器可以接收 函数 作为参数来传递。

并且可以通过 return 可以返回一个函数,装饰器通过接收一个函数,对它在装饰器内部进行处理、调用,并返回一个新的函数,同时还可以动态增强传入函数的功能。

装饰器整个流程是这样的:

  • A函数是装饰器,B函数是A函数传入的参数。
  • 将B函数在A函数中执行,在A函数中可以选择执行或不执行,也可以对B函数的结果进行二次加工处理。

接下来我们看看 装饰器长什么样子

def a():

    def b():
        print(helloworld)

    b()

a()
b()

a() 函数中书写了一个 b() 函数,并在 a() 函数中调用 b() 函数。是不是非常类似在类中定义一个局部函数并调用的例子?其实装饰器就是有些类似这样的操作,只不过被装饰器调用的函数是通过 参数 的形式传进去,并在 b() 函数中执行。

我们在定义完 a() 函数之后进行调用,可以正常处理。但是 b() 函数 是 a() 函数的局部函数 如果在外部调用会报错。(如上文中的第十行,就会报错)

装饰器的定义

示例如下:

def out(func_args):            # 装饰器的第一层函数被称为 外围函数 , 'func_args' 为要处理的函数

    def inter(*args, **kwargs):        # 外围函数 的函数体内定义的函数被称为 内嵌函数 ;传入的参数为要处理的 func_args 函数的参数
                                    # 这里我们并不知道 func_args 函数需要传入进来的参数是什么,所以目前写传入可变参数是比较合理的
        return func_args(*args, **kwargs)        # 在 内嵌函数 的函数体内调用 func_args 函数,并将可变参数传入
                                                # 其实这里我们可以处理更多的逻辑;
                                                # 我们可以选择执行或者不执行,甚至可以func_args 函数的执行结果进行二次处理

    return inter             # 书写完 内嵌函数的业务之后,我们在 外围函数体内返回 内嵌函数
                            # 需要注意的是,这里是不执行的(因为没有加括号),这是装饰器的定义规则,是必不可少的
                            # 只有外围函数返回内嵌函数,才可以被之后的代码执行;(因为所有的业务都在内嵌函数中,不返回就无法执行调用)

装饰器的用法

在我们日常工作中,装饰器的使用方法有两种。

第一种:将被调用的函数直接作用为参数传入装饰器的外围函数括弧内;示例如下:

def a(func):

    def b(*args, **kwargs):
        return func(*args, **kwargs)

    return b

def c(name):
    print(name)

a(c)('Neo')

# >>> 执行结果如下:
# >>> Neo

第二种:将装饰器与被调用函数绑定在一起, @ 符号 + 装饰器函数放在被调用函数的上一行,被调用的函数正常定义,只需要直接调用被执行函数即可。示例如下:

def a(func):

    def b(*args, **kwargs):
        return func(*args, **kwargs)

    return b

@a
def c(name):
    print(name)

c('Neo')

# >>> 执行结果如下:
# >>> Neo

最常用的装饰器用法为第二种。

现在我们构建一个 检查字符串类型的装饰器,加深一下对装饰器的理解。

def check_ok(func):

    def inner(*args, **kwargs):
        result = func(*args, **kwargs)
        if result == 'OK':
            return '传入的参数数据为:\'%s\'' % result
        else:
            return '传入的参数数据不为:\'OK\''

    return inner

@check_ok
def test_str(data):
    return data

result = test_str('OK')
print(result)

# >>> 执行结果如下:
# >>> 传入的参数数据为:'OK'

result = test_str('NO')
print(result)

# >>> 执行结果如下:
# >>> 传入的参数数据不为:'OK'

以上就是一个装饰器的简单用法,后续的学习内容会接触到更多的高级用法。

类中的装饰器

类的装饰器 - classmethod

classmethod 的功能:可以将类函数不经过实例化即可直接被调用

classmethod 的用法:示例如下

@classmethod
def func(cls, ...):
    todo

# >>> cls 替代普通类函数中的 self ;
# >>> 变更为 cls ,表示当前的类
# >>> self 代表的是 实例化对象,所以只有通过实例化后,才可以调用
# >>> cls 代表的是 类 ,所以即使不通过实例化,也可以调用 

# *****************************************************

class Test(object):

    @classmethod
    def add(cls, a, b):
        return a + b

print(Test.add(1, 3))

# >>> 执行结果如下:
# >>> 4

演示案例:

class Cat(object):
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(self.name, '喜欢吃鱼')

    @classmethod
    def work(cls):
        print('会抓老鼠')

dragonLi = Cat('狸花猫')
print(dragonLi.eat(), dragonLi.work())

# >>> 执行结果如下:
# >>> 狸花猫 喜欢吃鱼
# >>> 会抓老鼠

接下来我们不使用 类的实例化 ,直接使用 类 调用 eat() 函数 与 work() 函数

class Cat(object):
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(self.name, '喜欢吃鱼')

    @classmethod
    def work(cls):
        print('会抓老鼠')

dragonLi = Cat('狸花猫')

Cat.eat()

# >>> 执行结果如下:
# >>> TypeError: Cat.eat() missing 1 required positional argument: 'self'
# >>> 报错缺少重要参数 'self'  (没有进行实例化的类,类无法直接调用类函数)

Cat.work()
# >>> 执行结果如下:
# >>> 会抓老鼠
# >>> 绑定了 classmethod 装饰器 的 work() 函数,即使没有实例化,也可以直接被 类 调用

再尝试一下看看 没有装饰器的 eat() 函数 与 使用了 classmethod 装饰器 work() 之间可不可以互相调用

class Cat(object):
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(self.name, '喜欢吃鱼')

    @classmethod
    def work(cls):
        print('会抓老鼠')
        cls.eat()			# 在 classmethod 装饰器的 work() 函数内 调用 eat() 函数

dragonLi = Cat('狸花猫')
dragonLi.work()

# >>> 执行结果如下:
# >>> TypeError: Cat.eat() missing 1 required positional argument: 'self'
# >>> 同样报错缺少重要参数 'self'
class Cat(object):
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(self.name, '喜欢吃鱼')
        self.work()

    @classmethod
    def work(cls):
        print('会抓老鼠')

dragonLi01 = Cat('狸花猫')
dragonLi01.eat()

# >>> 执行结果如下:
# >>> 执行结果如下:
# >>> 狸花猫 喜欢吃鱼
# >>> 会抓老鼠

综合以上两个场景,我们得出以下结论:

  • 在带有 classmethod 装饰器 的 函数 内,是无法调用普通的 带有 self 的函数的
  • 但是在普通的带有 self 的类函数内,是可以调用带有 classmethod 装饰器 的 函数的

类的装饰器 - staticmethod

staticmethod 的功能:可以将 类函数 不经过实例化而直接被调用,被该装饰器调用的函数不需要传入 self 、cls 参数,并且无法在该函数内调用其他 类函数 或 类变量

staticmethod 的用法:参考如下

@staticmethod
def func(...):
    todo

# >>> 函数内无需传入 cls 或 self 参数

# *****************************************************

class Test(object):

    @staticmethod
    def add(a, b):
        return a + b

print(Test.add(1, 3))

# >>> 执行结果如下:
# >>> 4

接下来我们在上文的 Cat() 类基础演示一下 staticmethod 装饰器 (新建一个 color() 函数,使用 staticmethod 装饰器 )

class Cat(object):
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(self.name, '喜欢吃鱼')
        self.work()

    @classmethod
    def work(cls):
        print('会抓老鼠')

    @staticmethod
    def color():
        print('黄棕色')

dragonLi = Cat('狸花猫')
print(dragonLi.eat(), dragonLi.color())

# >>> 执行结果如下:
# >>> 狸花猫 喜欢吃鱼
# >>> 会抓老鼠
# >>> 黄棕色
# >>> 从执行结果来看, staticmethod 装饰器的 color() 函数可以被实例化后的对象 dragonLi 调用。
# >>> 那么可以被 Cat() 类 直接调用么?我们往下看

print(Cat.color())

# >>> 执行结果如下:
# >>> 黄棕色
# >>> 可以看到,staticmethod 装饰器构造的 color() 函数,即使没有被实例化,依然可以直接被 类 调用

同样的,也尝试一下 staticmethod 装饰器构造的 color() 函数 是否能够在类函数中互相调用。

class Cat(object):
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(self.name, '喜欢吃鱼')
        self.work()
        self.color()

    @classmethod
    def work(cls):
        print('会抓老鼠')

    @staticmethod
    def color():
        print('黄棕色')

dragonLi = Cat('狸花猫')
dragonLi.eat()

# >>> 执行结果如下:
# >>> 狸花猫 喜欢吃鱼
# >>> 会抓老鼠
# >>> 黄棕色
# >>> 结合执行结果得出结论:staticmethod 装饰器构造的 color() 函数 可以在 eat() 类函数中被调用

与带有 classmethod 装饰器 的 函数 一样,staticmethod 装饰器构造的 函数也是无法调用普通的 带有 self 的函数的,这里就不再书写演示代码进行演示了。(staticmethod 装饰器构造的 函数也是无法调用普通的 带有 self 的函数会报错 : NameError: name 'self' is not defined )

类的装饰器 - property

property 的功能:可以将类函数的执行免去小括号,类似于直接调用类的变量(属性)

staticmethod 的用法:参考如下

@property
def func(self):
    todo

# >>> 不能传入参数,无重要函数说明

# *************************示例如下*************************

class Test(object):

    def __init__(self, name):
        self.name = name

    @property
    def call_name(self):
        return 'Hello {}'.format(self.name)

test = Test('Neo')
result = test.call_name			# 不需要添加 小括号 即可调用 call_name 函数;
								# 关于 staticmethod 不可传入参数,其实并不是不可以传入参数,而是传入的方法比较另类,我们继续往下看
print(result)

# >>> 执行结果如下:
# >>> Hello Neo

重新创建一个 Dog 类 ,然后我们继续演示。

class Dog(object):

    def __init__(self, name):
        self.__name = name

    @property
    def type(self):
        if self.__name in ['哈士奇', '萨摩耶', '阿拉斯基']:
            return self.__name, '是雪橇犬,\'雪橇三傻\'之一'
        elif self.__name in ['吉娃娃', '博美犬', '约克夏']:
            return self.__name, '是小型犬'
        else:
            return self.__name, '我暂时不知道这是什么犬种,也许它是\'泰日天\'的亲戚'

dog = Dog(name='哈士奇')
print(dog.type)			# 这里我们并不需要 dog.type + () 小括号,即可调用 type() 函数

# >>> 执行结果如下:
# >>> ('哈士奇', "是雪橇犬,'雪橇三傻'之一")
# >>> 这里我们看到 当 Dog 类 实例化 dog 变量之后,我们传入的 '哈士奇' 参数是不可更改的,如果我们尝试利用赋值的方式修改传入的参数呢?

dog = Dog(name='哈士奇')
dog.type = '约克夏'
print(dog.type)

# >>> 执行结果如下:
# >>> AttributeError: can't set attribute
# >>> 报错:属性错误,不可以设置这个属性
# >>> 其实,property 装饰器绑定的函数的参数并不是不可以更改,只是更改的方式比较特殊,并不是不能通过赋值的形式传入参数,我们继续往下看。

首先,我们已经使用了 @property 绑定了我们的 type 函数,这是一个返回值的方法。 所以我们要如何给 type() 函数赋值呢?其实很简单,我们可以通过 @type 对应上 type() 函数,在它的函数内部有一个函数 setter ;然后再定义一个 type 函数,在这个新定义的 type() 函数内定义一个值 value (可以是任意的名字,但这里需要注意,只能定义一个值)。然后再通过设置一个 self.__name = value ,如此就可以达到修改传入参数的目的。废话不多说了,看下方的示例:

class Dog(object):

    def __init__(self, name):
        self.__name = name

    @property
    def type(self):
        if self.__name in ['哈士奇', '萨摩耶', '阿拉斯基']:
            return self.__name, '是雪橇犬,\'雪橇三傻\'之一'
        elif self.__name in ['吉娃娃', '博美犬', '约克夏']:
            return self.__name, '是小型犬'
        else:
            return self.__name, '我暂时不知道这是什么犬种,也许它是\'泰日天\'的亲戚'

    @type.setter
    def type(self, value):
        self.__name = value

dog = Dog(name='哈士奇')
dog.type = '约克夏'
print(dog.type)

# >>> 执行结果如下:
# >>> ('约克夏', '是小型犬')

附:使用最广泛的装饰器为 classmethod

以上就是Python学习之装饰器与类的装饰器详解的详细内容,更多关于Python装饰器的资料请关注我们其它相关文章!

(0)

相关推荐

  • 深入了解python装饰器

    目录 一.装饰器 1.相关知识点 2.语法糖 3.装饰器模板 4.有参装饰器 一.装饰器 1.相关知识点 *args:负责将多余的位置实参汇总,赋值给args **kwargs:负责将多余的关键字实参汇总,赋值给kwargs 命名空间与作用域 函数对象: 可以把函数当成参数传入 可以把函数当做返回值返回 函数的嵌套定义:在函数内定义函数 闭包函数:父函数的返回值为一个函数,被返回的函数调用了父函数的局部变量,且该函数可以在父函数外部执行 装饰器: 装饰器:定义一个为其他函数添加功能的函数 为什么

  • Python装饰器详情

    目录 1.装饰器 1.1 应用场景 2.万能装饰器 3.多层装饰器 4.带参数的装饰器 1.装饰器 装饰器(Decorator):从字面上理解,就是装饰对象的器件.可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出. 装饰器的特点是特点是函数是作为其参数出现的,装饰器还拥有闭包的特点. 示例代码如下所示: # 定义一个装饰器 def decorate(func): def wrapper(): func() print("已将学生加入学校学生名单")

  • Python 函数装饰器详解

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

  • Python的装饰器详情介绍

    目录 1.定义及使用 2.@classmethod 1.定义及使用 例1:装饰器定义: def 装饰器函数(外部函数):            def 内联函数(*args,**kwargs):                ...前置装饰...                外部函数(*args,**kwargs)                ...后置装饰...            return 内联函数  例2:装饰器两种调用方式 第一种:装饰器函数(外部函数)(参数1,参数2....

  • python 装饰器的使用示例

    无参修饰 ,无参数时不需要调用 def log1(func): func() @log1 def test(): print('test:') 有参修饰 def log2(func): def inner(*args, **kwargs): func(*args, **kwargs) return inner @log2 def test(num): print('testlog2:',num,test.__name__) test(20) #相当于log(test(20)) @wraps可以保

  • 举例讲解Python装饰器

    在Python里面,函数可以作为参数传入一个函数,函数也可以复制给变量,通过变量调用函数.装饰器可以扩展一个函数的功能,为函数做一个装饰器注解,可以把装饰器里面定义的功能于所有函数提前执行,提升代码的复用程度. 现在有这么个场景. 打卡 互联网公司里面有各种员工,程序员,前台...,程序员在打开电脑前,需要打卡,前台要早点来开门(我也不清楚,谁开门,这里假定,前台开门),前台开门前也需要打卡.也就是说,打卡是所有员工的最先的公共动作,那么可以把打卡这个功能抽出来作为公共逻辑. 普通函数调用方法

  • Python学习笔记之函数的定义和作用域实例详解

    本文实例讲述了Python函数的定义和作用域.分享给大家供大家参考,具体如下: 定义函数 默认参数: 可以向函数中添加默认参数,以便为在函数调用中未指定的参数提供默认值 # 如果调用 cylinder_volume 函数时,不提供radius参数,那么radius的值为5 def cylinder_volume(height, radius=5): pi = 3.14159 return height * pi * radius ** 2 向函数中的参数传值的方法:按照位置和按照名称 cylin

  • Python学习之装饰器与类的装饰器详解

    目录 装饰器 装饰器的定义 装饰器的用法 类中的装饰器 类的装饰器-classmethod 类的装饰器-staticmethod 类的装饰器-property 通过学习装饰器可以让我们更好更灵活的使用函数,通过学会使用装饰器还可以让我们的代码更加优雅. 在我们的实际工作中,很多场景都会用到装饰器,比如记录一些日志.或者屏蔽一些不太合法的程序执行从而使我们的代码更加安全. 装饰器 什么是装饰器?虽然对这个次感到陌生,但是完全不需要担心. 首先,装饰器也是一种函数:只不过装饰器可以接收 函数 作为参

  • 对python:threading.Thread类的使用方法详解

    Python Thread类表示在单独的控制线程中运行的活动.有两种方法可以指定这种活动: 1.给构造函数传递回调对象 mthread=threading.Thread(target=xxxx,args=(xxxx)) mthread.start() 2.在子类中重写run() 方法 这里举个小例子: import threading, time class MyThread(threading.Thread): def __init__(self): threading.Thread.__in

  • python编程普通及类和静态方法示例详解

    目录 前言 运行环境 普通方法 类方法 静态方法 前言 本文主要讲述了python类中的三类常用方法,普通方法.类方法和静态方法.  本文主要参考了https://youtu.be/rq8cL2XMM5M,强烈推荐一看这个系列的所有视频. 运行环境 import sys sys.version 结果为 '3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]' 普通方

  • python 工具类之Queue组件详解用法

    目录 简述 环境 单向队列 先进后出队列 优先级队列 双向队列 完整代码 总结 简述 队列一直都是工程化开发中经常使用的数据类型,本篇文章主要介绍一下python queue的使用,会边调试代码,边说明方法内容. 环境 python: 3.6.13 单向队列 初始化单向队列 放置一些数据 可以使用full()方法判断队列是否已经塞满数据,可以通过qsize()方法查看队列内元素数量. 这时候我们从队列取出数据,看先取到的是什么. 现在队列里面只有两个数,我们再塞入3个数看一下. 这个时候我们继续

  • python 中Mixin混入类的使用方法详解

    目录 前言 Mixin 与继承的区别 总结 前言 最近在看sanic的源码,发现有很多Mixin的类,大概长成这个样子 class BaseSanic(    RouteMixin,    MiddlewareMixin,    ListenerMixin,    ExceptionMixin,    SignalMixin,    metaclass=SanicMeta, ): 于是对于这种 Mixin 研究了一下,其实也没什么新的东西,Mixin 又称混入,只是一种编程思想的体现,但是在使用

  • Python深度学习之Keras模型转换成ONNX模型流程详解

    目录 从Keras转换成PB模型 从PB模型转换成ONNX模型 改变现有的ONNX模型精度 部署ONNX 模型 总结 从Keras转换成PB模型 请注意,如果直接使用Keras2ONNX进行模型转换大概率会出现报错,这里笔者曾经进行过不同的尝试,最后都失败了. 所以笔者的推荐的情况是:首先将Keras模型转换为TensorFlow PB模型. 那么通过tf.keras.models.load_model()这个函数将模型进行加载,前提是你有一个基于h5格式或者hdf5格式的模型文件,最后再通过改

  • 基于Python的接口自动化unittest测试框架和ddt数据驱动详解

    引言 在编写接口自动化用例时,我们一般针对一个接口建立一个.py文件,一条接口测试用例封装为一个函数(方法),但是在批量执行的过程中,如果其中一条出错,后面的用例就无法执行,还有在运行大量的接口测试用例时测试数据如何管理和加载.针对测试用例加载以及执行控制,python语言提供了unittest单元测试框架,将测试用例编写在unittest框架下,使用该框架可以单个或者批量加载互不影响的用例执行及更灵活的执行控制,对于更好的进行测试数据的管理和加载,这里我们引入数据驱动的模块:ddt,测试数据和

  • Python机器学习应用之基于BP神经网络的预测篇详解

    目录 一.Introduction 1 BP神经网络的优点 2 BP神经网络的缺点 二.实现过程 1 Demo 2 基于BP神经网络的乳腺癌分类预测 三.Keys 一.Introduction 1 BP神经网络的优点 非线性映射能力:BP神经网络实质上实现了一个从输入到输出的映射功能,数学理论证明三层的神经网络就能够以任意精度逼近任何非线性连续函数.这使得其特别适合于求解内部机制复杂的问题,即BP神经网络具有较强的非线性映射能力. 自学习和自适应能力:BP神经网络在训练时,能够通过学习自动提取输

  • Python自然语言处理之词干,词形与最大匹配算法代码详解

    本文主要对词干提取及词形还原以及最大匹配算法进行了介绍和代码示例,Python实现,下面我们一起看看具体内容. 自然语言处理中一个很重要的操作就是所谓的stemming和lemmatization,二者非常类似.它们是词形规范化的两类重要方式,都能够达到有效归并词形的目的,二者既有联系也有区别. 1.词干提取(stemming) 定义:Stemmingistheprocessforreducinginflected(orsometimesderived)wordstotheirstem,base

随机推荐