7Python全栈之路系列之面向对象运算符重载

简介:

Python全栈之路系列之面向对象运算符重载


运算符重载的概念如下:

  1. 运算符重载让类拦截常规的Python运算;

  2. 类可重载所有Python表达式运算符;

  3. 类也可重载打印、函数调用、属性点号运算等内置运算;

  4. 重载是类实例的行为想内置类型;

  5. 重载是通过提供特殊名称的类方法来实现的;


常见的运算符重载方法

方法 重载 调用
__init__ 构造函数 对象建立:X = Class(args)
__del__ 解析函数 X对象收回
__add__ 运算符+ 如果没有__iadd__,X+Y,X+=Y
__or__ 运算符或 如果没有__ior__
__repr__,__str__ 打印、转换 print(X)、repr(X)、str(X)
__call__ 函数调用 X(args, *kwargs)
__getattr__ 点号运算 X.undefined
__setattr__ 属性赋值语句 X.any = value
__delattr__ 属性删除 del X.any
__getattribute__ 属性获取 X.any
__getitem__ 索引运算 X[key],X[i:j],没__iter__时的for循环和其他迭代器
__setitem__ 索引赋值语句 X[key]=value,X[i:k]=sequence
__delitem__ 索引和分片删除 del X[key], del X[i:j]
__len__ 长度 len(X),如果没有__bool__,真值测试
__bool__ 布尔测试 bool(X),真测试
__lt____gt____le____ge____eq____ne__ 特定的比较 XY…,x>
__radd__ 右侧加法 Other + X
__iadd__ 增强的加法 X += Y
__iter____next__ 迭代环境 I=iter(X),next(I)
__contains__ 成员关系测试 item in X(任何可迭代对象)
__index__ 整数值 hex(X),bin(X),oct(X),o[X],O[X:]
__enter__,__exit__ 环境管理器 with obj as var:
__get__,__set__,__delete__ 描述符属性 X.attr,X.attr=Value,del X.attr
__new__ 创建 __init__之前创建对象

所有重载方法的名称前后都有两个下划线字符,以便把同类中定义的变量名区别开来。

构造函数和表达式:__init____sub__

1
2
3
4
5
6
7
8
9
10
11
12
>>>  class  Number:
...    def  __init__( self , start):
...      self .data  =  start
...    def  __sub__( self , other):
...      return  Number( self .data  -  other)
... 
>>> X  =  Number( 5 )
>>> Y  =  -  2
>>> Y
<__main__.Number  object  at  0x10224d550 >
>>> Y.data
3

索引和分片: __getitem____setitem__

基本索引

1
2
3
4
5
6
7
8
9
10
>>>  class  Index:
...      def  __getitem__( self , item):
...          return  item  * *  2
... 
>>> 
>>>  for  in  range ( 5 ):
...     I  =  Index()
...      print (I[i], end = ' ' )
... 
0  1  4  9  16

切片索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>>  class  Index:
...   data  =  [ 5 6 7 8 9 ]
...    def  __getitem__( self , item):
...      print ( 'getitem: ' , item)
...      return  self .data[item]
...    def  __setitem__( self , key, value):
...      self .data[key]  =  value
... 
>>> X  =  Index()
>>>  print (X[ 1 : 4 ])
getitem:   slice ( 1 4 None )
[ 6 7 8
>>> X[ 1 : 4 =  ( 1 1 1 )
>>>  print (X[ 1 : 4 ])
getitem:   slice ( 1 4 None )
[ 1 1 1 ]

索引迭代:__getitem__

如果重载了这个方法,for循环每次循环时都会调用类的getitem方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>>  class  stepper:
...      def  __getitem__( self , item):
...          return  self .data[item].upper()
... 
>>> 
>>> X  =  stepper()
>>> X.data  =  'ansheng'
>>>  for  item  in  X:
...      print (item)
... 
A
N
S
H
E
N
G

迭代器对象:__iter____next__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>>  class  Squares:
...    def  __init__( self , start, stop):
...          self .value  =  start  -  1
...          self .stop  =  stop
...    def  __iter__( self ):
...          return  self
...    def  __next__( self ):
...          if  self .value  = =  self .stop:
...              raise  StopIteration
...          self .value  + =  1
...          return  self .value  * *  2
... 
>>>  for  in  Squares( 1 5 ):
...    print (i)
... 
1
4
9
16
25

成员关系:__contains____iter____getitem__

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
class  Iters:
     def  __init__( self , value):
         self .data  =  value
         
     def  __getitem__( self , item):
         print ( 'get[%s]'  %  item, end = '')
         return  self .data[item]
         
     def  __iter__( self ):
         print ( 'iter>==' , end = '')
         self .ix  =  0
         return  self
         
     def  __next__( self ):
         print ( 'next:' , end = '')
         if  self .ix  = =  len ( self .data):  raise  StopIteration
         item  =  self .data[ self .ix]
         self .ix  + =  1
         return  item
         
     def  __contains__( self , item):
         print ( 'contains: ' , end = ' ' )
         return  item  in  self .data
         
=  Iters([ 1 2 3 4 5 ])
print ( 3  in  X)
for  in  X:
     print (i, end = '|' )
     
print ([i  * *  2  for  in  X])
print ( list ( map ( bin , X)))
 
=  iter (X)
while  True :
     try :
         print ( next (I), end = ' @' )
     except  StopIteration as e:
         break

属性引用:__getattr____setattr__

当通过未定义的属性名称和实例通过点号进行访问时,就会用属性名称作为字符串调用这个方法,但如果类使用了继承,并且在超类中可以找到这个属性,那么就不会触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>>  class  empty:
...      def  __getattr__( self , item):
...          if  item  = =  'age' :
...              return  40
...          else :
...              raise  AttributeError(item)
... 
>>> 
>>> x  =  empty()
>>>  print (x.age)
40
>>>  print (x.name)
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
   File  "<stdin>" , line  6 in  __getattr__
AttributeError: name
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>>  class  accesscontrol:
...      def  __setattr__( self , key, value):
...          if  key  = =  'age' :
...              self .__dict__[key]  =  value
...          else :
...              raise  AttributeError(key  +  ' not allowed' )
... 
>>> 
>>> x  =  accesscontrol()
>>> x.age  =  40
>>>  print (x.age)
40
>>> x.name  =  'Hello'
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
   File  "<stdin>" , line  6 in  __setattr__
AttributeError: name  not  allowed

__repr____str__会返回字符串表达式

__repr____str__都是为了更友好的显示,具体来说,如果在终端下print(Class)则会调用__repr__,非终端下会调用__str__方法,且这两个方法只能返回字符串;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class  adder:
     def  __init__( self , value = 0 ):
         self .data  =  value
         
     def  __add__( self , other):
         self .data  + =  other
         
     def  __repr__( self ):
         return  'addrepr(%s)'  %  self .data
         
     def  __str__( self ):
         return  'N: %s'  %  self .data
         
=  adder( 2 )
+  1
print (x)
print (( str (x),  repr (x)))

右侧加法和原处加法: __radd____iadd__

只有当+右侧的对象是类实例,而左边对象不是类实例的时候,Python才会调用__radd__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class  Commuter:
     def  __init__( self , val):
         self .val  =  val
         
     def  __add__( self , other):
         print ( 'add' self .val, other)
         return  self .val  +  other
         
     def  __radd__( self , other):
         print ( 'radd' self .val, other)
         return  other  +  self .val
         
=  Commuter( 88 )
=  Commuter( 99 )
print (x  +  1 )
print ('')
print ( 1  +  y)
print ('')
print (x  +  y)

使用__iadd__进行原处加法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class  Number:
     def  __init__( self , val):
         self .val  =  val
     def  __iadd__( self , other):
         self .val  + =  other
         return  self
         
=  Number( 5 )
+ =  1
+ =  1
print (x.val)
 
class  Number:
     def  __init__( self , val):
         self .val  =  val
         
     def  __add__( self , other):
         return  Number( self .val  +  other)
         
=  Number( 5 )
+ =  1
+ =  1
print (x.val)

Call表达式:__call__

当调用类实例时执行__call__方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class  Callee:
     def  __call__( self * args,  * * kwargs):
         print ( 'Callee:' , args, kwargs)
         
=  Callee()
C( 1 2 3 )
C( 1 2 3 , x = 1 , y = 2 , z = 3 )
 
class  Prod:
     def  __init__( self , value):
         self .value  =  value
         
     def  __call__( self , other):
         return  self .value  *  other
         
=  Prod( 3 )
print (x( 3 ))
print (x( 4 ))

比较:__lt__,__gt__和其他方法

类可以定义方法来捕获所有的6种比较运算符:<、>、<=、>=、==和!=

1
2
3
4
5
6
7
8
9
10
11
12
class  C:
     data  =  'spam'
     
     def  __gt__( self , other):
         return  self .data > other
         
     def  __lt__( self , other):
         return  self .data < other
         
=  C()
print (x >  'han' )
print (x <  'han' )

布尔值测试:boollen

1
2
3
4
5
6
7
8
9
10
11
12
13
class  Truth:
     def  __bool__( self ):
         return  True
         
=  Truth()
if  X:  print ( 'yes' )
 
class  Truth:
     def  __bool__( self ):
         return  False
         
=  Truth()
print ( bool (X)

如果没有这个方法,Python退而求其次的求长度,因为一个非空对象看作是真:

1
2
3
4
5
6
7
>>>  class  Truth:
...    def  __len__( self ):  return  0
... 
>>> X  =  Truth()
>>>  if  not  X:  print ( 'no' )
... 
no

如果两个方法都有,__bool__会胜过__len__

1
2
3
4
5
6
7
>>>  class  Truth:
...    def  __bool__( self ):  return  True
...    def  __len__( self ):  return  0
... 
>>> X  =  Truth()
>>>  bool (X)
True

如果两个方法都没有定义,对象毫无疑义的看作为真:

1
2
3
4
>>>  class  Truth:  pass
... 
>>>  bool (Truth)
True

对象解析函数:__del__

每当实例产生时,就会调用init构造函数,每当实例空间被收回时,它的对立面__del__,也就是解析函数,就会自动执行;

1
2
3
4
5
6
7
8
9
10
class  Life:
     def  __init__( self , name = 'unknown' ):
         print ( 'Hello, ' , name)
         self .name  =  name
         
     def  __del__( self ):
         print ( 'Goodbye' self .name)
         
brian  =  Life( 'Brian' )
brian  =  'loretta'









本文转自 Edenwy  51CTO博客,原文链接:http://blog.51cto.com/edeny/1924863,如需转载请自行联系原作者
目录
相关文章
|
2月前
|
Java 程序员 C++
Python 面向对象详解!
本文详细介绍了Python中的面向对象编程(OOP),包括类、对象、继承、封装、多态和抽象等核心概念。通过具体示例,解释了如何使用类定义对象的属性和方法,以及如何通过继承实现代码重用。文章还探讨了封装和多态的重要性,并介绍了私有属性和抽象类的使用方法。最后,总结了OOP的四大支柱:封装、抽象、继承和多态,强调了这些概念在Python编程中的应用。适合Java程序员扩展Python编程知识。
75 2
|
28天前
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
138 45
|
4月前
|
Python
你真的会面向对象吗!解密Python“魔术方法”
你真的会面向对象吗!解密Python“魔术方法”
42 0
|
2月前
|
Python
Python面向对象(2)
【10月更文挑战第14天】
Python面向对象(2)
|
2月前
|
设计模式 程序员 C语言
Python面向对象
【10月更文挑战第13天】
Python面向对象
|
29天前
|
安全 数据库 开发者
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第26天】本文详细介绍了如何在Django框架下进行全栈开发,包括环境安装与配置、创建项目和应用、定义模型类、运行数据库迁移、创建视图和URL映射、编写模板以及启动开发服务器等步骤,并通过示例代码展示了具体实现过程。
43 2
|
6月前
|
Python
Python进阶第一篇(Python的面向对象)
Python进阶第一篇(Python的面向对象)
|
2月前
|
开发者 Python
Python中的魔法方法与运算符重载
在Python的奇妙世界里,魔法方法(Magic Methods)和运算符重载(Operator Overloading)是两个强大的特性,它们允许开发者以更自然、更直观的方式操作对象。本文将深入探讨这些概念,并通过实例展示如何利用它们来增强代码的可读性和表达力。
|
3月前
|
前端开发 Python
Python编程的面向对象有哪些(二)
Python编程的面向对象(二)—类的多态
|
3月前
|
IDE Java 开发工具
Python类与面向对象
Python类与面向对象