Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Python——编写类装饰器

Python——编写类装饰器

作者头像
py3study
发布于 2020-01-06 06:31:11
发布于 2020-01-06 06:31:11
74600
代码可运行
举报
文章被收录于专栏:python3python3
运行总次数:0
代码可运行

编写类装饰器

类装饰器类似于函数装饰器的概念,但它应用于类,它们可以用于管理类自身,或者用来拦截实例创建调用以管理实例。

-------------------------------------------------------------------------------------------------------------------------------------

单体类 由于类装饰器可以拦截实例创建调用,所以它们可以用来管理一个类的所有实例,或者扩展这些实例的接口。 下面的类装饰器实现了传统的单体编码模式,即最多只有一个类的一个实例存在。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
instances = {} # 全局变量,管理实例
def getInstance(aClass, *args):
    if aClass not in instances:
        instances[aClass] = aClass(*args)
    return instances[aClass]     #每一个类只能存在一个实例

def singleton(aClass):
    def onCall(*args):
        return getInstance(aClass,*args)
    return onCall

为了使用它,装饰用来强化单体模型的类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@singleton        # Person = singleton(Person)
class Person:
    def __init__(self,name,hours,rate):
        self.name = name
        self.hours = hours
        self.rate = rate
    def pay(self):
        return self.hours * self.rate

@singleton        # Spam = singleton(Spam)
class Spam:
    def __init__(self,val):
        self.attr = val
        
bob = Person('Bob',40,10)
print(bob.name,bob.pay())

sue = Person('Sue',50,20)
print(sue.name,sue.pay())

X = Spam(42)
Y = Spam(99)
print(X.attr,Y.attr)

现在,当Person或Spam类稍后用来创建一个实例的时候,装饰器提供的包装逻辑层把实例构建调用指向了onCall,它反过来调用getInstance,以针对每个类管理并分享一个单个实例,而不管进行了多少次构建调用。 程序输出如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Bob 400
Bob 400
42 42

在这里,我们使用全局的字典instances来保存实例,还有一个更好的解决方案就是使用Python3中的nonlocal关键字,它可以为每个类提供一个封闭的作用域,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def singleton(aClass):
	instance = None
	def onCall(*args):
		nonlocal instance
		if instance == None:
			instance = aClass(*args)
		return instance
	return onCall

当然,我们也可以用类来编写这个装饰器——如下代码对每个类使用一个实例,而不是使用一个封闭作用域或全局表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class singleton:
	def __init__(self,aClass):
		self.aClass = aClass
		self.instance = None
	def __call__(self,*args):
		if self.instance == None:
			self.instance = self.aClass(*args)
		return self.instance

-------------------------------------------------------------------------------------------------------------------------------------

跟踪对象接口 类装饰器的另一个常用场景是每个产生实例的接口。类装饰器基本上可以在实例上安装一个包装器逻辑层,来以某种方式管理其对接口的访问。 前面,我们知道可以用__getattr__运算符重载方法作为包装嵌入到实例的整个对象接口的方法,以便实现委托编码模式。__getattr__用于拦截未定义的属性名的访问。如下例子所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Wrapper:
	def __init__(self,obj):
		self.wrapped = obj
	def __getattr__(self,attrname):
		print('Trace:',attrname)
		return getattr(self.wrapped,attrname)

	
>>> x = Wrapper([1,2,3])
>>> x.append(4)
Trace: append
>>> x.wrapped
[1, 2, 3, 4]
>>>
>>> x = Wrapper({'a':1,'b':2})
>>> list(x.keys())
Trace: keys
['b', 'a']

在这段代码中,Wrapper类拦截了对任何包装对象的属性的访问,打印出一条跟踪信息,并且使用内置函数getattr来终止对包装对象的请求。 类装饰器为编写这种__getattr__技术来包装一个完整接口提供了一个替代的、方便的方法。如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def Tracer(aClass):
    class Wrapper:
        def __init__(self,*args,**kargs):
            self.fetches = 0
            self.wrapped = aClass(*args,**kargs)
        def __getattr__(self,attrname):
            print('Trace:'+attrname)
            self.fetches += 1
            return getattr(self.wrapped,attrname)
    return Wrapper

@Tracer
class Spam:
    def display(self):
        print('Spam!'*8)

@Tracer
class Person:
    def __init__(self,name,hours,rate):
        self.name = name
        self.hours = hours
        self.rate = rate
    def pay(self):
        return self.hours * self.rate

food = Spam()
food.display()
print([food.fetches])

bob = Person('Bob',40,50)
print(bob.name)
print(bob.pay())

print('')
sue = Person('Sue',rate=100,hours = 60)
print(sue.name)
print(sue.pay())

print(bob.name)
print(bob.pay())
print([bob.fetches,sue.fetches])

通过拦截实例创建调用,这里的类装饰器允许我们跟踪整个对象接口,例如,对其任何属性的访问。 Spam和Person类的实例上的属性获取都会调用Wrapper类中的__getattr__逻辑,由于food和bob确实都是Wrapper的实例,得益于装饰器的实例创建调用重定向,输出如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Trace:display
Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!
[1]
Trace:name
Bob
Trace:pay
2000

Trace:name
Sue
Trace:pay
6000
Trace:name
Bob
Trace:pay
2000
[4, 2]

========================================================================================

示例:实现私有属性 如下的类装饰器实现了一个用于类实例属性的Private声明,也就是说,属性存储在一个实例上,或者从其一个类继承而来。不接受从装饰的类的外部对这样的属性的获取和修改访问,但是,仍然允许类自身在其方法中自由地访问那些名称。类似于Java中的private属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
traceMe = False
def trace(*args):
    if traceMe:
        print('['+ ' '.join(map(str,args))+ ']')

def Private(*privates):
    def onDecorator(aClass):
        class onInstance:
            def __init__(self,*args,**kargs):
                self.wrapped = aClass(*args,**kargs)
            def __getattr__(self,attr):
                trace('get:',attr)
                if attr in privates:
                    raise TypeError('private attribute fetch:'+attr)
                else:
                    return getattr(self.wrapped,attr)
            def __setattr__(self,attr,value):
                trace('set:',attr,value)
                if attr == 'wrapped': # 这里捕捉对wrapped的赋值
                    self.__dict__[attr] = value
                elif attr in privates:
                    raise TypeError('private attribute change:'+attr)
                else: # 这里捕捉对wrapped.attr的赋值
                    setattr(self.wrapped,attr,value)
        return onInstance
    return onDecorator

if __name__ == '__main__':
    traceMe = True

    @Private('data','size')
    class Doubler:
        def __init__(self,label,start):
            self.label = label
            self.data = start
        def size(self):
            return len(self.data)
        def double(self):
            for i in range(self.size()):
                self.data[i] = self.data[i] * 2
        def display(self):
            print('%s => %s'%(self.label,self.data))

    X = Doubler('X is',[1,2,3])
    Y = Doubler('Y is',[-10,-20,-30])

    print(X.label)
    X.display()
    X.double()
    X.display()

    print(Y.label)
    Y.display()
    Y.double()
    Y.label = 'Spam'
    Y.display()

    # 这些访问都会引发异常
    """
    print(X.size())
    print(X.data)

    X.data = [1,1,1]
    X.size = lambda S:0
    print(Y.data)
    print(Y.size())

这个示例运用了装饰器参数等语法,稍微有些复杂,运行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[set: wrapped <__main__.Doubler object at 0x03421F10>]
[set: wrapped <__main__.Doubler object at 0x031B7470>]
[get: label]
X is
[get: display]
X is => [1, 2, 3]
[get: double]
[get: display]
X is => [2, 4, 6]
[get: label]
Y is
[get: display]
Y is => [-10, -20, -30]
[get: double]
[set: label Spam]
[get: display]
Spam => [-20, -40, -60]
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/09/25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Python 3 之 装饰器详解
------------ 装饰器 -----------------------------------------------------
py3study
2020/01/03
1.3K0
Python 3 之 装饰器详解
Python学习笔记整理(十六) 类的设计
如何使用类来对有用的对象进行建模? 一、Python和OOP Python和OOP实现可以概括为三个概念。 继承     继承是基于Python中属性查找(在X.name表达式中) 多态     在X.method方法中,method的意义取决于X的类型(类) 封装     方法和运算符实现行为,数据隐藏默认是一种惯例。 封装指的是在Python中打包,也就是把实现的细节隐藏在对象接口之后。这并不代表有强制的私有性。封装可以让对象接口的现实 出现变动时,不影响这个对象的用户。 1、不要通过调用标记进行重载 不要在同一个类中对同一个方法名定义两次,后面的会覆盖前面,也不要对对象类型进行测试。应该把程序代码写成预期的对象接口。而不是特定类型的数据类型。 2、类作为记录 通过类的实例来创建多个记录。 3、类和继承:是“一个”关系 (is a) 从程序员的角度来看,继承是由属性点号运算启动的,由此触发实例,类以及任何超类中变量名搜索。 从设计师的角度看,继承是一种定义集合成员关系的方式:类定义了一组内容属性,可由更具体的集合(子类)继承和定制。 子类和超类的继承是1对1的关系. PizzaRobot是一种Chef,Chef是一种Employee.以OOP术语来看,我们称这些关系为“是一个连接”(is a):机器人是个主厨,主厨是一个员工。 class Employee:         def __init__(self,name,salary=0):                 self.name=name                 self.salary=salary         def giveRaise(self,percent):                 self.salary=self.salary+(self.salary*percent)         def work(self):                 print self.name,"does stuff"         def __repr__(self):                 return "<Employee:name=%s,salary=%s>" % (self.name,self.salary) class Chef(Employee):         def __init__(self,name):                 Employee.__init__(self,name,5000)         def work(self):                 print self.name,"make food" class Server(Employee):         def __init__(self,name):                 Employee.__init__(self,name,40000)         def work(self):                 print self.name,"interface with customer" class PizzaRobot(Chef):            def __init__(self,name):#有点想不明白,既然继承就够了,为什么还要在这里构造                 Chef.__init__(self,name)    #Chef.__init__(self,name) =》Employee.__init__(self,name,5000)=>__init__(self,name,salary=0)         def work(self):                 print self.name,"make pizza" if __name__=='__main__':         bob=PizzaRobot('bob')         print bob         bob.work()         bob.giveRaise(0.20)         print bob;print # python employees.py   <Employee:name=bob,salary=5000> bob make pizza <Employee:name=bob,salary=6000.0> 理解有问题的地方 class PizzaRobot(Chef):            def __init__(self,name):#有点想不明白,既然继承就够了,为什么还要在这里构造,下面拿掉这里做对比   
py3study
2020/01/09
7550
python装饰器2:类装饰器
"类装饰器"有两种解读方式:用来装饰类的装饰器;类作为装饰器装饰其它东西。你如何认为取决于你,两种说法都有出现在其它的文章中。我的文章中是将"类装饰器"解读为第一种方式,即装饰类的东西。而“类作为装饰器装饰其它东西”,我都会为其标注"类作为装饰器"或"作为装饰器的类"以避免歧义。
py3study
2020/01/19
1.2K0
Python学习笔记整理(十五)类的编写
类代码编写细节 一、class语句 一般形式 class    <name>(superclass,...):     data=value     def mothod(self,...):         self.member=value 在class语句内,任何赋值语句都会产生类属性。 类几乎就是命名空间,也就是定义变量名(属性)的工具,把数据和逻辑导出给客户端。 怎么样从class语句得到命名空间的呢? 过程如下。就像模块文件,位于class语句主体中的语句会建立起属性。当py
py3study
2020/01/08
9920
看代码学习python基础
#-- 寻求帮助: dir(obj) # 简单的列出对象obj所包含的方法名称,返回一个字符串列表 help(obj.func) # 查询obj.func的具体介绍和用法 #-- 测试类型的三种方法,推荐第三种 if type(L) == type([]): print("L is list") if type(L) == list: print(
py3study
2020/01/10
9340
千行代码入门python
# _*_ coding: utf-8 _*_ """类型和运算----类型和运算----类型和运算----类型和运算----类型和运算----类型和运算----类型和运算----类型和运算----类型和运算----类型和运算----类型和运算""" # -- 寻求帮助: dir(obj)  # 简单的列出对象obj所包含的方法名称,返回一个字符串列表 help(obj.func)  # 查询obj.func的具体介绍和用法 # -- 测试类型的三种方法,推荐第三种 if type(L) =
好派笔记
2021/09/16
6151
Python - 装饰器使用过程中的误
大家都知道装饰器是一个很著名的设计模式,经常被用于 AOP (面向切面编程)的场景,较为经典的有插入日志,性能测试,事务处理,Web权限校验, Cache等。
py3study
2020/01/03
3690
Python 3 之 运算符重载详解
实际上,“运算符重载”只是意味着在类方法中拦截内置的操作……当类的实例出现在内置操作中,Python自动调用你的方法,并且你的方法的返回值变成了相应操作的结果。以下是对重载的关键概念的复习:
py3study
2020/01/03
4.4K0
Python深入05 装饰器
装饰器(decorator)是一种高级Python语法。装饰器可以对一个函数、方法或者类进行加工。在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果。相对于其它方式,装饰器语法简单,代码可读性高。因此,装饰器在Python项目中有广泛的应用。 装饰器最早在Python 2.5中出现,它最初被用于加工函数和方法这样的可调用对象(callable object,这样的对象定义有__call__方法)。在Python 2.6以及之后的Python
Vamei
2018/01/18
6440
我是装饰器
其实,我并不难理解,而且学会使用我之后,可以让你写代码时偷点懒,少点重复性工作,代码也更优雅,更具有 Pythonic。
somenzz
2020/11/25
3600
(长文收藏) 如何理解 Python 装饰器?
https://www.biaodianfu.com/python-decorator.html 大家好,欢迎来到 Crossin的编程教室 !
Crossin先生
2021/12/01
6490
(长文收藏) 如何理解 Python 装饰器?
Python 元类(Meta Class):解密 Python 面向对象编程的幕后推手
在 Python 编程中,我们每天都在和类打交道,但是你是否也和我一样想过:类本身是什么?是谁创建了类?元类(Meta Class)就是用来创建类的"类"。今天让我们一起深入理解这个强大而神秘的特性。
Piper破壳
2024/12/13
1490
掌握Python 装饰器,其实只需要一盏茶的功夫
装饰器的语法为 @dec_name ,置于函数定义之前。如: import atexit @atexit.register def goodbye(): print('Goodbye!') print('Script end here') atexit.register 是一个装饰器,它的作用是将被装饰的函数注册为在程序结束时执行。函数 goodbye 是被装饰的函数。 程序的运行结果是: Script end here Goodbye! 可见函数 goodbye 在程序结束后被自动调用。 另一个常
用户2176428
2018/06/27
4640
再再肝3天,整理了70个Python面向对象编程案例
Python 作为一门面向对象编程语言,常用的面向对象知识怎么能不清楚呢,今天就来分享一波
周萝卜
2021/11/24
7480
python wraps那点儿事儿
那么问题来了,在打印__doc__  和 __name__ 的时候看到返回的并非是我们想要的,因为已经被包装到TimeIt中的可调用对象,所以,现在它是一个实例了,实例是不能调用__name__的;所以,我们来手动模拟一下,将其伪装写入__doc__ 和 __name__
py3study
2020/01/08
2740
Python装饰器学习笔记
装饰器是 Python 的一个重要部分。它是修改其他函数的功能的函数,有助于让我们的代码更简短
周希
2019/10/15
6820
万字长文深度解析Python装饰器
Python 中的装饰器是你进入 Python 大门的一道坎,不管你跨不跨过去它都在那里。
马哥linux运维
2019/01/02
4410
万字长文深度解析Python装饰器
Python 中测试函数调用顺序
在 Python 中测试函数调用顺序是一个常见的需求。例如,您可能有一个对象 Obj,其中包含三个方法:method1、method2 和 method3。您还编写了一个函数 do_something,该函数调用这些方法。您想编写一个测试来测试 do_something 函数和 Obj 对象。但是,您不想直接模拟或改变 Obj 对象的行为。您需要一种方法来获取在 obj 对象上调用的方法列表,而无需更改其行为。
华科云商小徐
2024/11/08
2570
Python运算符重载
在Python语言中提供了类似于C++的运算符重在功能: 一下为Python运算符重在调用的方法如下: Method        Overloads        Call for __init__        构造函数        X=Class() __del__        析构函数        对象销毁 __add__        +                X+Y,X+=Y __or__        |                X|Y,X|=Y __repr__        打印转换        print X,repr(X) __str__        打印转换        print X,str(X) __call__        调用函数        X() __getattr_    限制            X.undefine __setattr__    取值            X.any=value __getitem__    索引            X[key],                             For If __len__        长度            len(X) __cmp__        比较            X==Y,X<Y __lt__        小于            X<Y __eq__        等于            X=Y __radd__        Right-Side +        +X __iadd__        +=                X+=Y __iter__        迭代            For In 7.1    减法重载
py3study
2020/01/07
6530
Python 编程 | 连载 17 - 高阶函数与装饰器
__str__ 函数,当print当前实例化对象的时候,会打印出该函数中的return的信息,相当于Java中的 toString 函数,也就是对象的描述信息的定义函数
RiemannHypothesis
2022/09/26
2600
Python 编程 | 连载 17 - 高阶函数与装饰器
相关推荐
Python 3 之 装饰器详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验