博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python魔术方法之装饰器
阅读量:5866 次
发布时间:2019-06-19

本文共 7955 字,大约阅读时间需要 26 分钟。

三个魔术方法:

__get__()

__set__()

__delete__()

object.__get__(self,实例名,owner)    #owner = 属主 ,instance = 属主类owner的实例

object.__set__(self,实例名,value)

object.__delete__(self,实例名)

更改属性的行为,当属性等于实例的时候,则可以进行操作

例:

class A:    def __init__(self):        self.a1 = 'a1'class B:    x = A()    def __init__(self):        pass

这样是可以执行的,首先定义好了A

通过定义B的x属性,调用A()

相当于在B类中执行:

print(A().a1)x = A()print(x.a1)

这两个是等价的

标记执行顺序

class A:    def __init__(self):        print('init')        self.a1 = 'a1'class B:    x = A()    def __init__(self):        print('B init')print(B.x.a1)inita1class A:    def __init__(self):        print('init')        self.a1 = 'a1'class B:    x = A()    def __init__(self):        print('B init')        self.x = 100b = B()print(B.x.a1)initB inita1

涉及到字典的执行顺序,所以,print(b.x.a1)是不行的

print(b.x.a1)AttributeError: 'int' object has no attribute 'a1'

引入描述器

_

__get__(self,instance,owner)class A:    def __init__(self):        print('A init')        self.a1 = 'a1'    def __get__(self, instance, owner):        print(self,instance,owner)class B:    x = A()    #A() 就是一个描述器,当对B()或B的实例的x的属性进行访问,则成为A()的实例的方式,则调用__get__方法    def __int__(self):        print('B init')        self.x = 100print(B.x.a1)

发现报错提示如下:

    print(B.x.a1)

AttributeError: 'NoneType' object has no attribute 'a1'

提示 None类型是不能调用的,当通过一个属性访问,如果属性是另一个类的实例,而恰好这个类又实现了描述器的方法之一

    当访问描述器的时候,如果是get触发则返回当前实例以及描述器属主的类型信息

所以,return返回为None的实例,则不能被调用

打印B.x 的类型,可看到为None

print(B.x)def __get__(self, instance, owner):    print(self,instance,owner)<__main__.A object at 0x0000000000B88390> None 
None

对B实例化后打印查看

print('B.x : ',B.x)print()b = B()print('b.x.a1 : ',b.x.a1)

返回如下:

A initTraceback (most recent call last):<__main__.A object at 0x0000000000DB80B8> None 
B.x :   None    print('b.x.a1 :  ',b.x.a1)<__main__.A object at 0x0000000000DB80B8> <__main__.B object at 0x0000000000DB83C8> 
AttributeError: 'NoneType' object has no attribute 'a1'

发现依旧被拦截,所调用的是一个None类型

归根结底,都是与类属性有关系

b = B()print(B.x)

返回如下

A init<__main__.A object at 0x0000000000718390> None 
None

对照get定义的方法:

def __get__(self, instance, owner):    print(self,instance,owner)

执行效果如下:

A init<__main__.A object at 0x0000000000718390> None 
None

原来的实例返回是None,通过get方法变为了类的属性

b = B()print(B.x)print('-' * 90)print(b.x.a1)

凡是进入描述器的三个方法之一,都是会被拦截进行操作

返回如下所示:

A init<__main__.A object at 0x0000000000858390> None 
None------------------------------------------------------------------------------------------分别返回了 self, instance, owner<__main__.A object at 0x0000000000858390> <__main__.B object at 0x0000000000836320> 

当一个类的类属性等于另一个类的实例的时候,则实现了描述器方法,则是描述器的类

如果是类属性上访问的话,直接触发拦截

如果是实例属性访问,则不会访问描述器方法触发

解决返回值问题:return self

class A:    def __init__(self):        print('A init')    def __get__(self,instance,owner):        print('A get',self,instance,owner)        return selfclass B:    x = A()    def __init__(self):        print('B.init')# print(B.x)b = B()print(B.x)

返回如下:

A initB.initA get <__main__.A object at 0x0000000000DA6518> None 
<__main__.A object at 0x0000000000DA6518>

如果只是获取当前属性的手段,通过属性的描述器可以操作属主

这样可以解决不能访问的弊端

在遇到get中应该return一个有意义的值,至于return什么值合适,需要后期定义,具体就是可以获取属主的类及属性

如果仅实现了__get__,就是非数据描述符

同时实现了__set__ + __get__ 就是数据描述符

对日常来讲重要的是get和set同时出现

如果不是访问类的属性的话,则不会触发任何效果,只能是实例才会被拦截

__set__ 方法

class A:    def __init__(self):        print('A init')        self.a1 = 'a1'    def __get__(self,instance,owner):        print('!!!B get',self,instance,owner)        return self    def __set__(self,instance,value):    # #加入set之后,这里原本是为实例设置,但是触发了set        print('~~A.set',self,instance,value)class B:    x = A()    def __init__(self):        self.x = 100b = B()print(b.x)

可以看到,首先被__set__方法先拦截

A init~~A.set <__main__.A object at 0x0000000000DD45C0> <__main__.B object at 0x0000000000DB7320> 100!!!B get <__main__.A object at 0x0000000000DD45C0> <__main__.B object at 0x0000000000DB7320> 
<__main__.A object at 0x0000000000DD45C0>

对b.x进行跟进

class A:    def __init__(self):        print('A init')        self.a1 = 'a1'    def __get__(self,instance,owner):        print('!!!B get',self,instance,owner)        return self    def __set__(self,instance,value):        print('~~A.set',self,instance,value)class B:    x = A()    def __init__(self):        self.x = 10

对每个函数进行标记并跟进:

b = B()A init~~A.set <__main__.A object at 0x0000000000A945C0> <__main__.B object at 0x0000000000A77320> 100

当访问x属性,直接在A()中被__get__拦截

print(b.x)!!!B get <__main__.A object at 0x0000000000DA45C0> <__main__.B object at 0x0000000000D87320> 

查看类型字典

print(b.__dict__){}print(B.__dict__){'x': <__main__.A object at 0x0000000000D77588>, '__weakref__': 
, '__doc__': None, '__module__': '__main__', '__init__': 
, '__dict__': 
}

看到没有dict内容

照常来讲会修改dict,但是触发了set描述器,也就self.x = 这条语句没有被加入到dict

总结:

set如果对实例化中的属性定义,则对属性做修改

说到底就是如果实例的字典里没有,则去类的dict中去查找,set是对类的dict进行修改

通过这样的方式绕开了字典搜索

官方解释:有set,实例的优先级最高,如果没有set则类的优先级比较高

总结:

get:

class A:    def __init__(self,value='abc'):        self.a1 = value    def __get__(self,instance,owner):        return selfclass B:    x = A()    def __init__(self):        self.x = A(123)print(B.x)print(B.x.a1)<__main__.A object at 0x0000000000DB84A8>abcprint(b.x.a1)123 print(B.x.a1)~~~~A__get__ <__main__.A object at 0x0000000000DA84A8> None 
abc

print(b.__dict__),发现实例的dict中存在x方法

{'x': <__main__.A object at 0x00000000006F7940>}print(B.__dict__){'__init__': 
, '__weakref__': 
, 'x': <__main__.A object at 0x00000000006F70F0>, '__dict__': 
, '__doc__': None, '__module__': '__main__'}set:

只要设置相关的属性,实例方法添加不上dict,而set优先级别高,可以看到都是针对A的对象

print(b.x.a1)  被set先拦截

!!!!A__set__ <__main__.A object at 0x0000000000746550> <__main__.B object at 0x0000000000727278> <__main__.A object at 0x00000000007272B0>~~~~A__get__ <__main__.A object at 0x0000000000746550> <__main__.B object at 0x0000000000727278> 
abcprint(B.x.a1)!!!!A__set__ <__main__.A object at 0x0000000000726550> <__main__.B object at 0x0000000000707278> <__main__.A object at 0x00000000007072B0>~~~~A__get__ <__main__.A object at 0x0000000000726550> None 
abcprint(b.__dict__),发现实例的dict中不存在方法{}print(B.__dict__){'x': <__main__.A object at 0x0000000000DB7518>, '__module__': '__main__', '__init__': 
, '__weakref__': 
, '__doc__': None, '__dict__': 
}

一句话总结:一旦使用set,只能操作类属性

下面例子中,虽然会触发set,但是什么都没有操作

b = B()b.xxx = 777!!!!A__set__ <__main__.A object at 0x0000000000BE6550> <__main__.B object at 0x0000000000BC7278> 777{'xxxx': 777}

再访问的时候,再将实例返回回来,get就进行操作了

本质

主要看字典,一点点看到底修改了哪些,通过实例的方式无法修改属性

主要的特点是把实例从__dict__中去掉了,造成了该属性如果是数据描述则优先访问的假象

说到底,属性访问顺序就从来没有变过

一句话总结:非数据描述器可以覆盖,数据描述器直接修改类

在py中,所有的方法都是数据描述器

实现一个static装饰器

静态方法的本质

全局函数放到类中,使用时候,通过我们的类对象进行使用

class A:    @staticmethod    def bar():        return 1    def test(self):        return 2f = A()print(f.test)print(f.bar)

查看结果

<__main__.A object at 0x0000000000D86278>>

静态方法是作为一个function传递进来的

首先我们搞明白需求 如何调用的 A.foo 这么调用 

基础框架

class StaticMethod:    def __init__(self,fn):        self.fn = fn    def __get__(self,instance,owner):        print(self,instance,owner)class A:    @StaticMethod    def foo():        print('static')print(A.__dict__)

调用返回None,因为没有A的实例

a = A.fooprint(a)None

相当于在定义foo的时候被传递给StaticMethod(foo)

当前的foo相当于一个实例对象

返回的东西加了括号才可以调用,所以必须返回self

class Static_Method:    def __init__(self,fn):        print('fn:',fn)        self.fn = fn    def __get__(self,instance,owner):        print(self,instance,owner)        return self.fnclass A:    @Static_Method    def foo():        print('static')f = A.fooprint('f:',f)

这个foo原封不动的返回,打印他们的内存地址查看

fn: 
<__main__.Static_Method object at 0x0000000000A764E0> None 
f: 

spacer.gif 

等价式:foo = Static_Method(foo)

就是说,调用的时候,必须以func类型传递到Statice_Method中

class A:# @Static_Metdef foo():    print('static')print(A.foo)

返回为:

<function A.foo at 0x0000000000E3F9D8>

转载地址:http://edjnx.baihongyu.com/

你可能感兴趣的文章
SVG path
查看>>
js判断checkbox是否选中
查看>>
多系统盘挂载
查看>>
MySQL函数怎么加锁_MYSQL 函数调用导致自动生成共享锁问题
查看>>
MR1和MR2的工作原理
查看>>
Eclipse中修改代码格式
查看>>
GRUB Legacy
查看>>
关于 error: LINK1123: failure during conversion to COFF: file invalid or corrupt 错误的解决方案...
查看>>
hexo博客解决不蒜子统计无法显示问题
查看>>
python实现链表
查看>>
java查找string1和string2是不是含有相同的字母种类和数量(string1是否是string2的重新组合)...
查看>>
Android TabActivity使用方法
查看>>
java ShutdownHook介绍与使用
查看>>
Eclipse的 window-->preferences里面没有Android选项
查看>>
《麦田里的守望者》--[美]杰罗姆·大卫·塞林格
查看>>
遇到的那些坑
查看>>
央行下属的上海资信网络金融征信系统(NFCS)签约机构数量突破800家
查看>>
[转] Lazy evaluation
查看>>
常用查找算法总结
查看>>
grep 零宽断言
查看>>