1.构造和析构
魔法方法的特点:
1.__init__(self[,...])
实例被创建时自动调用,返回值只能是None。
2.__new__(cls[,...])
它的第一个参数是这个类,而其他参数则会传递给__init__方法,所以它第一个被调用的魔法方法,返回一个实例对象,很少去重写它。
当需要修改一个不可变类型时,才去重写它。
例如:
#小写变大写
>>> class CapStr(str):#str是一个不可变类型
def __new__(cls,s):
string = s.upper()
return str.__new__(cls,string)
>>> a = CapStr('abc')
再如:
#摄氏度变华氏度
class C2F(float):
def __new__(cls,x):
y = x*1.8+32
return float.__new__(cls,y)
print(C2F(32))
3.__del__(self)
它是一个析构器,当一个类的所有实例对象被del后,才会执行__del__。
注意:并非del x就相当于自动调用x.__del__(),__del__方法是当垃圾回收机制回收这个对象的时候调用的。
例如:
>>> class B:
def __init__(self):
print('调用init方法。。。')
def __del__(self):
print('调用del方法。。。')
>>> b1 = B()
调用init方法。。。
>>> b2 = b1
>>> del b2#没有调用__del__
>>> del b1#所有引用都删除了,就调用__del__方法
调用del方法。。。
2.算术运算
对象是可以进行计算的。
>>> class A:
pass
>>> type(A)
<class 'type'>
>>> type(int)
<class 'type'>
其实int等数据类型都是类,令a = int('12'),b = int('23'),a和b是可以相加的。
由此引出算术运算。
如:当实例进行加法操作时,调用__add__魔法方法。
例如:
>>> class Nint(int):
def __add__(self,other):
return int.__sub__(self,other)
def __sub__(self,other):
return int.__add__(self,other)
>>> a = Nint(3)
>>> b = Nint(5)
上例实现了加法和减法的对换。
3.反运算魔法方法
说明如图:
例如:
>>> class int(int):
def __radd__(self,other):
print('正在运行反运算')
return int.__add__(self,other)
>>> a=int(4)
>>> 1+a
正在运行反运算
5
4.简单定制
1.预备知识
- __str__()方法
用于print实例对象时要显示的内容。
>>> class A:
def __str__(self):
return 'hello.'
>>> a = A()
>>> print(a)
hello.
- __repr__()方法
用于直接写实例对象时就显示的内容。
>>> class B:
def __repr__(self):
return 'hello!'
>>> b = B()
>>> b
hello!
2.
功能要求:
资源需求:
详见课后题,这一节没做。
5.属性访问
介绍4种魔法方法:
应用举例:
>>> class C:
def __getattribute__(self,name):
print('__getattribute__')
return super().__getattribute__(name)
def __getattr__(self,name):
print('__getattr__')
def __setattr__(self,name,value):
print('__setattr__')
super().__setattr__(name,value)
def __delattr__(self,name):
print('__delattr__')
super().__delattr__(name)
>>> c = C()
>>> c.x#当x不存在时,两个方法都调用
__getattribute__
__getattr__
>>> c.x = 1
__setattr__
>>> c.x
__getattribute__
>>> del c.x
__delattr__
注意:__getattr__方法一般不返回,否则会报错。
练习题:
- 方法1:
class Rectangle:
def __init__(self,x = 0,y = 0):
self.x = x
self.y = y
def __setattr__(self,name,value):
print('setattr')
if name == 'square':
self.x = value
self.y = value
else:
super().__setattr__(name,value)#【1】
def getArea(self):
return self.x * self.y
- 方法2:
每一个实例对象都有一个__dict__特殊属性(不是方法),如下:
>>> r2.__dict__
{'x': 12, 'y': 12}
所以可以把上面的中【1】处改为:
self.__dict__[name] = value
注意:不要用下面的写法,会出现死循环。
self.name = value
思考题:编写一个Counter类,用于实时检测对象有多少个属性。
class Counter:
def __init__(self):
super().__setattr__('counter',0)
def __setattr__(self,name,value):
print('__setattr__')
super().__setattr__('counter',self.counter + 1)
super().__setattr__(name,value)
def __delattr__(self,name):
print('__delattr__')
super().__setattr__('counter',self.counter - 1)
super().__delattr__(name)
6.描述符
什么是描述符?
例如:
>>> class MyDecriptor:
def __get__(self,instance,owner):
print('getting...')
print(self)
print(instance)
print(owner)
def __set__(self,instance,value):
print('setting...')
print(self)
print(instance)
print(value)
def __delete__(self,instance):
print('delete...')
print(self)
print(instance)
>>> class Test:
x = MyDecriptor()
>>> t = Test()
>>> t.x
getting...
<__main__.MyDecriptor object at 0x02F27770>
<__main__.Test object at 0x02F277D0>
<class '__main__.Test'>
>>> t.x = 1
setting...
<__main__.MyDecriptor object at 0x02F27770>
<__main__.Test object at 0x02F277D0>
1
>>> del t.x
delete...
<__main__.MyDecriptor object at 0x02F27770>
<__main__.Test object at 0x02F277D0>
7.定制容器
1.协议
2.实战
编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数。
class List:
def __init__(self,*args):
self.value = [x for x in args]
self.count = {}.fromkeys(range(len(self.value)),0)
def __len__(self):
return len(self.value)
def __getitem__(self,key):
self.count[key] += 1
return self.value[key]