Python魔法函数

  • 前言
  • 魔法函数
    • 定义
    • 常用的魔法方法
      • 类构造与初始化
      • 类的表示
      • 控制属性访问
      • 比较、运算等操作
      • 容器类操作
      • 可调用对象
      • 序列化
  • 总结

前言

本篇博客主要介绍Python的魔法函数。在进行深度学习的工作或者python的编程时,或多或少会涉及到Python的类编写,其中会涉及到python的魔法函数,如编写一个数据加载的生成器的时候,可能会涉及到__next____iter__函数,当然生成器可能一个关键字yield就可以搞定了。最后为了加深对Python魔法函数的理解,这篇博客以代码加说明的方式,记录一些常见的Python魔法函数。

魔法函数

定义

魔法方法是Python的内置函数,一般以双下划线开头,每个魔法方法对应的一个内置函数或者运算符,比如当使用len(obj)的时候实际上是调用obj.__len__方法。因此当我们对象使用这些方法的时候,相当于对这个对象的这类方法进行重写或重载。

通过dir()可以查看对象的所有方法和属性,其中双下划綫开头和结尾的就是该对象具有的魔法方法。以整数对象为例:

常用的魔法方法

常用的魔法方法大致可以分为以下几类:

  • 构造与初始化
  • 类的表示
  • 访问控制
  • 比较、运算等操作
  • 容器类操作
  • 可调用对象
  • 序列化

类构造与初始化

对类的初始化一般涉及到三个魔法方法:__init____new____del__
初始化一个类的时候,如class_a = class_A(1),首先调用的是该类的__new__方法,返回该类的实例对象,然后该类的__init__方法,对该对象进行初始化。
__new__方法使用如下:

  1. __new__(cls,*args,**kwargs):至少要有一个参数cls,代表传入的类,此参数在实例化时由 Python 解释器自动提供,若返回该类的对象实例,后面的参数直接传递给__init__
class A:def __init__(self,a,b):print('this is A init')print(self)self.a=aself.b=bdef __new__(cls, *args, **kwargs):print('this is A new')print('args:%s'%args)print('kwargs:%s'%kwargs)print(cls)print(object.__new__(cls))#<__main__.A object at 0x000001BCD98FB3D0>,一个A的对象实例return object.__new__(cls)#创建实例并返回
>>>a=A(1,b=10)
this is A new#先进入__new__
args:1
kwargs:{'b': 10}
<class '__main__.A'>
<__main__.A object at 0x000001BCD98FB3D0>
this is A init#再进入__init__
<__main__.A object at 0x000001D0BC3EB3D0>#self就是__new__返回的对象实例
  1. __new__可以决定是否使用__init__方法,但是,执行了__new__,并不一定会进入__init__,只有__new__返回了,当前类cls的实例,当前类的__init__才会进入。即使返回父类的实例也不行,必须是当前类的实例;
class A:def __init__(self,a,b):print('this is A init')self.a=aself.b=bdef __new__(cls, *args, **kwargs):print('this is A new')print('args:%s'%args)print('kwargs:%s'%kwargs)print(cls)
>>>m=A(1,b=10)
this is A new
args:1
kwargs:{'b': 10}
<class '__main__.A'>
>>>print(m.a)#报错,未进入到当前类的__init__进行初始化
AttributeError: 'NoneType' object has no attribute 'a'
  1. object将__new__()方法定义为静态方法,并且至少需要传递一个参数cls,cls表示需要实例化的类,此参数在实例化时由Python解释器自动提供。
  2. __init__()有一个参数self,该self参数就是__new__()返回的实例

__new__的使用场景如单例模式、工厂模式,以及一些不可变对象的继承上。这类应用非常值得关注并使用,可以大大的让代码看起来优美和简洁;

__del__方法则是当对象被系统回收的时候调用的魔法方法,在对象生命周期调用结束时调用该方法。Python 采用自动引用计数(ARC)方式来回收对象所占用的空间,当程序中有一个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 1;当程序中有两个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 2,依此类推,如果一个对象的引用计数变成了 0,则说明程序中不再有变量引用该对象,表明程序不再需要该对象,因此 Python 就会回收该对象。所以大部分时候,都不需要我们手动去删掉不再使用的对象,python的回收机制会自动帮我们做这件事。

类的表示

类的表示相关的魔法方法主要有__str____repr____bool__

  • __str__主要是在打印对象print(obj)时,会隐式调用str(obj),即调用类中的__str__方法;定了该方法就可以通过str(obj)来调用;
  • __repr__主要式在直接输出对象时的显示,会调用__repr__方法;定义了该方法就可以通过repr(obj)来调用。
  • __bool__:当调用 bool(obj) 时,会调用 __bool__()方法,返回 True 或 False:

当自定义类中没有定义__str__()__repr__()时,在进行对象的输出时,会调用默认的__str__()__repr__();当类中只包含 __str__()时,调用 print()str()函数进行对象的输出,会调用__str__(),直接输出调用默认的 __repr__();当类中既包含 __str__()又包含__repr__()时,调用 print()str()函数进行对象的输出,会调用__str__(),直接输出会调用__repr__();当类中只包含__repr__()时,调用 print() 或str()函数进行对象的输出和直接输出都会调用 __repr__()

因此,对于自定义的类,建议定义__str__和__repr__,以更好的进行交互;其中__str__可以考虑设计为想转换输出的字符串,在后续str(obj)将对象转为特定的字符串输出时提供一些便利;__repr__可以考虑设计为输出较为详细的信息,如列名,甚至包括部分关键参数名,这样在开发的时候便于获取对象的准确信息(如sklearn中的机器学习模块就是如此设计)

控制属性访问

这类魔法方法主要再对对象的属性进行访问、定义、修改时起作用。主要有:

  • __getattr__(self, name): 定义当用户试图获取一个属性时的行为。
  • __getattribute__(self, name):定义当该类的属性被访问时的行为(先调用该方法,查看是否存在该属性,若不存在,接着去调用__getattr__)。
  • __setattr__(self, name, value):定义当一个属性被设置时的行为。
  • __delattr__(self, name):定义当一个属性被删除时的行为。
class A(object):def __init__(self,a,b):self.a=aself.b=bdef __setattr__(self, key, value):print(key,value)print('this is magic method setattr')def __getattr__(self, item):print('getattr:%s'%item)print('this is magic method getattr')def __delattr__(self, item):print('delattr:%s'%item)print('this is magic method delattr')>>>m=A(1,2)
a 1
this is magic method setattr#初始化self.a=a时调用 __setattr__
b 2
this is magic method setattr#初始化self.b=b时调用 __setattr__>>>a=m.a
getattr:a
this is magic method getattr#访问属性a时调用__getattr__
>>>m.b=100
b 100
this is magic method setattr#修改属性b时调用__setattr__
>>>delattr(m,'a')
delattr:a
this is magic method delattr#删除属性a时调用__delattr__
>>>print(m.a)
getattr:a
this is magic method getattr
None#属性a被删除,为None

在上面代码中,重载了__setattr__,因此属性初始化时就调用重载后的__setattr__;但是在初始化属性调用__setattr__时需要配合实例的属性管理__dict__来进行,即需要将属性都在self.__dict__中进行注册,否则实例是访问不到这些属性的。

>>>print(m.a)
getattr:a
this is magic method getattr
None#可以看到,并没有初始化成功为a=1,因为重载的__setattr__方法内部尚未将属性在__dict__中注册#修改上面的__setattr__:
class A(object):def __init__(self,a,b):self.a=aself.b=bdef __setattr__(self, key, value):print(key,value)print('this is magic method setattr')self.__dict__[key] = value#在__dict__注册实例属性#super().__setattr__(key,value) 也可以通过继承的方式来实现;def __getattr__(self, item):print('getattr:%s'%item)print('this is magic method getattr')def f(self):return self.__dict__#查看属性管理字典
>>>m=A(1,2)
>>>m.a
1
>>>m.f()
{'a': 1, 'b': 2}

控制属性重载的使用场景:如在初始化属性时先对属性的值进行拦截,进行相应的处理或者判断(比如类型判断,或者范围判断)

class A(object):def __init__(self,age,sex):self.age=ageself.sex=sexdef __setattr__(self, key, value):if key=='age':if not 0<=value<=100:raise Exception('age must between 0 and 100')elif key=='age':if not (value=='male' or value=='female'):raise Exception('sex must be male of female')else:passsuper().__setattr__(key,value)
>>>m=A(age=102,sex='male')
Exception: age must between 0 and 100
>>>m=A(age=90,sex='hhh')
Exception: sex must be male of female
>>>m=A(age=90,sex='male')
>>>print(m.sex,m.age)
male 90

比较、运算等操作

通过定义各类比较、运算、类型相关的魔法方法,来实现对象之间的各类比较、运算等操作。这类魔法方法非常多,不一一展开。

  1. 用于比较的魔法函数:
  2. 双目运算符或函数
  3. 增量运算
  4. 类型转换

容器类操作

有一些方法可以自定义容器,就像python内置的list,tuple,dict等等;容器分为可变容器和不可变容器,这里的细节需要去了解相关的协议。如果自定义一个不可变容器的话,只能定义__len____getitem__;定义一个可变容器除了不可变容器的所有魔法方法,还需要定义__setitem____delitem__;如果容器可迭代。还需要定义__iter__

__len__(self):返回容器的长度
__getitem__(self,key):当需要执行self[key]的方式去调用容器中的对象,调用的时该方法
__setitem__(self,key,value):当需要执行self[key] = value时,调用的是该方法。
__delitem__(self, key):当需要执行 del self[key]时,需要调用该方法;
__iter__(self):当容器可以执行 for x in container: ,或者使用iter(container)时,需要定义该方法
__reversed__(self):实现当reversed()被调用时的行为。应该返回序列反转后的版本。仅当序列可以是有序的时候实现它,例如对于列表或者元组。
__contains__(self, item):定义了调用in和not in来测试成员是否存在的时候所产生的行为。

class SpecialList(object):def __init__(self,values=None):if values is None:self.values=[]else:self.values=valuesself.count={}.fromkeys(range(len(self.values)),0)def __len__(self):#通过len(obj)访问容器长度return len(self.values)def __getitem__(self, key):#通过obj[key]访问容器内的对象self.count[key]+=1return self.values[key]def __setitem__(self, key, value):#通过obj[key]=value去修改容器内的对象self.values[key]=valuedef __delitem__(self, key):#通过del obj[key]来删除容器内的对象del self.values[key]def __iter__(self):#通过for 循环来遍历容器return iter(self.values)def __next__(self):# 迭代的具体细节# 如果__iter__返回时self 则必须实现此方法if self._index >= len(self.values):raise StopIteration()value = self.values[self._index]self._index += 1return valuedef __reversed__(self):#通过reverse(obj)来反转容器内的对象return SpecialList(reversed(self.values))def __contains__(self, item):#通过 item in obj来判断元素是否在容器内return item in self.valuesdef append(self, value):self.values.append(value)def head(self):# 获取第一个元素return self.values[0]def tail(self):# 获取第一个元素之后的所有元素return self.values[1:]

可调用对象

在Python中,方法也是一种高等的对象。通过对对象实现__call__就可以实现像调用方法一样去调用类。

class A(object):def __init__(self,a,b):self.a=aself.b=bdef __call__(self,a):self.a=a>>>m=A(1,2)
>>>m.a
1
>>>m.b
2
>>>id(m)
1460475565152
>>>m(100)#像函数一样直接调用类,本质是调用的__call__方法
>>>m.a
100
>>>m.b
2
>>>id(m)
1460475565152

应用场景:

  1. 自建一个简单的装饰器(实际例子如 bottle 框架源码的 cached_property)
  2. 可以用作抽象窗口函数,抽象出使用规则,通过子类的改写其他方法,在不改变原代码的情况下取得不同的结果

序列化

在序列化的时候也是调用的内置魔法方法:

  • __getstate__():用于Python 对象的序列化,指定在序列化时将哪些信息记录下来
  • __setstate__():用于Python 对象的反序列化,指定在反序列化时指明如何利用信息
class A(object):def __init__(self,a,b):self.a=aself.b=bdef __getstate__(self):print('this is magic method __getstate__')return {'a':self.a,'b':self.b}#序列化时返回的,即为在反序列化时传入的statedef __setstate__(self, state):print('this is magic method __setstate__')self.a=state['a']self.b=300import pickle
>>>a=A(1,2)
>>>a_1=pickle.dumps(a)#调用__getstate__
>>>print(a_1)
this is magic method __getstate__
b'\x80\x04\x95&\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x01A\x94\x93\x94)\x81\x94}\x94(\x8c\x01a\x94K\x01\x8c\x01b\x94K\x02ub.'
>>>a_2=pickle.loads(a_1)#调用__setstate__
>>>print(a_2)
this is magic method __setstate__
<__main__.A object at 0x000001BF5B086670>
>>>print(a_2.a,a_2.b)
1 300

总结

希望在后续工程化工作中能够有意识有意义去运用python的魔法函数。

python 魔法函数相关推荐

  1. exit函数_全面深入了解 Python 魔法函数

    (点击上方公众号,可快速关注一起学Python) 作者:浪子燕青       链接: http://www.langzi.fun/Python魔法函数.html 魔法函数概念 魔法函数是以双下划线开头 ...

  2. 一文读懂什么是Python魔法函数

    一文读懂什么是Python魔法函数 Python的魔法函数是指Python的类中,一系列函数名由双下划线包裹的函数. 笔者最初接触到魔法函数的使用是在Pytorch中,在Pytorch中的Datase ...

  3. python 魔法函数 运行时_16个python常用魔法函数

    ==,is的使用 ·is是比较两个引用是否指向了同一个对象(引用比较). ·==是比较两个对象是否相等 1.__ init__(): 所有类的超类object,有一个默认包含pass的__ init ...

  4. Python魔法函数

    python中定义的以__开头和结尾的的函数.可以随意定制类的特性.魔法函数定义好之后一般不需要我们自己去调用,而是解释器会自动帮我们调用. __getitem__(self, item) 将类编程一 ...

  5. python 魔法函数 __add___PythonI/O进阶学习笔记_2.魔法函数

    前言: 本文一切观点和测试代码是在python3的基础上. Content: 1.什么是魔法函数,魔法函数__getitem__在python中应用. 2.python的数据模型和数据模型这种设计对p ...

  6. python 魔法函数是什么意思_Python 中的魔法函数

    魔法函数是Python中的特性,学习好魔法函数将有助于我们写出优秀的pythonic(优雅的.地道的.整洁的)代码,同时因为Python语言的特性,我们在进行框架设计的时候除了设计模式等高级技能,魔法 ...

  7. python魔法函数(二)之__getitem__、__len__、__iter__

    魔法函数会增强python类的类型,独立存在 __getitem class Company:def __init__(self, employees):self.employees = employ ...

  8. Python魔法函数与两比特量子系统模拟

    技术背景 本文主要涵盖两个领域的知识点:python的魔法函数和量子计算模拟,我们可以通过一个实际的案例来先审视一下这两个需求是如何被结合起来的. 量子计算模拟背景 ProjectQ是一个非常优雅的开 ...

  9. python 魔法函数 __add___python魔法方法

    python魔法方法 1.__init__: 初始化函数,在创建实例对象为其赋值时使用,在__new__方法之后.__init__必须至少有一个参数__self__,就是这个__new__返回的实例, ...

最新文章

  1. DPDK — DPDK APP 的指令行参数
  2. 4G EPS 的架构模型
  3. php伪造页面url地址,php 伪造HTTP_REFERER页面URL来源的三种方法
  4. C++ Primer 5th笔记(chap 19 特殊工具与技术)定位 new 表达式
  5. centos php编译安装mysql_CentOS手动编译安装Apache、PHP、MySQL
  6. Python:windows程序打包
  7. OpenGL多窗口示例
  8. 用Python分析中国大学分布,终于知道为什么好大学难上了
  9. 三联竟出了这么赞的杂志!各领域佼佼者畅所欲言,为少年建立思维判断体系!
  10. 指令 机器指令 汇编指令 指令系统 汇编指令的基本构成 操作数
  11. java求圆弧切线_两个圆的公切线
  12. 循环神经网络_漫谈循环神经网络:LSTM、GRU
  13. idea 2020 社区版传递参数
  14. c++中两个头文件定义同名类的解决办法
  15. 【踩坑速记】二次依赖?android studio编译运行各种踩坑解决方案,杜绝弯路,总有你想要的~...
  16. 微信朋友圈功能测试用例
  17. 农业银行联行号怎么查询_中国农业银行大额联行号12位是什么,怎么查找
  18. 叶罗丽颜值测试软件齐娜多少分,精灵梦叶罗丽:美颜相机下灵公主16岁、齐娜7岁,他竟然拍的46岁...
  19. linux 查找py文件命令_Linux文件查找
  20. c语言源码什么意思,请问C语言源代码什么意思?

热门文章

  1. [Java学习打卡]
  2. UE4 Windows游戏窗口操作
  3. 语音-自监督学习《self-supervised learning》
  4. 服务器开关电源型号ab和sb,北京DELTA DPS-500AB-21A 500W 小1U开关电源 FLEX 80PLUS 白金价格 - 中国供应商...
  5. Spring Security 短信验证码登录(5)
  6. 点赞、收藏必读文章--数据分析的多变量分析
  7. STM32F051 触摸按键功能
  8. Oracle学习I —— Oracle介绍
  9. python 表格输出到pdf_用python将excel文件中选定的工作表打印为pdf
  10. thinkphp框架之后台登录页面cookie有效期设置