python学习-魔法函数【转载】

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

1
本文链接:https://blog.csdn.net/qdPython/article/details/126955376

一、什么是魔法函数(网络用语)

以双下划线开始,双下滑线结尾。魔法函数是为了增强一个类的特性。
魔法函数可以随意定义某个类的特性,这些方法在进行特定的操作时会自动被调用。

1 需求:封装一个员工列表,并遍历查看

(1)不使用魔法函数方法

1
2
3
4
5
6
7
8
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

company = Company(["tom", "bob", "jane"])
employee = company.employee
for em in employee:
print(em)

(2)使用 __getitem__(self, item)魔法函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

def __getitem__(self, item):
return self.employee[item]

company = Company(["tom", "bob", "jane"])
for em in company:
print(em)

"""
在解释器执行到for语句时,解释器首先寻找对象的迭代器。
虽然company对象没有迭代器,
但是解释器会尝试去寻找__getitem__()这一魔法函数,
有了这个魔法函数的对象就是一个可迭代的类型,即便没有迭代器,
解释器会调用该魔法函数,直到其抛出异常结束。
若注释__getitem__代码片段,则会抛出不可迭代异常。
"""

注意:在案例中,魔法函数既不是属于<class ‘Company’>,也不是从<class ‘object’>基继承过来。是一个独立的存在,往类里放入魔法函数之后,会增强类的一些类型。魔法函数不需要我们显示调用。Python会识别对象或自定义类的魔法函数,并隐式调用。

2 Python的数据模型以及数据模型对Python的影响

实际上魔法函数是网络上通常的叫法,其实魔法函数只不过是Python数据类型的一个概念而已。

当实现了 __getitem__(self, item)魔法函数,就可以使用for循环去遍历。魔法函数实际上会影响python语法的,或可以理解为Python语法实际上会识别一个对象或者说自定义类里的魔法函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 使用__getitem__(self, item)魔法函数
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

def __getitem__(self, item):
return self.employee[item]

company = Company(["tom", "bob", "jane"]) # __getitem__(self, item) 实际上也是序列类型
company1 = company[:2]
print(company1)

"""
['tom', 'bob']

"""
12345678910111213141516

如果不实现 __getitem__(self, item)就会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

# def __getitem__(self, item):
# return self.employee[item]

company = Company(["tom", "bob", "jane"])
company1 = company[:2]
print(company1)

"""
Traceback (most recent call last):
File "E:/projects/vue-django/myproject/sample/chapter02/company03.py", line 14, in <module>
company1 = company[:2]
TypeError: 'Company' object is not subscriptable

"""
123456789101112131415161718
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

# def __getitem__(self, item):
# return self.employee[item]

company = Company(["tom", "bob", "jane"])
print(len(company)) # 没有定义__len__(self)或__getitem__(self, item)魔法函数时,这个语句会报错

"""
Traceback (most recent call last):
File "E:/projects/vue-django/myproject/sample/chapter02/company03.py", line 14, in <module>
print(len(company))
TypeError: object of type 'Company' has no len()
"""
12345678910111213141516

两种方式实现对象可len()
(1)getitem(self, item) + 切片 实现
(2)len(self, item) 实现
在调用内置函数len()时,解释器首先会调用魔法函数 __len__(self),若没有,则退一步,去不断调用 __getitem__(self, item)直到抛出异常,len()函数返回执行次数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用__getitem__(self, item)魔法函数 + 切片
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

def __getitem__(self, item):
return self.employee[item]

company = Company(["tom", "bob", "jane"])
company1 =company[:]
print(len(company1))

"""
3
"""
123456789101112131415
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 使用__len__(self)魔法函数
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

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

company = Company(["tom", "bob", "jane"])
print(len(company))

"""
3
"""
1234567891011121314

魔法函数会影响Python语法和其内置函数本身的。

二、魔法函数一览

1 非数学运算

字符串表示 魔法函数提供了两个来应对开发模式的不同
(1)repr 用于命令行模式,作用:输入对象名即可调用repr方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  # jupyter或ipython 命令行终端运行
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

def __repr__(self):
return ','.join(self.employee)

company = Company(['tom', 'bob', 'jane'])
company

"""
tom,bob,jane
"""
1234567891011121314

(2)__str__用于脚本,作用:print(对象名)即可调用str方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
'''
学习中遇到问题没人解答?小编创建了一个Python学习交流群:711312441
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

def __str__(self):
return ','.join(self.employee)

company = Company(['tom', 'bob', 'jane'])
print(company)

"""
tom,bob,jane
"""
1234567891011121314151617

魔法函数只要定义了,就不需要开发者调用,Python解释器自己知道什么时候调用它。

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
54
55
56
57
集合、序列相关
__len__
__getitem__
__setitem__
__delitem__
__contains__


迭代相关
__iter__
__next__


可调用
__call__


with上下文管理器
__enter__
__exit__


数值转换
__abs__
__bool__
__int__
__float__
__hash__
__index__


元类相关
__new__
__init__


属性相关
__getattr__
__setattr__
__getattribute__
__setattribute__
__dir__


属性描述符
__get__
__set__
__delete__


协程
__await__
__aiter__
__anext__
__aenter__
__aexit
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556

2 数学运算

数学运算在一般开发使用不多,在数据处理是用的比较多,这里简单举几个例子。

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# ============== __abs__ Demo start ============
class Nums(object):
def __init__(self, num):
self.num = num

def __abs__(self):
return abs(self.num)


num = Nums(-1)
print(abs(num)) # 结果:1
# ============== __abs__ Demo end ============


# ============== __add__ Demo start ============
class MyVector(object):
def __init__(self, x, y):
self.x = x
self.y = y

def __add__(self, other_instance):
re_vector = MyVector(self.x+other_instance.x, self.y+other_instance.y)
return re_vector

def __str__(self):
return "x:{x}, y:{y}".format(x=self.x, y=self.y)


first_vec = MyVector(1, 2)
second_vec = MyVector(2, 3)
print(first_vec+second_vec) # 结果:x:3,y:5
# ============== __add__ Demo end ============



一元运算符
__neg__ -
__pos__ +
__abs__ |x|


二元运算符
__lt__ <
__le__ <=
__eq__ ==
__ne__ !=
__gt__ >
__ge__ >=


算术运算符
__add__ +
__sub__ -
__mul__ *
__truediv__ /
__floordiv__ //
__mod__ %
__divmod__ divmod()
__pow__ ** 或 pow()
__round__ round()


反向算术运算符
__radd__
__rsub__
__rmul__
__rtruediv__
__rfloordiv__
__rmod__
__rdivmod__
__rpow__


增量赋值算术运算符
__iadd__
__isub__
__imul__
__itruediv__
__ifloordiv__
__imod__
__ipow__


位运算符
__invert__ ~
__lshift__ <<
__rshift__ >>
__and__ &
__or__ |
__xor__ ^


反向位运算符
__rlshift__
__rrshift__
__rand__
__rxor__
__ror__


增量赋值位运算符
__ilshift__
__irshift__
__iand__
__ixor__
__ior__
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106

四、len函数的特殊性

len函数不仅仅调用 __len__方法这么简单,len函数对于set dict list等Python原生数据结构做了内部的优化,其性能是非常高的。应为原生数据结构中,会有一个专门的字段来储存数据长度,那么len函数会直接去读取这个字段,而不会去遍历它。

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • © 2023 glisses
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信