Python魔法函数+代码演示

欢迎访问的个人博客,博客地址为 https://haibei.online/

〇、魔术方法定义

  • 在python语法中,往往你会看到有的名称前面和后面都加上了双下划线,由这些名字组成的集合所包含的方法就叫做魔法方法,也叫做特殊方法。
  • python的魔术方法非常强大,然而随之而来的则是责任。了解正确的方法去使用非常重要!

一、总述

1、class类的老大哥 __new__

  • 之所以称之为老大哥,是因为 __new__ 在类执行时第一个被调用,而不是 __init__
  • 如果 __new__ 返回值为None,那么 __init__ 根本不会被调用。
  • 举个例子
class A(object):
    def __new__(cls):
        print("__new__方法被执行")
        return super().__new__(cls)
    def __init__(self):
        print("__init__方法被执行")
 
a = A()

结果如下:

__new__方法被执行
__init__方法被执行

2、实例销毁: __del__

__del__(self) :当一个实例被销毁的时候调用的方法

3、类语法糖: __call__

__call__(self[, args...]) 允许一个类的实例像函数一样被调用: x(a, b) 调用 x.__call__(a, b)

3.1、演示代码
class A(object):
    def __init__(self, name):
        self.name = name
        print(f"{name}被创造出来了了")

    def __call__(self, *args, **kwargs):
        """允许一个类的实例像函数一样被调用"""
        print(f"{self.name}打了个响指,一共{len(args)}名超级英雄消失,他们分别是{'、'.join(args)}等。")

    def __del__(self):
        """程序结束(销毁)后,会自动执行"""
        print(f"{self.name}被销毁了")

if __name__ == '__main__':
    a = A("灭霸")
    a("蜘蛛侠", "幻世", "黑豹", "奇异博士")

结果如下:

灭霸被创造出来了了
灭霸打了个响指,一共4名超级英雄消失,他们分别是蜘蛛侠、幻世、黑豹、奇异博士等。
灭霸被销毁了

二、调用系统方法

语法说明符号
__len__(self)定义当被 len 调用时的行为len()
__repr__(self)定义当被 repr 调用时的行为repr()
__str__(self)定义当被 str 调用时的行为str()
__bytes__(self)定义当被 bytes 调用时的行为bytes()
__hash__(self)定义当被 hash 调用时的行为hash()
__bool__(self)定义当被 bool 调用时的行为bool()
__format__(self, format_spec)定义当被 format 调用时的行为format()

1、 __repr____str__ 的区别

  1. 当一个类中没有使用 __repr____str__ 这两个魔法函数, print() 直接打印实例会返回类名和内存地址

  2. __repr____str__ 这两个方法都是用于显示的, __str__ 是面向用户的,而 __repr__ 面向程序员。

  3. 打印操作会首先尝试 __str__ ,它通常应该返回一个友好的显示。

  4. 在交互环境下, __repr__ 会生效,而 __str__ 不会起作用。直接执行实例变量名即可打印,无需 print()

2、参考代码

class A(object):
    """对照组:没有使用__str__,打印结果为类型名及其内存地址"""

    def __init__(self, info):
        self.info = info

class B(object):
    """标准组:使用__str__了,打印结果为__str__魔法函数的返回值"""

    def __init__(self, info):
        self.info = info

    def __str__(self):
        return self.info + f"我是{__class__.__name__}类,我用了__str__的魔法函数,真香!"

if __name__ == '__main__':
    demo_message = "真氪金定律:程序员的能力与发量成反比!快拔头发吧!"
    a = A(demo_message)
    b = B(demo_message)
    print(a)
    print(b)

显示结果如下:

<__main__.A object at 0x1097bf438>
真氪金定律:程序员的能力与发量成反比!快拔头发吧!我是B类,我用了__str__的魔法函数,真香!
2.1、什么是交互环境

交互环境的典型特点是写完一行,执行一行。非交互环境要一次执行所有程序

  1. 直接在Terminal中启动python,就是交互环境
  2. Jupyter Notebook和Lab都是交互环境
  3. Pycharm中的python Console是交互环境
  4. Anaconda Spyder中提供的Console也是交互环境
2.2、在交互环境中 __repr____str__

代码如下:

[外链图片转存中…(img-2LbfSCdz-1572167290870)]

  • 可以看出,5行中直接执行 a 因为定义了 __repr__ ,在交互环境下是可以正确显示的;
  • 而在7行中,直接执行 b 是无法正确显示的。

三、有关属性

语法说明
__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)定义当描述符的值被删除时的行为

1、 __getattr____setattr__ 存在的问题

  • __getattr____setattr__ 这两个方法 __init__ 中变量赋值事,就会默认被创建。
  • 举个例子:传统赋值方式是 self.a="a"__getattr____setattr__ 就会默认创建出来。
  • 所以当在 __getattr__ 中使用 getattr() 或者 self.xxx , 程序会陷入无限递归。
  • 防止递归解决方式有两种,但原理相同。使用超类赋值或者通过超类进行访问
    1. 超类赋值(新式类):super().setattr(“xxx”, xxx)
    2. 超类访问(新式类):super().getattr(“xxx”)

2、演示代码

代码功能:伪造一个类,使其具有与 dict 类型变量相同的功能;
同时,该类还具备通过’点’来访问dict的方法

class A(object):
    """伪造字典"""

    def __init__(self, **kwargs):
        super().__setattr__("dict_like", kwargs)  # 超类赋值,防止递归

    def __getattr__(self, name):
        """可以调用dict类的系统方法"""
        print("__getattr__被执行:", end="")
        if name in self.dict_like:
            print(f"\t访问{name}变量", end="")
            return self.dict_like[name]
        else:
            print(f"\t访问{name}方法", end="")
            return getattr(self.dict_like, name)

    def __setattr__(self, key, value):
        """可以通过'点'的方式,设置字典内容"""
        print("__setattr__被执行:", end="")
        self.dict_like[key] = value

    def __delattr__(self, item):
        print("__delattr__被执行:", end="")
        del self.dict_like[item]

    def __len__(self):
        print("__len__被执行:", end="")
        return len(self.dict_like)

    def __str__(self):
        print("__str__被执行:", end="")
        return str(self.dict_like)

if __name__ == '__main__':
    a = A(a=1, b=2, c=3, d=4, e=5, f=6, g=7)  # 实例化
    print(a)  # 打印
    print(len(a))  # 获取长度
    a.h = 8  # 字典赋值
    print(a)  # 打印赋值结果
    print("\t|\t访问b元素:", a.b)  # 访问b元素
    print(list(a.items()))  # 调用dict的系统方法
    del a.f  # 删除字典元素
    print(a)  # 打印删除后的结果

结果如下:

__str__被执行:{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7}
__len__被执行:7
__setattr__被执行:__str__被执行:{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7, 'h': 8}
__getattr__被执行:访问b变量 | 访问b元素: 2
__getattr__被执行:访问items方法[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7), ('h', 8)]
__delattr__被执行:__str__被执行:{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'g': 7, 'h': 8}

四、比较操作符

语法说明符号
__lt__(self, other)定义小于号的行为x < y
__le__(self, other)定义小于等于号的行为x <= y
__eq__(self, other)定义等于号的行为x == y
__ne__(self, other)定义不等号的行为x != y
__gt__(self, other)定义大于号的行为x > y
__ge__(self, other)定义大于等于号的行为x >= y

五、算数运算符

可以让类使用算数预算符进行操作

1、标准运算符

说明代码符号反运算增强运算
加法__add__(self, other)++=
减法__sub__(self, other)--=
乘法__mul__(self, other)**=
除法__truediv__(self, other)//=
整除__floordiv__(self, other)////=
模运算__mod__(self, other)%%=
divmod操作__divmod__(self, other)divmod()
次方__pow__(self, other[, modulo])power()或 ****=
左位移__lshift__(self, other)<<<<=
右位移__rshift__(self, other)>>>>=
按位与__and__(self, other)&&=
按位异或__xor__(self, other)^^=
按位或__or__(self, other)||=

2、反运算

  • 在标准运算符的前面加上“r”就是反运算
  • 假设 ab 是两个实例,就a而言, a+b 为标准预算,因为 a 在前边; b+a 就是反运算,因为 a 在后面。
  • 如果 a 想使用反运算,就需要定义 __radd__

3、增量赋值运算

  • 在标准运算符的前面加上“i”就是增量赋值运算

4、代码实例

4.1、3种运算符的区别
class A(object):
    def __add__(self, other):
        print("A:加法的标准运算")

    def __radd__(self, other):
        print("A:加法的反运算")
        
    def __iadd__(self, other):
        print("A:加法的增强赋值运算")
class B:
    pass

a=A()
b=B()
a+b
b+a
a+=b

输出结果:

A:加法的标准运算
A:加法的反运算
A:加法的增强赋值运算
4.2、伪造list类,使其具备运算符功能
class A(object):
    """伪造list,通过运算符号,完成增减数据"""

    def __init__(self, *args):
        self.list_like = list(args)  # args默认为元组类型,需要转换成为list

    def __add__(self, other):
        print("__add__被执行:", end="")
        self.list_like.append(other)
        return self

    def __radd__(self, other):
        print("__radd__被执行:", end="")
        self.list_like = [other] + self.list_like
        return self

    def __iadd__(self, other):
        print("__iadd__被执行:", end="")
        self.list_like.append(other)
        return self

    def __mul__(self, other):
        print("__mul__被执行:", end="")
        self.list_like = list(map(lambda x: x * other, self.list_like))
        return self

    def __len__(self):
        return len(self.list_like)

    def __str__(self):
        return str(self.list_like)

if __name__ == '__main__':
    a = A(1, 2, 3, 5, 6, 7)
    a = a + 10  # 向list中最后一位添加数据10
    print(a)
    a = 0 + a  # 向list中第一位添加数据0
    print(a)
    a += 100  # 向list中最后一位添加数据100
    print(a)
    a = a * 6  # 向list中每一位乘以6
    print(a)

输出结果:

__add__被执行:[1, 2, 3, 5, 6, 7, 10]
__radd__被执行:[0, 1, 2, 3, 5, 6, 7, 10]
__iadd__被执行:[0, 1, 2, 3, 5, 6, 7, 10, 100]
__mul__被执行:[0, 6, 12, 18, 30, 36, 42, 60, 600]

六、一元操作符

语法说明符号
__neg__(self)定义正号的行为-x
__pos__(self)定义负号的行为+x
__abs__(self)定义绝对值的行为abs()
__invert__(self)定义按位求反的行为~x

1、示例代码

class A(object):
    """伪造list,具备算法运算符功能"""

    def __init__(self, *args):
        self.list_like = list(args)  # args默认为元组类型,需要转换成为list

    def __neg__(self):
        print("__neg__被执行:", end="")
        self.list_like = list(map(lambda x: -x, self.list_like))
        return self

    def __abs__(self):
        print("__abs__被执行:", end="")
        self.list_like = list(map(lambda x: abs(x), self.list_like))
        return self

    def __invert__(self):
        print("__invert__被执行:", end="")
        self.list_like = list(map(lambda x: ~x, self.list_like))
        return self

    def __len__(self):
        return len(self.list_like)

    def __str__(self):
        return str(self.list_like)

if __name__ == '__main__':
    a = A(5, 3, 16, 14, 60, 3)
    a = -a  # 对list中每一位数据取负值
    print(a)
    a = abs(a)  # 对list中每一位数据绝对值
    print(a)
    a = ~a  # 对list中每一位数据取反
    print(a)

输出结果

__neg__被执行:[-5, -3, -16, -14, -60, -3]
__abs__被执行:[5, 3, 16, 14, 60, 3]
__invert__被执行:[-6, -4, -17, -15, -61, -4]

七、类型转换

语法说明
__complex__(self)定义当被 complex() 调用时的行为(需要返回恰当的值)
__int__(self)定义当被 int() 调用时的行为(需要返回恰当的值)
__float__(self)定义当被 float() 调用时的行为(需要返回恰当的值)
__round__(self[, n])定义当被 round() 调用时的行为(需要返回恰当的值)
__index__(self)当对象是被应用在切片表达式中时,实现整形强制转换;如果你定义了一个可能在切片时用到的定制的数值型, 你应该定义 index。如果 index 被定义,则 int 也需要被定义,且返回相同的值

八、上下文管理(with 语句)

演示代码如下:

with open("./1.txt","r",encoding="utf-8") as f:
    data=f.readlines()
语法说明
__enter__1. 定义当使用 with 语句时的初始化行为;
2. __enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定
__exit__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)定义当使用成员测试运算符( innot in )时的行为

1、 示例代码

class Avenger(object):
    """复仇者联盟"""

    def __init__(self, **kwargs):
        self.members = kwargs
        self.names = list(kwargs.keys())
        self.index = 0

    def __getitem__(self, item):
        fighting_num = self.members[item]
        print("__getitem__被执行:", f"正在读取复仇者【{item}】的信息,其战斗力为{fighting_num}")
        return fighting_num

    def __setitem__(self, key, value):
        print("__setitem__被执行:", f"新增复仇者【{key}】,其战斗力为{value}")
        self.members["key"] = value

    def __delitem__(self, key):
        print("__delitem__被执行:", f"复仇者【{key}】已阵亡!")
        del self.members["key"]

    def __iter__(self):
        print("__iter__被执行")
        return self

    def __next__(self):
        if self.index == len(self.names) - 1:
            raise StopIteration
        else:
            self.index += 1
        return self.names[self.index]

    def __gt__(self, other):
        print("__gt__被执行", f"战斗力大于60的复仇者:{other}")
        members = filter(lambda x: x[-1] > other, self.members.items())
        return list(members)

    def __len__(self):
        return len(self.members)

    def __str__(self):
        power_info = map(lambda x: "{0}的战斗力为 {1}".format(*x), self.members.items())
        return "\n".join(power_info)

if __name__ == '__main__':
    a = Avenger(钢铁侠=106, 雷神=110, 浩克=120, 奇异博士=90, 蜘蛛侠=80, 美国队长=50, 幻世=60)
    print(len(a))
    print(f"======== 雷神的战斗力:{a['雷神']} ========")
    a["古鲁姆"] = 15.6
    del a["幻世"]
    for i in a:
        print(i)
    print(a > 60)

结果如下:

7
__getitem__被执行: 正在读取复仇者【雷神】的信息,其战斗力为110
======== 雷神的战斗力:110 ========
__setitem__被执行: 新增复仇者【古鲁姆】,其战斗力为15.6
__delitem__被执行: 复仇者【幻世】已阵亡!
__iter__被执行
雷神
浩克
奇异博士
蜘蛛侠
美国队长
幻世
__gt__被执行 战斗力大于60的复仇者:60
[('钢铁侠', 106), ('雷神', 110), ('浩克', 120), ('奇异博士', 90), ('蜘蛛侠', 80)]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值