目录
  1. 〇、魔术方法定义
  2. 一、总述
    1. 1、class类的老大哥 __new__
    2. 2、实例销毁: __del__
    3. 3、类语法糖: __call__
      1. 3.1、演示代码
  3. 二、调用系统方法
    1. 1、 __repr__ 和 __str__ 的区别
    2. 2、参考代码
      1. 2.1、什么是交互环境
      2. 2.2、在交互环境中 __repr__ 和 __str__
  4. 三、有关属性
    1. 1、 __getattr__ 和 __setattr__ 存在的问题
    2. 2、演示代码
  5. 四、比较操作符
  6. 五、算数运算符
    1. 1、标准运算符
    2. 2、反运算
    3. 3、增量赋值运算
    4. 4、代码实例
      1. 4.1、3种运算符的区别
      2. 4.2、伪造list类,使其具备运算符功能
  7. 六、一元操作符
    1. 1、示例代码
  8. 七、类型转换
  9. 八、上下文管理(with 语句)
  10. 九、容器类型
    1. 1、 示例代码
【语法】python魔法函数+代码演示

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

〇、魔术方法定义

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

一、总述

1、class类的老大哥 __new__

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

a = A()

结果如下:

1
2
__new__方法被执行
__init__方法被执行

2、实例销毁: __del__

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

3、类语法糖: __call__

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

3.1、演示代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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("蜘蛛侠", "幻世", "黑豹", "奇异博士")

结果如下:

1
2
3
灭霸被创造出来了了
灭霸打了个响指,一共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、参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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)

显示结果如下:

1
2
<__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__

代码如下:

交互环境下,结果完全不同

  • 可以看出,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的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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) # 打印删除后的结果

结果如下:

1
2
3
4
5
6
__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种运算符的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

输出结果:

1
2
3
A:加法的标准运算
A:加法的反运算
A:加法的增强赋值运算

4.2、伪造list类,使其具备运算符功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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)

输出结果:

1
2
3
4
__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、示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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)

输出结果

1
2
3
__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 语句)

演示代码如下:

1
2
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、 示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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)

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
7
__getitem__被执行: 正在读取复仇者【雷神】的信息,其战斗力为110
======== 雷神的战斗力:110 ========
__setitem__被执行: 新增复仇者【古鲁姆】,其战斗力为15.6
__delitem__被执行: 复仇者【幻世】已阵亡!
__iter__被执行
雷神
浩克
奇异博士
蜘蛛侠
美国队长
幻世
__gt__被执行 战斗力大于60的复仇者:60
[('钢铁侠', 106), ('雷神', 110), ('浩克', 120), ('奇异博士', 90), ('蜘蛛侠', 80)]
文章作者: Haibei
文章链接: http://www.haibei.online/posts/3165833207.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Haibei的博客
打赏
  • 微信
  • 支付宝

评论