1、什么叫魔法方法?
- 魔法方法:Python解释器自动给出默认的,是可以给你的类增加魔力的特殊方法。如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用(不重载则会使用默认的)。你可以根据需求,重写这些方法去定义自己想要的行为,而这一切都是自动发生的。
- 魔法方法经常是两个下划线包围来命名的(比如
__init__
,__del__
) - 函数与方法的区别:
- 魔法方法是针对class而言的,脱离了”类“谈magic_method是没有意义的
- 网上的一些魔法方法详解:http://blog.csdn.net/koko66/article/details/42709279,可以学习一下
2、__init__
(self[,...])(构造器,注意前后都是双下划线)
-
只要实例化一个类,该方法在创建对象时会被自动调用
#实例化创建一个对象,系统会自动调用了__init__方法 >>>wangwu = Person('王五','男','16') >>>wangwu.sheep() 王五:不嗨了,早睡早起身体棒!
可在类里初始化好name,sex,age三个值,此时实例化Person时可不用传参,直接用默认的值。
class Person1():#定义一个Person1类 def __init__(self): self.name='旺仔牛奶' #默认name >>>a=Person1() >>>a.name '旺仔牛奶'
若无初始化具体的值,实例化Person时又没有传入参数,则会报错。
>>>lisi = Person() Traceback (most recent call last): File "
", line 1, in TypeError: __init__() missing 3 required positional arguments: 'name', 'sex', and 'age' -
小结:
1、根据“需求”(需要初始化时,如上面的Person类,需要初始化name,sex,age)才去重写
__init__
方法2、有了
__init__
方法,在创建实例的时候,必须传入与__init__
方法匹配的参数,但self
不需要传,Python解释器自己会把实例变量传进去3、该方法的返回值为None,不能试图让其返回其他值!
class A: def __init__(self): return 'A' >>>a = A()#会报错 Traceback (most recent call last): File "
", line 1, in TypeError: __init__() should return None, not 'str'
3、__new__
(cls[, ...])
-
实例化调用的第一个方法,它只取下cls参数(类参数),并把其他参数传给
__init__
,这个方法不常去重写,但也有适用场景:继承一个不可变的类型,如元组、字符串,但又需去修改值时,可重写该方法。#全部转为大写字母 class CapStr(str): #继承str类 def __new__(cls,string): #重写__new__方法,在初始化时就将传入字符串全部转为大写 string = string.upper() return str.__new__(cls,string) #返回一个类对象str的__new__方法,参数是修改为大写的string >>>a = CapStr("abcd") ABCD
4、__del__
(self[, ...])
-
当对象将要被销毁时,此方法被自动调用,类似于析构器!注:是当已经没有任何变量引用该对象时,python的垃圾回收机制会将其自动销毁,此时才会去调用
__del__
方法!del x ==x.__del__() 是错误的!
class C: def __init__(self): print('我是__inti__方法,我被调用了') def __del__(self): print('我是__del__方法,我被调用了') >>>a = C() #__inti__方法在对象创建时就会被自动调用 我是__inti__方法,我被调用了 >>>b=a >>>c=b >>>del c #此时不会调用__del__方法 >>>del b >>>del a #所有对a的引用都被del后,__del__才会被调用 我是__del__方法,我被调用了
注意:当解释器退出的时候,如果对象还存活着,这里不能确保
__del__
一定会被执行!所以__del__
不能替代一些良好的编程习惯(比如连接用完了将其关掉)!
5、算术运算魔法方法
-
当对象进行算术操作时,即触发对应的算术魔法方法
1、
__add__
(self, other), 定义加法行为 : +2、
__sub__
(self, other) ,定义减法行为 :-3、
__mul__
(self, other) ,定义乘法行为 :*4、
__truediv__
(self, other) ,定义真除法行为 :/(算数运算,反运算,增量赋值运算,一元操作符等其他的方法见上面给出的魔法方法详解链接)
class New_int1(int):#继承int类 pass >>> a = New_int1(3) >>> b = New_int1(4) >>> a+b #加号行为,继承int类,则会去调用int中的__add__(self, other)魔法方法 7 >>> a-b -1 ------------------------------------------------------------------------------------ class New_int2(int): #继承int类 def __add__(self,other): #重写加法行为,令New_int类中的加法运算来做int中的减法运算 return int.__sub__(self,other) def __sub__(self,other): return int.__add__(self,other) >>> a = New_int2(3) >>> b = New_int3(4) >>> a+b -1 >>> a-b 7
6、属性访问控制
-
__getattr__
(self, name):定义当用户试图访问一个不存在的属性时的行为class Test(object): def __init__(self,city): self.city = city def __getattr__(self,name): print('don\'t have the attribute:%s'%name) >>> a = Test('广州') >>> a.city '广州' >>> a.name don't have the attribute:name #这里并没有name属性,在找不到属性的情况下,正常的继承object的对象都会抛出AtrributeError的错误。但是这里通过__getattr__魔法方法改变了找不到属性时候的类的行为。输出了查找的属性的参数。
-
__getattribute__
(self, name): 定义当该类的属性被访问时的行为(拦截所有的属性,包括存在的属性)。注意:避免"无限递归"的错误(返回时最好用调用基类的方法)class Test(object): def __init__(self,city): self.city = city def __getattribute__(self,name): print('__getattribute__:%s'%name) return name >>> a = Test('广州') >>> a.city #__getattribute__会拦截所有的属性,包括已存在的。 __getattribute__:city '广州' >>> a.name __getattribute__:name 'name'
-
__setattr__
(self, name,value):定义当一个属性被设置时的行为。不管对象的某个属性是否存在,它都允许你为该属性进行赋值。注意:避免"无限递归"的错误(返回时最好用调用基类的方法)class Test(object): def __init__(self,city): self.city = city def __setattr__(self,name,value): return super().__setattr__(name,value) #调用基类的__setattr__方法(推荐) >>> a = Test('广州') >>> a.name ='小茗童鞋' >>> a.name '小茗童鞋' -------------------------------------------------------------------------------------------- #若在 __setattr__不调用基类方法,可以采用给对象的特殊属性__dict__赋值的方法(__dict__会以字典形式显示当前对象的所有属性和对应的值) >>> a.__dict__ {'city': '广州', 'name': '小茗童鞋'} class Test(object): def __init__(self,city): self.city = city def __setattr__(self,name,value): self.__dict__[name] = value >>> b = Test('深圳') >>> b.sex = '男' >>> b.sex '男' >>> b.__dict__ {'city': '深圳', 'sex': '男'} -------------------------------------------------------------------------------------------- #无限递归例子: class Test(object): def __init__(self,city): self.city = city def __setattr__(self,name,value): self.name = value #执行初始化方法self.city = city的时候,就会调用__setattr__方法,而这里的__setattr__方法里面的self.name = value又会调用自身,所以导致无限递归。故使用该魔法方法的时候要特别注意! >>> a = Test('广州') Traceback (most recent call last): File "
", line 1, in a = Test('广州') File " ", line 3, in __init__ self.city = city File " ", line 5, in __setattr__ self.name = value RecursionError: maximum recursion depth exceeded while calling a Python object -
__delattr__
(self, name):定义当一个属性被删除时的行为。注意:避免"无限递归"的错误(返回时最好用调用基类的方法)class Test(object): def __init__(self,city): self.city = city def __delattr__(self,name): print('__delattr__:%s'%name) return super().__delattr__(name) #调用基类的__delattr__方法(推荐) >>> a = Test('广州') >>> del a.city __delattr__:city >>> a.city Traceback (most recent call last): File "
", line 1, in a.city AttributeError: 'Test' object has no attribute 'city' -
property(fget=None, fset=None, fdel=None, doc=None):是一个类,主要功能是为了方便类内部函数的调用(使得在外部不用调用类方法,可以直接以属性方式操作)
class C: def __init__(self,size=10): self.size = size def getSize(self): return self.size def setSize(self,value): self.size = value def delSize(self): del self.size x = property(getSize,setSize,delSize) #通过property函数,使得x与size直接挂钩 >>> c = C() >>> c.x = 1 >>> c.x 1 >>> c.size 1 >>> del c.x >>> c.size Traceback (most recent call last): File "
", line 1, in c.size AttributeError: 'C' object has no attribute 'size'
7、描述符(property的原理)
描述符就是将某种特殊类型的类(类里含有下述魔法方法之一)的实例指派给另一个类的属性。
1、__get__
(self, instance, owner):用于访问属性,它返回属性的值
2、__set__
(self, instance, value):将在属性分配操作中调用,不返回任何内容
3、__delete__
(self, instance):控制删除操作,不返回任何内容
class MyDecriptor:#称为描述符类
def __get__(self,instance,owner):
print("geting...",self,instance,owner)
def __set__(self,instance,value):
print("seting...",self,instance,value)
def __delete__(self,instance):
print("deleting...",self,instance)
class Test:
x = MyDecriptor() #此处说明MyDecriptor就是x的描述符,类似property使用
>>> test = Test()
>>> test.x #调用__get__方法
geting... <__main__.MyDecriptor object at 0x0000000003570FD0> <__main__.Test object at 0x0000000003570F98>
#此处self:描述符类本身的一个实例 <__main__.MyDecriptor object at 0x0000000003570FD0>
#instance:拥有者类的实例 <__main__.Test object at 0x0000000003570F98>
#owner:拥有者类本身
>>> test.x ='哈哈哈' #调用__set__方法
seting... <__main__.MyDecriptor object at 0x0000000003570FD0> <__main__.Test object at 0x0000000003570F98> 哈哈哈
>>> del test.x #调用__delete__方法
deleting... <__main__.MyDecriptor object at 0x0000000003570FD0> <__main__.Test object at 0x0000000003570F98>
------------------------------------------------------------------------------------------------
#定义一个自己的property类
class MyProperty:#称为描述符类
def __init__(self,fget=None,fset=None,fdel=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self,instance,owner):
return self.fget(instance)
def __set__(self,instance,value):
self.fset(instance,value)
def __delete__(self,instance):
self.fdel(instance)
class C:
def __init__(self):
self._x = None
def getX(self):
return self._x
def setX(self,value):
self._x = value
def delX(self):
del self._x
x = MyProperty(getX,setX,delX)
>>> c = C()
>>> c.x = '哈哈' #直接以访问属性形式调用描述类MyProperty
>>> c.x
'哈哈'
>>> c._x
'哈哈'
>>> del c.x
>>> c._x
Traceback (most recent call last):
File "", line 1, in
c._x
AttributeError: 'C' object has no attribute '_x'
8、容器类型-魔法方法
1、容器是对数据的封装
2、python的容器类型分为可变类型(如list、dict)和不可变类型(如string、tuple)。可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。
魔法方法 | 定义 | 备注 |
---|---|---|
__len__(self) |
求容器的大小(注意与capacity的区别) | 可变和不可变容器均具备 __len__ 和 __getitem__ |
__getitem__ (self, key) |
获取容器中指定元素的行为 | |
__setitem__ (self, key, value) |
设置容器中指定元素的行为 | 只有可变容器拥有 __setitem__ 和 __delitem__ |
__delitem__ (self, key) |
删除容器中指定元素的行为 | |
__iter__ (self) |
定义迭代器中元素的行为 | |
__reversed__ (self) |
当调用reversed()函数时 | 仅当序列可以是有序的时候实现它,例如对于列表或者元组。 |
__contains__ (self, item) |
成员运算符in/ not in的行为 |
class FunctionalList:
''' 实现了内置类型list的功能,并丰富了一些其他方法: head, tail, init, last, drop, take'''
def __init__(self, values=None):
if values is None:
self.values = []
else:
self.values = values
def __len__(self):
return len(self.values)
def __getitem__(self, key):
return self.values[key]
def __setitem__(self, key, value):
self.values[key] = value
def __delitem__(self, key):
del self.values[key]
def __iter__(self):
return iter(self.values)
def __reversed__(self):
return FunctionalList(reversed(self.values))
def append(self, value):
self.values.append(value)
def head(self):
# 获取第一个元素
return self.values[0]
def tail(self):
# 获取第一个元素之后的所有元素
return self.values[1:]
def init(self):
# 获取最后一个元素之前的所有元素
return self.values[:-1]
def last(self):
# 获取最后一个元素
return self.values[-1]
def drop(self, n):
# 获取所有元素,除了前N个
return self.values[n:]
def take(self, n):
# 获取前N个元素
return self.values[:n]
>>> a = FunctionalList([1,2,7,10,3])
>>> a.head()
1
>>> a.take(2)
[1, 2]
>>> a.init()
[1, 2, 7, 10]
>>> a.tail()
[2, 7, 10, 3]
>>> b = iter(a) #调用迭代器行为
>>> for i in b:
print(i)
1
2
7
10
3
>>> from collections import Iterator #可以使用isinstance()判断一个对象是否是Iterator对象
>>> isinstance(b, Iterator)
True
9、迭代器--魔法方法
1、可迭代对象Iterable:可以直接作用于for循环的对象统称为可迭代对象,使用isinstance()判断一个对象是否是Iterable对象。
>>> from collections import Iterable
>>> isinstance({}, Iterable)
True
>>> isinstance('hhh', Iterable)
True
>>> isinstance(10, Iterable)
False
2、迭代器Iterator:可以被next()函数调用并不断返回下一个值的对象称为迭代器,使用isinstance()判断一个对象是否是Iterator对象。
- 内置函数:iter(),next()
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
#生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
#把list、dict、str等Iterable变成Iterator可以使用iter()函数
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
- 魔法方法:
__iter__
(self),__next__
(self)
>>> string = 'abcd'
>>> it = iter(string)
#每次调用next(it),就计算出it的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
'd'
>>> next(it)
Traceback (most recent call last):
File "", line 1, in
next(it)
StopIteration
--------------------------------------------------------------------------------------------
#定义一个斐波那契迭代器
class Fibs:
def __init__(self,n=10):
self.a = 0
self.b = 1
self.n = n
def __iter__(self):
return self #返回迭代器本身
def __next__(self):
self.a,self.b = self.b,self.a+self.b
if self.a > self.n:#控制迭代数量
raise StopIteration
else:
return self.a
>>> fibs = Fibs()
>>> for each in fibs:
print(each)
1
1
2
3
5
8
10、__call__
(self,name)
任何类,只需要定义一个__call__()
方法,就可以直接对实例(对象)进行调用。
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __call__(self, friend):
print('My name is %s...' % self.name)
print('My friend is %s...' % friend)
>>> p = Person('Bob', 'male')
>>> p('Tim') #直接对实例进行调用,即是把这个类型的对象当作函数来使用
My name is Bob...
My friend is Tim...
#对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象。
如何判断一个变量是对象还是函数?(即判断一个对象是否能被调用,能被调用的对象就是一个Callable
对象)
#用callabel()函数判断
>>> callable(Person('Bob', 'male'))
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
11、__str__
与__repr__
__str__()
返回用户看到的字符串,__repr__()
返回程序开发者看到的字符串,即__repr__()
是为调试服务的。
class Animal(object):
def __init__(self,name):
self.name = name
>>> print(Animal('Cat')) #打印出来不好看
<__main__.Animal object at 0x0000000003558978>
#改进
class Animal(object):
def __init__(self,name):
self.name = name
def __str__(self): #定义一个__str__()方法,返回一个好看的字符串
return 'Animal object (name: %s)' % self.name
>>> print(Animal('Cat'))
Animal object (name: Cat)
#但若不用print,打印出来的仍是不好看,因为直接显示变量调用的不是__str__(),而是__repr__()
>>> a = Animal('Cat')
>>> a
<__main__.Animal object at 0x0000000003558C18>
#解决:再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的
class Animal(object):
def __init__(self,name):
self.name = name
def __str__(self):
return 'Animal object (name: %s)' % self.name
__repr__ = __str__
>>> print(Animal('Cat'))
Animal object (name: Cat)
>>> a = Animal('Cat')
>>> a
Animal object (name: Cat)