4.1.7 特殊方法与运算符重载

  Python的类有大量的特殊方法,其中比较常见的是构造函数和析构函数。Python中类的构造函数是__init__(),一般用来为数据成员设置初始值或进行其他必要的初始化工作,在创建对象时被自动调用和执行。如果用户没有设计构造函数,Python将提供一个默认的构造函数用来进行必要的初始化工作。Python中类的析构函数是__del__(),一般用来释放对象占用的资源,在Python删除对象和回收对象空间时别自动调用和执行。如果用户没有编写析构函数,Python将提供一个默认的析构函数进行必要的清理工作。

  在Python中,除了构造函数和析构函数之外,还有大量的特殊方法支持更多的功能,例如,运算符重载就是通过在类中重写特殊函数来实现的。在自定义类时如果重写了某个特殊方法即可支持对应的运算符,具体实现了什么工作则完全可以根据需要来定义。

                          Python类特殊方法

方法 功能说明
__new__()  类的静态方法,用于确定是否要创建对象
__init__()  构造函数,生成对象时调用
__del__()  析构函数,释放对象时调用 
__add__()  +
__sub__()  -
__mul__()  *
__truediv__()  /
__floordiv__()  //
__mod__()  %
__pow__()  **
__repr__()  打印、转换
__setitem__()  按照索引赋值
__getitem__()  按照索引获取值
__len__()  计算长度
__call__()  函数调用
__contains__()  in
__eq__()、__ne__()、__lt__()、__le__()、__gt__()、__ge__()  ==、!=、<、<=、>、>=
__str__()  转化为字符串
___lshift__()、__rshift__()  <<、>>
__and__()、__or__()、__invert__()、__xor__()  &、|、~、^
__iadd__()、__isub__()  +=、-=

1、__init__:构造初始化函数,在创建实例对象为其赋值时使用,在__new__之后,__init__必须至少有一个参数self,就是这个__new__返回的实例,__init__是在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值。

2、__new__:创建并返回一个实例对象,如果__new__只调用了一次,就会得到一个对象。继承自object的新式类才有__new__这一魔法方法,__new__至少必须要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供,__new__必须要有返回值,返回实例化出来的实例(很重要),这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例,若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行。__new__是唯一在实例创建之前执行的方法,一般用在定义元类时使用。

创建对象的步骤:

a、首先调用__new__得到一个对象

b、调用__init__为对象添加属性

c、将对象赋值给变量。

 1 class A(object):
 2     pass
 3
 4 class B(A):
 5     def __init__(self):
 6         print(‘__init__被调用...‘)
 7
 8     def __new__(cls):
 9         print(‘__new__被调用...‘)
10         print(id(cls))
11         return object.__new__(A)  #注意在此处采用了参数A而不是cls,__new__没有正确返回当前类cls的实例
12
13 ‘‘‘
14 从运行结果可以看出,__new__中的参数cls和B的id是相同的,表明__new__中默认的参数cls就是B类本身,而在return时,
15 并没有正确返回当前类cls的实例,而是返回了其父类A的实例,因此__init__这一魔法方法并没有被调用,
16 此时__new__虽然是写在B类中的,但其创建并返回的是一个A类的实例对象。
17 ‘‘‘

3、__class__:获得已知对象的类 ( 对象.__class__)。

1 class A:
2     count = 0
3     def addcount(self):
4         self.__class__.count += 1

4、__str__:在将对象转换成字符串  str(对象)  测试的时候,打印对象的信息,__str__方法必须要return一个字符串类型的返回值,作为对实例对象的字符串描述,__str__实际上是被print函数默认调用的,当要print(实例对象)时,默认调用__str__方法,将其字符串描述返回。如果不是要用str()函数转换。当你打印一个类的时候,那么print首先调用的就是类里面的定义的__str__。

 1 class A:
 2     def __init__(self,name):
 3         self.name = name
 4
 5     def __str__(self):
 6         return ‘我是A类的实例对象a,我的名字叫{}‘.format(self.name)
 7
 8 if __name__ == ‘__main__‘:
 9
10     a = A(‘老王‘)
11     print(A)  #<class ‘__main__.A‘>
12     print(a)  #我是A类的实例对象a,我的名字叫老王
13
14     #__str__方法是不会被调用的,而print(a)的时候,_str__就被调用了。

可以看出,直接敲a的话,__str__方法是不会被调用的,而print(a)的时候,__str__就被调用了。

5、__repr__:如果说__str__体现的是一种可读性,是给用户看的,那么__repr__方法体现的则是一种准确性,是给开发人员看的,它对应的是repr()函数,重构__repr__方法后,在控制台直接敲出实例对象的名称,就可以按照__repr__中return的值显示了。

 1 class A:
 2     def __init__(self,name):
 3         self.name = name
 4
 5     def __str__(self):
 6         return ‘我是A类的实例对象a,我的名字叫{}‘.format(self.name)
 7
 8     def __repr__(self):
 9         return ‘哈哈,我是A的实例对象a!‘
10
11 if __name__ == ‘__main__‘:
12
13     a = A(‘老王‘)
14     print(A)  #<class ‘__main__.A‘>
15     print(a)  #我是A类的实例对象a,我的名字叫老王
16     print(repr(a))
17
18 ‘‘‘
19 打印操作会首先尝试__str__和str内置函数(print运行的内部等价形式),它通常应该返回一个友好的显示。
20
21 __repr__用于所有其他的环境中:用于交互模式下提示回应以及repr函数,它通常应该返回一个编码字符串,可以用来重新创建对象,或者给开发者详细的显示。
22
23 当我们想所有环境下都统一显示的话,可以重构__repr__方法;当我们想在不同环境下支持不同的显示,例如终端用户显示使用__str__,而程序员在开发期间则使用底层的__repr__来显示,实际上__str__只是覆盖了__repr__以得到更友好的用户显示。
24
25 ‘‘‘

6、__del__:对象在程序运行结束之后进行垃圾回收的时候调用这个方法,来释放资源。此时,此方法是被自动调用的。除非有特殊要求,一般不要重写。在关闭数据库连接对象的时候,可以在这里,释放资源。

 1 class NewClass(object):
 2     num_count = 0  # 所有的实例都共享此变量,即不单独为每个实例分配
 3     def __init__(self,name):
 4         self.name = name
 5         NewClass.num_count += 1
 6         print(name,NewClass.num_count)
 7
 8     def __del__(self):
 9         NewClass.num_count -= 1
10         print(‘Del‘,self.name,NewClass.num_count)
11
12     def test(self):
13         print(‘aa‘)
14
15 if __name__ == ‘__main__‘:
16     #aa = NewClass(‘Hello‘)
17     #bb = NewClass(‘World‘)
18     #cc = NewClass(‘aaaa‘)
19     #print(‘Over‘)
20     pass
21
22 #可以看出在程序运行结束之后,__del__默认被调用了三次,分别对实例对象aa,bb,cc进行垃圾回收,因为此时创建的实例已经没有对象再指向它了。
23
24 import time
25
26 class Animal:
27     def __init__(self,name):
28         print(‘__init__方法被调用‘)
29
30     def __del__(self):
31         print(‘__del__ ......‘)
32
33 wangcai = Animal(‘旺财‘)
34 xiaoqiang = wangcai
35
36 del wangcai
37 print(‘*‘ * 50)
38 del xiaoqiang
39 time.sleep(2)
40 print(‘over......‘)
41
42 #__init__方法被调用
43 #**************************************************
44 #__del__ ......
45 #over......
46
47 ‘‘‘可以看出,wangcai和xiaoqiang指向的是同一个实例对象,在del wangcai的时候,__del__并没有被调用,因为此时这个对象还在被xiaoqiang引用着,当del xiaoqiang的时候,__del__就默认被调用了,因为此时没有变量再引用这个实例对象了,相当于其引用计数变为0了,这个对象理所当然就会被垃圾回收。
48
49 总而言之,__del__魔法方法是在对象没有变量再引用,其引用计数减为0,进行垃圾回收的时候自动调用的。
50
51 ‘‘‘

7、__getattribute__:属性访问拦截器,在访问实例属性时自动调用。在python中,类的属性和方法都理解为属性,且均可以通过__getattribute__获取。当获取属性时,相当于对属性进行重写,直接return object.__getattribute__(self, *args, **kwargs)或者根据判断return所需要的重写值,如果需要获取某个方法的返回值时,则需要在函数后面加上一个()即可。如果不加的话,返回的是函数引用地址。

 1 class Test(object):
 2     def __init__(self,subject1):
 3         self.subject1 = subject1
 4         self.subject2 = ‘cpp‘
 5
 6     def __getattribute__(self,obj):
 7         if obj == ‘subject1‘:
 8             return ‘redirect python‘
 9         else:
10             return object.__getattribute__(self,obj)
11
12
13 if __name__ == ‘__main__‘:
14     s = Test(‘Python‘)
15     print(s.subject1)
16     print(s.subject2)
17
18 #redirect python
19 #cpp
20
21 ‘‘‘
22 在创建实例对象s并对其初始化的时候,subject1的值设置为‘python’,subject2的值设置为‘cpp’,
23 在访问s的subject1属性时,因为Test类对object类中的__getattribute__方法进行了重写,
24 所以在调用此方法时,首先对要访问的属性做一个拦截和判断,
25 此时__getattribute__方法中的参数obj对应的是要访问的属性,若要访问subject1属性,
26 则对该属性进行重写,返回了一个不同的字符串,我们可以看到,在初始化时,
27 subject1 的值为‘python’,而在访问subject1这个属性时,返回的值是‘redirect python‘,
28 而在访问subject2时,则调用其父类中的__getattribute__方法,返回正常的subject2属性的值。
29 当然,在访问类的方法属性时,也可以通过重写__getattribute__的方法对其进行重写。
30 ‘‘‘

8、__bases__:获取指定类的所有父类构成元素,使用方法为类名.__bases__

 1 class A:
 2     pass
 3
 4 class B(A):
 5     pass
 6
 7 class C():
 8     pass
 9
10 class D(B,C):
11     pass
12
13 print(D.__bases__)
14
15 #(<class ‘__main__.B‘>, <class ‘__main__.C‘>)   直显示父类,不现实父类的父类

9、__mro__:显示指定类的所有继承脉络和继承顺序,假如这个指定的类不具有某些方法和属性,但与其有血统关系的类中具有这些属性和方法,则在访问这个类本身不具有的这些方法和属性时,会按照__mro__显示出来的顺序一层一层向后查找,直到找到为止。

 1 class A:
 2     pass
 3
 4 class B(A):
 5     pass
 6
 7 class C():
 8     pass
 9
10 class D(B,C):
11     pass
12
13 print(D.__mro__)
14
15 # (<class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.A‘>,       <class ‘__main__.C‘>, <class ‘object‘>)

10、__call__:具有__call__魔法方法的对象可以使用XXX()的形式被调用,比如说类的实例对象

 1 class Dog(object):
 2
 3     def __init__(self):
 4         print(‘__init__被调用......‘)
 5
 6     def __call__(self):
 7         print(‘__call__被调用......‘)
 8
 9 #wangcai = Dog()
10 #wangcai()
11
12 ‘‘‘
13 __init__被调用......
14 Traceback (most recent call last):
15   File "C:/Users/dddd/PycharmProjects/untitled5/test1.py", line 8, in <module>
16     wangcai()
17 TypeError: ‘Dog‘ object is not callable
18 ‘‘‘
19 ‘‘‘
20 可以看到,Dog类的实例对象laowang是不可以使用laowang()的方式进行调用的,   因为其没有__call__魔法方法,进行了修改之后,   laowang这个实例对象就可以使用()的方式被调用了:
21 ‘‘‘
22
23 #然后为Dog类重写 __call__函数
24 wangcai = Dog()
25 wangcai()
26
27 #__init__被调用......
28 #__call__被调用......

11、魔法属性:__stlos__:可以限制实例对象的属性和方法,但是对类不起作用。

12、__all__:将一个py文件作为模块导入时,其中if __name__ == "main"以上的类、方法、函数等都能被导入,但某些方法可能只是用来做测试用的,不希望也不建议被导入,可以用__all__=[‘函数名或方法名‘]的方式限制一下哪些函数或方法可以被导入,即[]中的函数名或方法名可以被导入。但是需要强调的是,__all__魔法方法只针对通过 from xx import *这种导入方式有效

 1 #自定义一个脚本
 2 __all__ = [‘a‘,‘b‘]
 3
 4 def a():
 5     print(‘a‘)
 6
 7 def b():
 8     print(‘b‘)
 9
10 def c():
11     print(‘c‘)
12
13 if __name__ == ‘__main__‘:
14     print(‘hello 老王‘)
15     print(‘123‘)

https://www.jianshu.com/p/3f4786b33f34

原文地址:https://www.cnblogs.com/avention/p/8642192.html

时间: 2024-08-26 19:28:05

4.1.7 特殊方法与运算符重载的相关文章

自动类型转换之运算符重载

第二种自动类型转换的方法是运算符重载,其是形式是operator 目的类型();   这个函数通过在关键字operator后跟随想要转换到的类型的方法,将当前类型转换为希望的类型.这种形式的运算符重载是独特的,因为没有指定返回值类型,其返回值类型就是正在重载运算符的名字. 1 #include<iostream> 2 using namespace std; 3 4 class Three{ 5 int i; 6 public: 7 Three(int ii=0) :i(ii){ 8 9 }

Kotlin中复合赋值(+=,-=,……)运算符重载

本篇建立在已经了解了kotlin中运算符重载的理念以及如何实现的基础上. 来我们首先写一个简单的类,然后重载运算符+,+=,-,-=这个几个运算符.代码如下: data class Point(var x: Int, var y: Int) { operator fun plus(point: Point): Point { return Point(this.x + point.x, this.y + point.y) } operator fun plusAssign(point: Poin

第十章 运算符重载

第十章 运算符重载 1.运算符重载的方法 (实质:函数重载) *一般格式: 函数类型名operator 运算符名称(参数表){函数体:} 2.运算符重载的规则 *不允许定义新的运算符 *允许重载的运算符 双目运算符 +(加法)  - (减法)  * (乘法) / (除法)   %(取模) 关系运算符 ==(等于) != (不等)  <   >   <=   >= 逻辑运算符 ||(或)   && (与)  !(非) 单目运算符 + (正)  -(负)   *(指针)

C++运算符重载的妙用

运算符重载(Operator overloading)是C++重要特性之中的一个,本文通过列举标准库中的运算符重载实例,展示运算符重载在C++里的妙用.详细包含重载operator<<,operator>>支持cin,cout输入输出.重载operator[],实现下标运算.重载operator+=实现元素追加:重载operator()实现函数调用.假设你对C++的运算符重载掌握的游刃有余.那就无需继续往下看了. 运算符重载带来的优点就是--让代码变得简洁.以下将展示几个标准库因使

C++:运算符重载函数之&quot;++&quot;、&quot;--&quot;、&quot;[ ]&quot;的应用

5.2.5 "++"和"--"的重载 对于前缀方式++ob,可以用运算符函数重载为: ob.operator++() //成员函数重载 或 operator++(X &ob) //友元函数重载,其中ob为类X的对象的引用 对于后缀方式++ob,可以用运算符函数重载为: ob.operator++(int) //成员函数重载 或 operator++(X &ob,int) //友元函数重载,其中ob为类X的对象的引用 调用时,参数int一般被传递给值0

C++:运算符重载函数之成员运算符重载函数

5.2.3 成员运算符重载函数 在C++中可以把运算符重载函数定义为某个类的成员函数,称之为成员运算符重载函数. 1. 定义成员运算符重载函数的语法形式 (1)在类的内部,定义成员运算符重载函数的格式如下: 函数类型 operator 运算符(形参表) {       函数体 } (2)成员运算符重载函数也可以在类中声明成员函数的原型,在类外定义. 在类的内部,声明成员运算符重载函数原型的格式如下: class X{      ...      函数类型 operator运算符(参数表); };

【C++】运算符重载

这是C++的特色,也是C++比较难的一个基本语法,但是,如果你把运算符重载看成一个特殊的类方法,且这个类方法的名称就是运算符,一切就变得简单了. 下面用一个简单的程序说明这个问题: 声明两个3x3二维矩阵a,b,其实就是Matrix类,里面就用私有的长度为9的一维数组存储数据.只是打印的时候,打成二维数组的样子 实现~a或者~b就能直接打印a与b,也就是~相当于把a,b打印出来,此乃单目运算符重载. 实现a+b,返回另一个相加之后的矩阵c. 也就是说做到如下图的效果: 代码如下: #includ

C++哪些运算符重载可以重载?

运算符重载是C++极为重要的语言特性之一,本文将用代码实例回答--C++哪些运算符可以重载?如何重载?实现运算符重载时需要注意哪些? 哪些运算符可以重载,哪些不可重载? C++98,C++0x,C++11对"哪些运算符重载可以重载"有一致的规定,具体如下: 其中,很少使用的是","(逗号运算符). 标准同样规定了不可重载的运算符: 其中,"::"是作用域运算符, "?:"是条件运算符. 两个较少使用的运算符是 .* 和 -&g

scala学习手记7 - 运算符重载

从语法上来说scala是没有运算符的.之前的一节里也曾提到过scala的运算符实际上是方法名,如1 + 2实际上就是1.+(2).我们可以将之视为运算符,是因为scala的一个特性:如果方法的参数小于等于1个的话,那么"."和括号就都是可选的. scala的运算符重载指的就是重载+.-这样的符号--和C.java或者python等语言不一样,我们需要自己定义这些符号如何实现. 下面看一个"+"运算符实现的例子.这里定义了一个Complex类,Complex指的是复数