广告
返回顶部
首页 > 资讯 > 后端开发 > Python >python魔术方法之装饰器
  • 432
分享到

python魔术方法之装饰器

魔术方法python 2023-01-31 07:01:46 432人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

三个魔术方法:__get__()__set__()__delete__()object.__get__(self,实例名,owner)    #owner = 属主 ,instance = 属主类owner的实例object.__set__

三个魔术方法:

__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)
init
a1
class A:
    def __init__(self):
        print('init')
        self.a1 = 'a1'
class B:
    x = A()
    def __init__(self):
        print('B init')
        self.x = 100
b = B()
print(B.x.a1)
init
B init
a1


涉及到字典的执行顺序,所以,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 = 100
print(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 <class '__main__.B'>
None


对B实例化后打印查看

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


返回如下:

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


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

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

b = B()
print(B.x)


返回如下

A init
<__main__.A object at 0x0000000000718390> None <class '__main__.B'>
None


对照get定义的方法:

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


执行效果如下:

A init
<__main__.A object at 0x0000000000718390> None <class '__main__.B'>
None


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

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

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

返回如下所示:

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

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

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

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

解决返回值问题:return self

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

返回如下:

A init
B.init
A get <__main__.A object at 0x0000000000DA6518> None <class '__main__.B'>
<__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 = 100
b = 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> <class '__main__.B'>
<__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> <class '__main__.B'

查看类型字典

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

看到没有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 self
class B:
    x = A()
    def __init__(self):
        self.x = A(123)
print(B.x)
print(B.x.a1)
<__main__.A object at 0x0000000000DB84A8>
abc
print(b.x.a1)
123
 
print(B.x.a1)
~~~~A__get__ <__main__.A object at 0x0000000000DA84A8> None <class '__main__.B'>
abc


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

{'x': <__main__.A object at 0x00000000006F7940>}
print(B.__dict__)
{'__init__': <function B.__init__ at 0x0000000000E2AA60>, '__weakref__': <attribute '__weakref__' of 'B' objects>, 'x': <__main__.A object at 0x00000000006F70F0>, '__dict__': <attribute '__dict__' of 'B' objects>, '__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> <class '__main__.B'>
abc
print(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 <class '__main__.B'>
abc
print(b.__dict__),发现实例的dict中不存在方法
{}
print(B.__dict__)
{'x': <__main__.A object at 0x0000000000DB7518>, '__module__': '__main__', '__init__': <function B.__init__ at 0x0000000000E1BAE8>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None, '__dict__': <attribute '__dict__' of 'B' objects>}


一句话总结:一旦使用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 2
f = A()
print(f.test)
print(f.bar)


查看结果

<bound method A.test of <__main__.A object at 0x0000000000D86278>>
<function A.bar at 0x0000000000DF11E0>


静态方法是作为一个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.foo
print(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.fn
class A:
    @Static_Method
    def foo():
        print('static')
f = A.foo
print('f:',f)


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

fn: <function A.foo at 0x0000000000DEAA60>
<__main__.Static_Method object at 0x0000000000A764E0> None <class '__main__.A'>
f: <function A.foo at 0x0000000000DEAA60>


 

等价式:foo = Static_Method(foo)

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

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


返回为:

<function A.foo at 0x0000000000E3F9D8>



--结束END--

本文标题: python魔术方法之装饰器

本文链接: https://www.lsjlt.com/news/190980.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • python魔术方法之装饰器
    三个魔术方法:__get__()__set__()__delete__()object.__get__(self,实例名,owner)    #owner = 属主 ,instance = 属主类owner的实例object.__set__...
    99+
    2023-01-31
    魔术 方法 python
  • python进阶之魔术方法详解
    目录一、三个内置函数二、双下划线开头和结尾的方法,叫魔术方法。总结一、三个内置函数 1、@classmethod–类名.属性名 2、@staticmethod&ndash...
    99+
    2022-11-12
  • Python 语法之装饰器
      装饰器的概念  装饰器是 Python 的一个重要部分。简单地说:就是用于拓展原来函数功能的一种函数,目的是在不改变原函数名(或类名)的情况下,给函数增加新的功能。  这个函数的特殊之处在于它的返回值也是一个函数,这个函数是内嵌 “原”...
    99+
    2023-06-02
  • php 之魔术方法详解
    ✨ 目录 🎈 构造方法 / __construct🎈 析构方法 / __destruct🎈 克隆方法 / __cloneἸ...
    99+
    2023-08-31
    php 开发语言 魔术方法 面向对象 经验分享
  • python魔法方法之__setattr__()
    目录前言:1、实例属性管理__dict__2、__setattr__()与__dict__3、重载__setattr__()必须谨慎总结:前言: python提供了诸多的魔法方法,其...
    99+
    2022-11-13
  • python类之特殊属性和魔术方法
    1 总述 属性 含义 _name_ 类,函数,方法等的名字 _module_ 类定义所现在的模块名 _class_ 对象或类所属的类 _bases_ 类的基类的元素,顺序为他们在基类列表中出现的顺序 _doc...
    99+
    2023-01-31
    魔术 属性 方法
  • Python之装饰器
    在Python中一切皆对象,函数是一等对象。这意味着可以通过名字引用函数。>>> a=123 >>> a 123 >>> name='zeng' >>> name 'z...
    99+
    2023-01-31
    Python
  • Python学习【魔术方法】
    魔术方法 Python中,所有以双下划线“__”包围的方法(即定义在类中的函数)为魔术方法Magic Method。 构造和初始化 在使用classname()创造实例化对象时,会依次执行__new__和__init__两个方法。 __...
    99+
    2023-01-31
    魔术 方法 Python
  • Python学习之魔法方法
      Python中会看到前后都加双下划线的函数名,例如 __init__(self),这类写法在Python中具有特殊的含义。如果对象使用了这类方法中的某一个,那么这个方法将会在特殊的情况下被执行,然而几乎不会直接调用此类方法。 如果没...
    99+
    2023-01-30
    方法 魔法 Python
  • day20-python之装饰器
    1.装饰器 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 import time 4 def cal(l): 5 start_time=time.time() ...
    99+
    2023-01-31
    python
  • Python黑魔法@property装饰器的使用技巧解析
    @property有什么用呢?表面看来,就是将一个方法用属性的方式来访问. 上代码,代码最清晰了. class Circle(object): def __init__(self, rad...
    99+
    2022-06-04
    使用技巧 黑魔法 Python
  • python中魔术方法简述
    魔术方法:***实例化:new :实例化一个对象 方法很少使用,一般使用return super().))new(cls)基类ibject方法来创建实例并返回。 hash:返回一个整数,如果定义这个方法该类的实例就可hash。eq:...
    99+
    2023-01-31
    魔术 方法 python
  • python中类的魔术方法
    目的:学习python中class的magic methods,提高编程效率。环境:ubuntu 16.4   python 3.5.2在学习class时一定会接触到它的magic methods,比如常用__init__,形式都是前后有双...
    99+
    2023-01-31
    魔术 方法 python
  • python魔术方法是什么
    这篇文章主要介绍“python魔术方法是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“python魔术方法是什么”文章能帮助大家解决问题。一、三个内置函数@classmethod&nda...
    99+
    2023-06-26
  • python魔术方法有哪些
    python魔术方法有哪些?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、类和对象通俗理解:类就是模板,对象就是通过模板创造出来的物体类(Class)由3个部...
    99+
    2023-06-15
  • Python语法详解之decorator装饰器
    python 是一门优雅的语言,有些使用方法就像魔法一样。装饰器(decorator)就是一种化腐朽性为神奇的技巧。最近一直都在使用 Tornado 框架,一直还是念念不忘 Flas...
    99+
    2022-11-12
  • python面向对象基础之常用魔术方法
    目录一、类和对象二、魔法方法三、理解self四、练习对战一、类和对象 通俗理解:类就是模板,对象就是通过模板创造出来的物体 类(Class)由3个部分构成: 类的名称: 类名 类的...
    99+
    2022-11-12
  • python进阶之装饰器
    一.无参装饰器 问题:如何计算一段程序的运行时间? 先看一段简单代码: 1 import time 2 def func(): 3 start = time.time() # 记录程序开始时间 4 time.sleep(...
    99+
    2023-01-30
    进阶 python
  • python之装饰器(函数)
    1. 装饰器   遵循的原则:     开闭原则:   对功能的扩展开放  对代码的修改是封闭 # 通用装饰器写法 # 存在的意义: 在不破坏原有函数和原有函数调用的基础上,给函数添加新的功能. def wrapper...
    99+
    2023-01-30
    函数 python
  • python之yield与装饰器
    防伪码:忘情公子著python中的yield:  在之前发布的《python之列表解析与生成器》中我们有提到过,生成器所实现的是跟列表解析近似的效果,但是我们不能对生成器做一些属于列表解析的操作。  因为生成器本身就不是一个列表,它只是模拟...
    99+
    2023-01-31
    python yield
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作