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
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__就被调用了。
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 '''
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'>) 直显示父类,不现实父类的父类
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'>)
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__:可以限制实例对象的属性和方法,但是对类不起作用。
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')