Python中类的魔术方法

  在Python中以两个下划线开头的方法,__init__、__str__、__doc__、__new__等,被称为"魔术方法"(Magic methods)。魔术方法在类或对象的某些事件出发后会自动执行,如果希望根据自己的程序定制自己特殊功能的类,那么就需要对这些方法进行重写。
  注意:Python 将所有以 __(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以 __ 为前缀。

Python提供的魔术方法

  魔术方法这里按照不同的类别有如下分类:

魔法方法
含义
 
基本的魔法方法
__new__(cls[, ...]) 1. __new__ 是在一个对象实例化的时候所调用的第一个方法
2. 它的第一个参数是这个类,其他的参数是用来直接传递给 __init__ 方法
3. __new__ 决定是否要使用该 __init__ 方法,因为 __new__ 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 __new__ 没有返回实例对象,则 __init__ 不会被调用
4. __new__ 主要是用于继承一个不可变的类型比如一个 tuple 或者 string
__init__(self[, ...]) 构造器,当一个实例被创建的时候调用的初始化方法
__del__(self) 析构器,当一个实例被销毁的时候调用的方法
__call__(self[, args...]) 允许一个类的实例像函数一样被调用:x(a, b) 调用 x.__call__(a, b)
__len__(self) 定义当被 len() 调用时的行为
__repr__(self) 定义当被 repr() 调用或者直接执行对象时的行为
__str__(self) 定义当被 str() 调用或者打印对象时的行为
__bytes__(self) 定义当被 bytes() 调用时的行为
__hash__(self) 定义当被 hash() 调用时的行为
__bool__(self) 定义当被 bool() 调用时的行为,应该返回 True 或 False
__format__(self, format_spec) 定义当被 format() 调用时的行为
 
有关属性
__getattr__(self, name) 定义当用户试图获取一个不存在的属性时的行为
__getattribute__(self, name) 定义当该类的属性被访问时的行为
__setattr__(self, name, value) 定义当一个属性被设置时的行为
__delattr__(self, name) 定义当一个属性被删除时的行为
__dir__(self) 定义当 dir() 被调用时的行为
__get__(self, instance, owner) 定义当描述符的值被取得时的行为
__set__(self, instance, value) 定义当描述符的值被改变时的行为
__delete__(self, instance) 定义当描述符的值被删除时的行为
 
比较操作符
__lt__(self, other) 定义小于号的行为:x < y 调用 x.__lt__(y)
__le__(self, other) 定义小于等于号的行为:x <= y 调用 x.__le__(y)
__eq__(self, other) 定义等于号的行为:x == y 调用 x.__eq__(y)
__ne__(self, other) 定义不等号的行为:x != y 调用 x.__ne__(y)
__gt__(self, other) 定义大于号的行为:x > y 调用 x.__gt__(y)
__ge__(self, other) 定义大于等于号的行为:x >= y 调用 x.__ge__(y)
 
算数运算符
__add__(self, other) 定义加法的行为:+
__sub__(self, other) 定义减法的行为:-
__mul__(self, other) 定义乘法的行为:*
__truediv__(self, other) 定义真除法的行为:/
__floordiv__(self, other) 定义整数除法的行为://
__mod__(self, other) 定义取模算法的行为:%
__divmod__(self, other) 定义当被 divmod() 调用时的行为
__pow__(self, other[, modulo]) 定义当被 power() 调用或 ** 运算时的行为
__lshift__(self, other) 定义按位左移位的行为:<<
__rshift__(self, other) 定义按位右移位的行为:>>
__and__(self, other) 定义按位与操作的行为:&
__xor__(self, other) 定义按位异或操作的行为:^
__or__(self, other) 定义按位或操作的行为:|
 
反运算
__radd__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rsub__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rmul__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rtruediv__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rfloordiv__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rmod__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rdivmod__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rpow__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rlshift__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rrshift__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rand__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rxor__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__ror__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
 
增量赋值运算
__iadd__(self, other) 定义赋值加法的行为:+=
__isub__(self, other) 定义赋值减法的行为:-=
__imul__(self, other) 定义赋值乘法的行为:*=
__itruediv__(self, other) 定义赋值真除法的行为:/=
__ifloordiv__(self, other) 定义赋值整数除法的行为://=
__imod__(self, other) 定义赋值取模算法的行为:%=
__ipow__(self, other[, modulo]) 定义赋值幂运算的行为:**=
__ilshift__(self, other) 定义赋值按位左移位的行为:<<=
__irshift__(self, other) 定义赋值按位右移位的行为:>>=
__iand__(self, other) 定义赋值按位与操作的行为:&=
__ixor__(self, other) 定义赋值按位异或操作的行为:^=
__ior__(self, other) 定义赋值按位或操作的行为:|=
 
一元操作符
__pos__(self) 定义正号的行为:+x
__neg__(self) 定义负号的行为:-x
__abs__(self) 定义当被 abs() 调用时的行为
__invert__(self) 定义按位求反的行为:~x
 
类型转换
__complex__(self) 定义当被 complex() 调用时的行为(需要返回恰当的值)
__int__(self) 定义当被 int() 调用时的行为(需要返回恰当的值)
__float__(self) 定义当被 float() 调用时的行为(需要返回恰当的值)
__round__(self[, n]) 定义当被 round() 调用时的行为(需要返回恰当的值)
__index__(self) 1. 当对象是被应用在切片表达式中时,实现整形强制转换
2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 __index__
3. 如果 __index__ 被定义,则 __int__ 也需要被定义,且返回相同的值
 
上下文管理(with 语句)
__enter__(self) 1. 定义当使用 with 语句时的初始化行为
2. __enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定
__exit__(self, exc_type, exc_value, traceback) 1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么
2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作
 
容器类型
__len__(self) 定义当被 len() 调用时的行为(返回容器中元素的个数)
__getitem__(self, key) 定义获取容器中指定元素的行为,相当于 self[key]
__setitem__(self, key, value) 定义设置容器中指定元素的行为,相当于 self[key] = value
__delitem__(self, key) 定义删除容器中指定元素的行为,相当于 del self[key]
__iter__(self) 定义当迭代容器中的元素的行为
__reversed__(self) 定义当被 reversed() 调用时的行为
__contains__(self, item) 定义当使用成员测试运算符(in 或 not in)时的行为

Python的魔术方法举例

这里仅针对常用的魔术方法进行举例,便于理解及灵活运用,部分魔术方法已经在:这篇博客中举例说明

小技巧:如果在需要返回对象的魔术方法里面不知道如何返回,可以调用super函数来执行父类的相同方法。

注意:魔法方法必须要使用return进行返回。

基本的魔法方法

__repr__(self):直接执行对象时执行的方法

>>>
>>> class A:
def __repr__(self):
return '__repr__' >>> a = A()
>>> a
__repr__

__bool__(self):判断对象的bool值时执行的方法,返回值只能是bool类型

>>>
>>> class C:
def __bool__(self):
return False >>> c = C()
>>> bool(c)
False
>>>

  

属性相关的魔术方法

__getattr__(self,name):获取一个不存在的属性时执行的方法。

__setattr__(self,name,value):设置一个属性时执行的方法。

__delattr__(self,name):删除一个属性时执行的方法。

__getattribute__(self,name):当该类的属性被访问时执行的方法。

>>> class A:
def __getattr__(self,name):
print('__getattr__')
def __setattr__(self,name,value):
print('__setattr__')
super().__setattr__(name,value)
def __delattr__(self,name):
print('__delattr__')
return super().__delattr__(name)
def __getattribute__(self,name):
print('__getattribute__')
return super().__getattribute__(name) >>> a = A()
>>> a.name
__getattribute__
__getattr__
>>> a.name = 'daxin'
__setattr__
>>> a.name
__getattribute__
'daxin'
>>> del a.name
__delattr__
>>>

  

注意

  1、__getattribute__,先于__getattr__访问

  2、__getattr__,没有在父类中进行定义,所以不能继承

  3、__setattr__,为设置属性,所以无需return

  3、在__setattr__时,不能直接使用self.name = value ,会造成死循环,因为在执行self.name = value的时候又会调用本身,无限循环下去

小技巧

  在__setattr__的时候,除了使用super的方式设置变量和值以外,还可以使用__dict__来设置。(但是建议使用super的方法)

>>> class A:
def __getattr__(self,name):
print('__getattr__')
def __setattr__(self,name,value):
print('__setattr__')
self.__dict__[name] = value
def __delattr__(self,name):
print('__delattr__')
return super().__delattr__(name)
def __getattribute__(self,name):
print('__getattribute__')
return super().__getattribute__(name) >>> a = A()
>>> a.name
__getattribute__
__getattr__
>>> a.name = 'daxin'
__setattr__
__getattribute__
>>> a.name
__getattribute__
'daxin'
>>>

  

属性相关之property

我们知道使用property可以把,类的某些方法当作属性进行访问,赋值,修改的,而Property内部也是通过类的魔术方法实现的。

下面的三个属性,主要用于在被当作其他的类的属性时使用的。

__set__(self,instance,value):当作一个描述符(属性)被赋值时执行的方法

__get__(self,instance,owner):当作一个描述符(属性)被获取时执行的方法

__delete__(self,instance):当作一个描述符(属性)被删除时执行的方法

 # 当作一个描述符被访问的含义就是:
# 一个函数在实例化的时候调用了其他类的对象 >>>
>>> class A:
pass >>> class B:
def __init__(self):
self.name = A() # 把A类的对象在B实例化的时候当作描述符(属性)进行赋值 >>>

 

>>> class A:
def __get__(self,instance,owner):
print('__get__','self:',self,'instance:',instance,'owner:',owner) def __set__(self,instance,value):
print('__set__','self:',self,'instance:',instance,'value:',value) def __delete__(self,instance):
print('__delete__','self:',self,'instance:',instance) >>> class B:
name = A() >>>
>>> b = B()
>>> b.name
__get__ self: <__main__.A object at 0x1134247f0> instance: <__main__.B object at 0x113411d68> owner: <class '__main__.B'>
>>> b.name = 'abc'
__set__ self: <__main__.A object at 0x1134247f0> instance: <__main__.B object at 0x113411d68> value: abc
>>> del b.name
__delete__ self: <__main__.A object at 0x1134247f0> instance: <__main__.B object at 0x113411d68>
>>>

  

扩展

由于不知道魔术方法传递的参数都是什么东西,所以在上面的例子打印了一下:

  • self:表示的是当前类(拥有__set__,__get__等方法的类本身)
  • instance:表示的是调用此类的类的实例化对象
  • owner:表示调用此类的类
  • value:表示要设置的值

根据这三个方法,那么我们完全可以定义自己的property属性

class Myproperty:   # 定义自己的Property

    def __init__(self,mget=None,mset=None,mdel=None):   # 绑定三个修改方法
self.mget = mget
self.mset = mset
self.mdel = mdel def __get__(self,instance,owner):
return self.mget(instance) def __set__(self,instance,value):
return self.mset(instance,value) def __delete__(self,instance):
return self.mdel(instance) class A:
def __init__(self):
self._x = None def setX(self,value):
self._x = value def getX(self):
try:
if self._x:
return self._x
except AttributeError as e:
print('Attribute is not exist') def delX(self):
del self._x name = Myproperty(getX,setX,delX) >>> a = A()
>>> a.name
>>> a.name = 'daxin'
>>> a.name
'daxin'
>>> del a.name
>>> a.name
Attribute is not exist
>>>

  

PS:关于为什么在Myproperty中传递instance,那么请注意我们的setX,getX,默认有一个self参数,使用自定义Property的话,那么我们必须手动传递,在Myproperty类中instance表示对象自己,所以传递instance给setX/getX,其实等同于self。

运算符相关魔术方法  

__add__:在执行加法时执行的方法

>>> class A(int):
def __add__(self,other):
print('__add__')
return super().__sub__(other) # 这里改写了 add方法,调用了父类的减法 >>> a = A(8)
>>> a + 6
__add__
2
>>>

  

__radd__:当左边的对象没有__add__方法时,执行右边对象的__radd__方法

>>> class A:
pass >>> class B(int): def __radd__(self,other):
return 'I am B,because right object not have __add__' >>>
>>> a = A()
>>> b = B(12)
>>> a + b
'I am B,because right object not have __add__'
>>>

  

容器类

在Python中,列表、元组、字典和集合等等,这些都可以称为容器。

__getitem__:获取容器中的元素,比如mylist[1]

__setitem__:设置容器中的元素,比如mylist[2] = 'hello'

__delitem__:删除容器中的某个元素,比如 del mylist[2]

# 定义一个不可变的列表类型
# 记录元素被访问的次数 class Mylist: def __init__(self,*args):
self.value = [ x for x in args ] # 列表生成式
self.count = {}.fromkeys(range(len(self.value)),0) # 定义字典用于按照索引统计访问次数 def __getitem__(self,key):
self.count[key] += 1
return self.value[key] def __repr__(self):
return str(self.value) __str__ = __repr__ >>> a = Mylist(1,2,3,45)
>>> a
[1, 2, 3,45]
>>> a.count
{0: 0, 1: 0, 2: 0, 3: 0}
>>> a[0]
1
>>> a.count
{0: 1, 1: 0, 2: 0, 3: 0}
>>> a[2]
3
>>> a.count
{0: 1, 1: 0, 2: 1, 3: 0}
>>> a[1] = 123

  

小练习

1、写一个矩形的类,那么这个类默认有长和宽两个参数,当传入一个叫square的属性时,值就等于边长,由于是正方形那么长和宽就等于边长。

class Rectangle(object):

    # init object
def __init__(self,width,height):
self.width = width
self.height = height def getacreage(self):
return self.width * self.height def __setattr__(self,name,value):
if name == 'square':
self.width = value
self.height = value else:
super().__setattr__(name,value)

  

2、完成如下要求:

  - 定制一个计时器类

  - start和stop方法代表启动和停止计时

  - 假设计时器对象t1,定制t1对象直接执行和打印时的输出信息

  - 当计时器未启动或已经停止计时,调用stop方法会给予温馨提示

  - 两个计时器对象可以进行相加: t1 + t2

import time

class Mytimer():

    # 初始化
def __init__(self):
self.prompt = 'Timer is not start' # 提示信息
self.start_time = None
self.stop_time = None # 启动定时器
def start(self):
if self.start_time:
print('Please Use Stop() to stop Timer') # 属性存在,表示已经启动计时
else:
self.start_time = time.time()
print('The start of Timer') # 关闭定时器
def stop(self):
if self.start_time:
self.stop_time = time.time()
print('The stop of Timer')
self._calc()
else:
print('Please Use Start() to start Timer') # 没有执行start(),检测不到属性,进行提示 # 计算耗时
def _calc(self):
self.prompt = 'Total time : '
self.end_time = self.stop_time - self.start_time
self.prompt += str('{:.2f}'.format(self.end_time))
self.start_time = None
self.stop_time = None # 直接执行对象时,打印提示信息
def __repr__(self):
return self.prompt # 打印对象和执行对象相同
__str__ = __repr__ # 计时器相加
def __add__(self,other):
self.prompt = 'All Time : '
add_time = self.end_time + other.end_time
self.prompt += str('{:.2f}'.format(add_time))
return self.prompt

  

3、完成如下需求

  - 定义一个温度类,然后定义两个描述符类用于描述摄氏度和华氏度两个属性。

  - 两个属性会自定进行转换,也就是说可以给摄氏度这个属性赋值,然后打印的华氏度属性是自动转换后的结果。

class Celsius:

    def __init__(self,value = 37):
self.value = value def __set__(self,instance,value):
self.value = value def __get__(self,instance,owner):
return self.value class Fahrenheit: def __set__(self,instance,value):
instance.cel = (float(value) - 32 ) / 1.8 def __get__(self,instance,owner):
return instance.cel * 1.8 + 32 # 通过instance实例访问,实例化后对象的cel属性 class Temperature:
cel = Celsius()
fah = Fahrenheit() >>> a = Temperature()
>>> a.cel
37
>>> a.fah
98.60000000000001
>>> a.fah = 100
>>> a.cel
37.77777777777778
>>>

PS:由于instance表示的是实例化的对象本身(a),所以这里使用instance,指代a,来访问设置的cel属性,来进行换算。

Python 类的魔术方法的更多相关文章

  1. Python学习笔记之面向对象编程(三)Python类的魔术方法

    python类中有一些方法前后都有两个下划线,这类函数统称为魔术方法.这些方法有特殊的用途,有的不需要我们自己定义,有的则通过一些简单的定义可以实现比较神奇的功能 我主要把它们分为三个部分,下文也是分 ...

  2. Python类之魔术方法

    一.什么是魔术方法? 在Python的方法,我们经常会遇见__XXX__(),这样的方法,我们一般称为"魔术方法",赶紧搬个小板凳,我们一起来看看魔术方法有啥神奇的地方,这些方法又 ...

  3. python类,魔术方法等学习&&部分ssti常见操作知识点复习加深

    python类学习&&部分ssti常见操作知识点复习加深 在做ssti的模块注入的时候经常觉得自己python基础的薄弱,来学习一下,其实还是要多练习多背. 在python中所有类默认 ...

  4. python 类属性与方法

    Python 类属性与方法 标签(空格分隔): Python Python的访问限制 Python支持面向对象,其对属性的权限控制通过属性名来实现,如果一个属性有双下划线开头(__),该属性就无法被外 ...

  5. python类之魔法方法

    python类之魔法方法: class A(object): def __init__(self,x): self.x = x def __neg__(self): print('-v') def _ ...

  6. day19-Python运维开发基础(类的魔术方法)

    1. __new__魔术方法 # ### __new__ 魔术方法 ''' 触发时机:实例化类生成对象的时候触发(触发时机在__init__之前) 功能:控制对象的创建过程 参数:至少一个cls接受当 ...

  7. 第8.3节 Python类的__init__方法深入剖析:构造方法与继承详解

    第8.3节 Python类的__init__方法深入剖析:构造方法与继承详解 一.    引言 上两节介绍了构造方法的语法及参数,说明了构造方法是Python的类创建实例后首先执行的方法,并说明如果类 ...

  8. Python中的魔术方法详解

    介绍 在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”,中文称『魔术方法』,例如类的初始化方法 __init__ ,Python中所有的魔术方法均在官方文档中 ...

  9. python所有的魔术方法

    据说,Python 的对象天生拥有一些神奇的方法,它们总被双下划线所包围,他们是面向对象的 Python 的一切. 他们是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个, ...

随机推荐

  1. vue和react动画区别

    触发动画 vue触发动画是 v-show,v-if ,动态组件或者组件的根节点 react 是CSSTransition上的属性 in 是true 或false触发动画

  2. MySQL免安装配置(亲测过,请放心借鉴)

    下载地址:https://dev.mysql.com/downloads/mysql 1.mysqld --initialize-insecure  初始化 2.mysqld install  安装服 ...

  3. NEU(Fst Network Embedding Enhancement via High Order Proximity Approximation)

    NEU(Fst Network Embedding Enhancement via High Order Proximity Approximation) NEU:通过对高阶相似性的近似,加持快速网络 ...

  4. code signing is required for product type 'Application' in SDK 'iOS 8.1' 错误分析以及解决方案

    在真机测试的时候往往会突然出现这样一个错误,code signing is required for product type 'Application' in SDK 'iOS 7.0'  ,就是说 ...

  5. YARN label 特性 & 指定队列及label提交任务

    以下基于 hadoop版本 hadoop-2.8.4 给各个节点打标签 yarn rmadmin -addToClusterNodeLabels fastcpu,normal # 是否独占默认是tru ...

  6. snmp安装,及测试

    https://www.cnblogs.com/xdp-gacl/p/4016524.html

  7. [转] Android中的设计模式-备忘录模式

    转自Android中的设计模式-备忘录模式 定义 备忘录设计模式的定义就是把对象的状态记录和管理委托给外界处理,用以维持自己的封闭性. 比较官方的定义 备忘录模式(Memento Pattern)又叫 ...

  8. Vue.js——使用$.ajax和vue-resource实现OAuth的注册、登录、注销和API调用

    转自:https://www.cnblogs.com/keepfool/p/5665953.html 概述 上一篇我们介绍了如何使用vue resource处理HTTP请求,结合服务端的REST AP ...

  9. 报表UI测试点

    1.功能完整性:是否实现了产品需求功能 2.数据准确性:UI显示数据,是否与后端传过来的数据一致 3.页面兼容性:浏览器兼容.布局 4.分页查询 5.数据格式一致性:小数精确位.百分比保留位数等 6. ...

  10. J2SE 8的流库 --- 转换流, 得到的还是流

    流的转换, 按照条件过滤/映射/摊平/截取/丢弃/连接/去重/排序. 辅助方法 public static int myCompare(String x, String y) { if(x.lengt ...