一、魔法方法特点
- 被双上下滑线包围
- 魔法方法是面向对象的Python的一切,如果你不知道魔法方法,说明你还没能意识到面向对象的Python的强大(不是说Python脚本)
- 通过对制定方法的重写,完全可以让python根据个人的用途去实现!
二、魔法方法
1、构造相关
__init__(self [,...])构造方法
- 不写时,会默认存在一个无参的,写了会覆盖
- 必须返回None,不能写 return,否则返回TypeError;因此不要对init做返回;
__new__(cls[, ...])
- 对象实例化时调用的第一个方法,在init之前,它有个很大的不同第一个参数不是self,而是这个类cls,返回一个对象,cls后边的参数会原封不动的传给__init__()方法
- 平时极少重写,当继承一个不可变类型又需要修改的时候,需要重写new
class CapStr(str): #继承str类是不可变得
def __new__(cls, string): #重写new,第一个传入class,叫其他名字也无所谓,只是为了区分
string = string.upper() #全部变大写
return str.__new__(cls, string) 变完大写后,把它作为参数去调用基类的new方法
a = CapStr('I love Money') #得到的a为大写
__del__(self)析构
- delete缩写,当没有任何变量引用这个变量后,垃圾回收机制会自动销毁时调用它
- 并非执行del x 就立马是调用x.__del__();
2、表现类
__str__ 和print()对应
__repr__
>>> class A():
... def __str__(self): #给print()用
... return "hahaha"
...
>>> a = A()
>>> a
<__main__.A object at 0x7f85993317f0>
>>> print(a)
hahaha
打印时,返回字符串,需要return 字符串,不可以是别的类型!
__str__ = __repr__
>>> class A():
... def __repr__(self): #给obj对象用
... return "hahaha"
...
>>> a = A()
>>> a
hahaha
>>> print(a)
hahaha
>>>
3、有关算数运算的魔法方法
在python2.2之前类和类型是分开的;类是属性和方法的封装;类型是像整型、浮点型、字符串等这些;但Python2.2之后,对二者进行统一,做法是将int、float、string、list、tuple等这些bif统统转化为工厂函数
int('123')在以前是调用int函数,把参数转化为整型;现在是用参数实例化一个int对象。并且对象可以做加法!
调用__add__(),因此可以重写,但重写没有多大意义吧
下边写法会进入无线递归:
改进:
推荐第一种写法
上表中的魔法方法加上r,就是反运算符:a + b,如果a对象的add方法没有实现或者不支持相应的操作的时候,那么python就会自动找到对象b的__radd__(self, other)方法
1为什么没有__add__()方法??
在减法中,为了实现目的,可以修改为 def __radd__(self, other): return int.__sub__(other, self)
一元操作符
__neg__(self) 定义正号的行为:+x
__pos__(self) 定义符号的行为:-x
__abs__(self) 定义当被abs()调用时的行为
__invert__(self) 定义按位取反的行为:~x
4、属性访问
点;bif;
__getattr__(self, name)
- 定义当用户试图获取一个不存在的属性时的行为
__getattribute__(self, name)
- 定义当该类的属性被访问时的行为
__setattr__(self, name, value)
- 定义当一个属性被设置时的行为
__delattr__(self, name)
- 定义当一个属性被删除时的行为
>>> 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 getattribute getattr >>> c.x = 1 setattr >>> c.x getattribute 1 >>> del c.x delattr
定义一个矩形类,且修改__setattr__()方法,如果为一个叫square的属性赋值,那么说明这是一个正方形,值就是正方形的边长,宽和高等于边长。
1 class Rextangle(): 2 def __init__(self, width=0, height=0): 3 self.width = width 4 self.height = height 5 6 def __setattr__(self, name, value): 7 if name == 'square': 8 self.width = value 9 self.height = value 10 else: 11第一种 self.width = value #报错,无限递归 12 self.height = value #报错,无限递归
第二种 self.name = value #报错,无限递归
第三种 super().__setattr__(name, value) #正确(推荐)
第四种 self.__dict__[name] = value #正确 13 def getArea(self): 14 return self.width * self.height
r = Rectangle(4, 5) 初始化是,有self.widthself.height赋值,回去调用被改写了的__setattr__()方法
r.getArea() 返回20
r.square = 10
r.width r.height 返回10
r.getArea() 返回100
r.__dict__ 返回{'width':10, 'height':10} 因此会有“第四种”
5、描述符
描述符就是将某种特殊类型的类的实例指派给另一个类的属性。
- __get__(self, instance, owner) 用于访问属性时调用,它返回属性值
- __set__(self, instance, value) 将在属性分配操作中调用,不返回任何内容
- __delete__(self, instance) 控制删除操作,不返回任何内容
这三个方法跟4里的属性访问方法很相似:python中__get__,__getattr__,__getattribute__的区别:主要看例子
1 class MyDecriptor(): 2 def __get__(self, instance, owner): 3 print("getting...", self, instance, owner) 4 5 def __set__(self, instance, value): 6 print("setting...", self, instance, value) 7 8 def __delete__(self, instance): 9 print("deleting...", self, instance) In [51]: class Test(): ...: x = MyDecriptor() #将类的实例指派给Test类的属性x,此时MyDecriptor()实例里的属性、方法就是x的描述符 ...: In [52]: test = Test() #实例化Test类 In [53]: test.x #调用了描述符的__get__() getting... <yu412.MyDecriptor object at 0x7fa160356dd8> <__main__.Test object at 0x7fa1602a4f98> <class '__main__.Test'> In [54]: test #实例 Out[54]: <__main__.Test at 0x7fa1602a4f98> In [55]: Test #类 Out[55]: __main__.Test In [56]: test.x = "X-man" #调用__set__() setting... <yu412.MyDecriptor object at 0x7fa160356dd8> <__main__.Test object at 0x7fa1602a4f98> X-man In [57]: del test.x #调用__delete__() deleting... <yu412.MyDecriptor object at 0x7fa160356dd8> <__main__.Test object at 0x7fa1602a4f98>
利用以上原理可以轻松实现property()方法
练习:
先定义一个温度类,然后定义两个描述符类用于描述摄氏度和华氏度两个属性;要求两个属性会自动进行转换,也就是说你可以给摄氏度这个属性赋值,然后打印的华氏度属性是自动转换后的结果
6、定制容器
http://www.cnblogs.com/daduryi/p/6737186.html
_len__()、__getitem__()、__setitem__()和__delitem__()
__iter__()——返回迭代器本身
__next__()——决定迭代器的规则