Python魔法方法

写在前面

以前手写在笔记本上的知识,这次把这些杂七杂八整理下上云,省的以后笔记本不知道丢哪里。

参考学习:

python魔法方法汇总

概述

主要是双下划线方法,是一系列特殊的方法,其名称以两个下划线开头和结尾。

这些方法在Python的类定义中扮演重要角色,允许开发者通过它们来自定义类的行为,使得类能够以更自然的方式与Python的语言特性互动。

一、创建访问类
1、__init__

__init__(self, 参数1, 参数2......)

在创建实例时会立刻执行此方法从,对实例进行初始化,也是最常用的调用方法

当我们创建一个空白对象后,我们用该方法为这个空白对象增加属于它的独特部分(创建属性值),此方法不需要任何返回值。

2、__new__

__new__(cls, 参数1, 参数2......)

此方法永远为静态方法,且不需要显式装饰(即通常不需要@staticmethod)。

会在__init__方法之前调用,并返回一个空白对象。

第一个参数时创建实例所需的类,其余的参数会传给__init__方法,最后必须返回一个创建好的对象,且一定要有cls。(其返回对象会交给__init__进行初始化,若不是cls对象,则不会触发cls的__init__)

若要重写该方法,也需要调用父类的__new__得到空白对象。

instance = super().__new__(cls)
return instance

但实际上,__new__参数不会影响__init__收集参数(__new__为静态方法,里面执行的操作不会影响对象)。

3、__getattr__

__getattr__(self, attr_name: str)

当访问一个对象不存在的属性时会触发此方法。

若对象不具备某属性,则会触发AttributError异常。

若自定义此方法,则不会报异常。

4、__getattribute__

__getattribute__(self, attr_name: str)

其类似于__getattr__,而此方法是访问属性时都会执行。

其有返回值,返回为对应属性的值,自定义时如不写返回,则永远也得不到对应属性的值。

需要注意的是,其方法内一定不要使用常规方法访问属性,即用【self.属性】,因为此方法会导致死循环(self表示对象,便会进入该对象的__getattribute__,之后再次进入self,最终栈溢出)。

为解决上面的方法,可以这样操作:(其中__name即想要取的属性)

  • 调用父类的方法(将被访问属性传进去)
super().__getattribute__(__name)
  • 调用基类方法(需要传self指定对象)
object.__getattribute__(self.__name)

梳理访问对象属性的逻辑:

当试图访问一个对象的属性时,必先进入__getattribute__方法。

若无此属性,则会抛出AttributError异常;若自定义重写了__getattr__,则进入此方法。

若有此属性,则返回属性值。

5、__setattr__

__setattr__(self, attr_name: str, attr_value: str)

对一个属性/对象进行赋值时便会调用。

在使用__init__进行赋值时,也会调用此方法。

对不存在的属性赋值是允许的,且赋值后是可以访问的,即__getattribute__不会抛出异常。

内部赋值也容易死循环,即在__setattr__中用self进行赋值,解决方法与__getattribute__一样。

二、类型转换类
1、__str__

__str__(self)

将对象转化为字符串时调用,必须有一个字符串类型的返回值,在执行str(对象)时调用。

2、__bool__

__bool__(self)

在执行bool(对象)或者是放在if后进行条件判断时调用。

3、__int__、__float__

__int__(self)、__float__(self)

在执行init(对象)或者是float(对象)时执行。

需要注意两方法的返回值需要符合各自的要求。

三、比较操作符类
1、__eq__、__ne__

__eq__(self, other)、__ne__(self, other)

当使用==符号时,__eq__会被调用,会率先调用左边对象的__eq__, 若左边没有,才会调用右边。而__ne__是使用!=时候调用。

两方法都需要返回True or False。

2、__lt__、__le__、__gt__、__ge__

__lt__(self, other)、__le__(self, other)、__gt__(self, other)、__ge__(self, other)

以上方法依次在<、<=、>、>=时候调用。

在进行对象比较排序时,即可以自定义规则(借用列表的sort方法)。

如boy1,boy2,boy3,boy4为四个Boy对象,通过自定义上面的魔法方法,可以比较每个对象的年龄(用列表排序[boy1, boy2, boy3, boy4])

通常不需要全部定义,只要定义__eq__、__lt__、__gt__即可,因为==与!=,<与>=,>与<=互反。

3、一元操作符
__pos__(self)数值取正
__neg__(self)数值取负
__invert__(self)对象取反

 

4、二元操作符

二元操作符的调用顺序是从左到右,左侧对象的方法优先调用。

在重写该方法时,要保证其可交换性。

对于该类方法,由分为三小类:

1)普通方法
2)即席方法

如+=、-=等,即修改self本身并返回self。

若没有定义即席方法,只定义了普通方法,并不会报错,此时+=等价于x=x.__add__(y),x接收x.__add__(y)的返回值,类型也变为数值型。

3)取反方法 

+左边没有提供__add__方法,且两边操作的对象不同,执行右边对象的__radd__方法。

即x+y时,x没提供__add__方法且x,y的类型不同,执行y.__radd__(x)。

操作符魔法方法
操作符普通方法取反方法即席方法
+__add____radd____iadd__
-__sub____rsub____isub__
*__mul____rmul____imul__
/__truediv____rtruediv____itruediv__
//__floordiv____rfloordiv____ifloordiv__
%__mod____rmod____imod__
**__pow____rpow____ipow__
&__and____rand____iand__
|__or____ror____ior__
^__xor____rxor____ixor__
<<__lshift____rlshift____ilshift__
>>__rshift____rrshift____irshift__

 

四、集合类

1、__getitem__

__getitem__(self, index)

当对象使用下标查询时会进入该方法,该方法会收集index,后续如何处理, 自己定义/

该方法只能完成查询操作,不能对下标进行赋值。

2、__setitem__

__setitem__(self, key, value)

若重写此方法,便可以使用中括号+下标进行赋值。

3、__contains__

__contains__(self, ele)

在使用in的时候进行赋值,如 boy1 in boy2,(查询boy1对象是否在boy2对象中)

五、其他类

1、__len__

__len__(self)

调用len(对象)时候会执行,返回对象的长度(返回值需要>=0)。

在没有定义__bool__时,若使用if对象或者bool(对象)时候,也会进入该方法。(0代表flase)

2、__repr__

__repr__(self)

确定对象在终端的显示方式,即print()。

自定义终端的显示方式,否则返回的样式并不友好(<__main__.Object at XXX>)。

对比__repr__(self)与__str__(self):

前者时显示在终端,debug对象时候查看,给程序员看;后者是调用str(对象)时执行,主要给用户看。

3、__hash__

__hash__(self)

在对象传递给散列函数时调用,也就是将数据放入集合时有,在hash表中会调用。

其返回一个整型值,可以是负数,作为一个唯一标识,标识一个对象。

若自定义了__eq__方法,但没有定义__hash__方法,默认的__hash__将会隐式变为None,即无法调用。

例如下面的代码示例:

class MyClass(object):
 
    def __init__(self, age, name):
        self.name = name
        self.age = age
     
    def __hash__(self):
        print(f"{self.name}的__hash__被执行")
        return 0
     
    def __repr__(self) -> str:
        return f"MyClass(name={self.name}, age={self.age})"
     
    def __eq__(self, other) -> bool:
        if self.age == other.age:
            return True
        else:
            return False
     
boy1 = MyClass(name="小明", age=14)
boy2 = MyClass(name="小华", age=14)
boy3 = MyClass(name="小褚", age=14)
boys = set()
boys.add(boy1)
boys.add(boy2)
boys.add(boy3)
print(boys)

其执行结果为:

小明的__hash__被执行
小华的__hash__被执行
小明的__eq__被执行
小红的__hash__被执行
小明的__eq__被执行
{MyClass (name=小明, age=14)}

相应解释如下:

重写了__hash__和__eq__方法,当年龄相等时,__eq__将返回True,而__hash__则一直返回0。
在set中判断两个对象是否相等,使用的逻辑是: obj1.__hash__() == obj.__hash__() and (obj1.id() == obj.id() or obj1.__eq__(obj2))。
(因为__hash__返回均为0,且年龄一致,所以返回的最后一直是小明,不会替换)

4、__format__

__format__(self, spec_str)

在字符串输出时调用,在“”.format(对象)中执行。

而spec_str参数会保留在{}中的:后面的部分(格式化要求)。如:

“{}, {: .2f}”.format(boy1, boy3)中,spec_str依次为“”,“.2f”。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值