1植阴、什么叫魔法方法?
- 魔法方法:Python解釋器自動給出默認的圾浅,是可以給你的類增加魔力的特殊方法掠手。如果你的對象實現(xiàn)(重載)了這些方法中的某一個,那么這個方法就會在特殊的情況下被 Python 所調(diào)用(不重載則會使用默認的)狸捕。你可以根據(jù)需求喷鸽,重寫這些方法去定義自己想要的行為,而這一切都是自動發(fā)生的灸拍。
- 魔法方法經(jīng)常是兩個下劃線包圍來命名的(比如
__init__
做祝,__del__
) - 函數(shù)與方法的區(qū)別:
- 魔法方法是針對class而言的,脫離了”類“談magic_method是沒有意義的
- 網(wǎng)上的一些魔法方法詳解:http://blog.csdn.net/koko66/article/details/42709279株搔,可以學習一下
2剖淀、__init__
(self[,...])(構(gòu)造器,注意前后都是雙下劃線)
-
只要實例化一個類纤房,該方法在創(chuàng)建對象時會被自動調(diào)用
#實例化創(chuàng)建一個對象纵隔,系統(tǒng)會自動調(diào)用了__init__方法 >>>wangwu = Person('王五','男','16') >>>wangwu.sheep() 王五:不嗨了,早睡早起身體棒炮姨!
可在類里初始化好name捌刮,sex,age三個值舒岸,此時實例化Person時可不用傳參绅作,直接用默認的值。
class Person1():#定義一個Person1類 def __init__(self): self.name='旺仔牛奶' #默認name >>>a=Person1() >>>a.name '旺仔牛奶'
若無初始化具體的值蛾派,實例化Person時又沒有傳入?yún)?shù)俄认,則會報錯个少。
>>>lisi = Person() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __init__() missing 3 required positional arguments: 'name', 'sex', and 'age'
-
小結(jié):
1、根據(jù)“需求”(需要初始化時眯杏,如上面的Person類夜焦,需要初始化name,sex岂贩,age)才去重寫
__init__
方法2茫经、有了
__init__
方法,在創(chuàng)建實例的時候萎津,必須傳入與__init__
方法匹配的參數(shù)卸伞,但self
不需要傳,Python解釋器自己會把實例變量傳進去3锉屈、該方法的返回值為None荤傲,不能試圖讓其返回其他值!
class A: def __init__(self): return 'A' >>>a = A()#會報錯 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __init__() should return None, not 'str'
3部念、__new__
(cls[, ...])
-
實例化調(diào)用的第一個方法弃酌,它只取下cls參數(shù)(類參數(shù)),并把其他參數(shù)傳給
__init__
,這個方法不常去重寫儡炼,但也有適用場景:繼承一個不可變的類型妓湘,如元組、字符串乌询,但又需去修改值時榜贴,可重寫該方法。#全部轉(zhuǎn)為大寫字母 class CapStr(str): #繼承str類 def __new__(cls,string): #重寫__new__方法,在初始化時就將傳入字符串全部轉(zhuǎn)為大寫 string = string.upper() return str.__new__(cls,string) #返回一個類對象str的__new__方法妹田,參數(shù)是修改為大寫的string >>>a = CapStr("abcd") ABCD
4唬党、__del__
(self[, ...])
-
當對象將要被銷毀時,此方法被自動調(diào)用鬼佣,類似于析構(gòu)器驶拱!注:是當已經(jīng)沒有任何變量引用該對象時,python的垃圾回收機制會將其自動銷毀晶衷,此時才會去調(diào)用
__del__
方法蓝纲!del x ==x.__del__() 是錯誤的!
class C: def __init__(self): print('我是__inti__方法晌纫,我被調(diào)用了') def __del__(self): print('我是__del__方法税迷,我被調(diào)用了') >>>a = C() #__inti__方法在對象創(chuàng)建時就會被自動調(diào)用 我是__inti__方法,我被調(diào)用了 >>>b=a >>>c=b >>>del c #此時不會調(diào)用__del__方法 >>>del b >>>del a #所有對a的引用都被del后锹漱,__del__才會被調(diào)用 我是__del__方法箭养,我被調(diào)用了
注意:當解釋器退出的時候,如果對象還存活著哥牍,這里不能確保
__del__
一定會被執(zhí)行毕泌!所以__del__
不能替代一些良好的編程習慣(比如連接用完了將其關掉)!
5喝检、算術運算魔法方法
-
當對象進行算術操作時,即觸發(fā)對應的算術魔法方法
1撼泛、
__add__
(self, other)蛇耀, 定義加法行為 : +2、
__sub__
(self, other) 坎弯,定義減法行為 :-3、
__mul__
(self, other) 译暂,定義乘法行為 :*4抠忘、
__truediv__
(self, other) ,定義真除法行為 :/(算數(shù)運算外永,反運算崎脉,增量賦值運算,一元操作符等其他的方法見上面給出的魔法方法詳解鏈接)
class New_int1(int):#繼承int類 pass >>> a = New_int1(3) >>> b = New_int1(4) >>> a+b #加號行為伯顶,繼承int類囚灼,則會去調(diào)用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__魔法方法改變了找不到屬性時候的類的行為蝎抽。輸出了查找的屬性的參數(shù)。
-
__getattribute__
(self, name): 定義當該類的屬性被訪問時的行為(攔截所有的屬性路克,包括存在的屬性)樟结。注意:避免"無限遞歸"的錯誤(返回時最好用調(diào)用基類的方法)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):定義當一個屬性被設置時的行為精算。不管對象的某個屬性是否存在,它都允許你為該屬性進行賦值瓢宦。注意:避免"無限遞歸"的錯誤(返回時最好用調(diào)用基類的方法)class Test(object): def __init__(self,city): self.city = city def __setattr__(self,name,value): return super().__setattr__(name,value) #調(diào)用基類的__setattr__方法(推薦) >>> a = Test('廣州') >>> a.name ='小茗童鞋' >>> a.name '小茗童鞋' -------------------------------------------------------------------------------------------- #若在 __setattr__不調(diào)用基類方法,可以采用給對象的特殊屬性__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 #執(zhí)行初始化方法self.city = city的時候灰羽,就會調(diào)用__setattr__方法驮履,而這里的__setattr__方法里面的self.name = value又會調(diào)用自身,所以導致無限遞歸谦趣。故使用該魔法方法的時候要特別注意疲吸! >>> a = Test('廣州') Traceback (most recent call last): File "<pyshell#36>", line 1, in <module> a = Test('廣州') File "<pyshell#35>", line 3, in __init__ self.city = city File "<pyshell#35>", line 5, in __setattr__ self.name = value RecursionError: maximum recursion depth exceeded while calling a Python object
-
__delattr__
(self, name):定義當一個屬性被刪除時的行為。注意:避免"無限遞歸"的錯誤(返回時最好用調(diào)用基類的方法)class Test(object): def __init__(self,city): self.city = city def __delattr__(self,name): print('__delattr__:%s'%name) return super().__delattr__(name) #調(diào)用基類的__delattr__方法(推薦) >>> a = Test('廣州') >>> del a.city __delattr__:city >>> a.city Traceback (most recent call last): File "<pyshell#73>", line 1, in <module> a.city AttributeError: 'Test' object has no attribute 'city'
-
property(fget=None, fset=None, fdel=None, doc=None):是一個類前鹅,主要功能是為了方便類內(nèi)部函數(shù)的調(diào)用(使得在外部不用調(diào)用類方法摘悴,可以直接以屬性方式操作)
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函數(shù),使得x與size直接掛鉤 >>> c = C() >>> c.x = 1 >>> c.x 1 >>> c.size 1 >>> del c.x >>> c.size Traceback (most recent call last): File "<pyshell#16>", line 1, in <module> c.size AttributeError: 'C' object has no attribute 'size'
7舰绘、描述符(property的原理)
描述符就是將某種特殊類型的類(類里含有下述魔法方法之一)的實例指派給另一個類的屬性蹂喻。
1葱椭、__get__
(self, instance, owner):用于訪問屬性,它返回屬性的值
2口四、__set__
(self, instance, value):將在屬性分配操作中調(diào)用孵运,不返回任何內(nèi)容
3、__delete__
(self, instance):控制刪除操作蔓彩,不返回任何內(nèi)容
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 #調(diào)用__get__方法
geting... <__main__.MyDecriptor object at 0x0000000003570FD0> <__main__.Test object at 0x0000000003570F98> <class '__main__.Test'>
#此處self:描述符類本身的一個實例 <__main__.MyDecriptor object at 0x0000000003570FD0>
#instance:擁有者類的實例 <__main__.Test object at 0x0000000003570F98>
#owner:擁有者類本身 <class '__main__.Test'>
>>> test.x ='哈哈哈' #調(diào)用__set__方法
seting... <__main__.MyDecriptor object at 0x0000000003570FD0> <__main__.Test object at 0x0000000003570F98> 哈哈哈
>>> del test.x #調(diào)用__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 = '哈哈' #直接以訪問屬性形式調(diào)用描述類MyProperty
>>> c.x
'哈哈'
>>> c._x
'哈哈'
>>> del c.x
>>> c._x
Traceback (most recent call last):
File "<pyshell#125>", line 1, in <module>
c._x
AttributeError: 'C' object has no attribute '_x'
8、容器類型-魔法方法
1赤嚼、容器是對數(shù)據(jù)的封裝
2旷赖、python的容器類型分為可變類型(如list、dict)和不可變類型(如string更卒、tuple)等孵。可變?nèi)萜骱筒豢勺內(nèi)萜鞯膮^(qū)別在于蹂空,不可變?nèi)萜饕坏┵x值后俯萌,不可對其中的某個元素進行修改。
魔法方法 | 定義 | 備注 |
---|---|---|
__len__(self) |
求容器的大猩险怼(注意與capacity的區(qū)別) | 可變和不可變?nèi)萜骶邆?__len__ 和 __getitem__
|
__getitem__ (self, key) |
獲取容器中指定元素的行為 | |
__setitem__ (self, key, value) |
設置容器中指定元素的行為 | 只有可變?nèi)萜鲹碛?__setitem__ 和 __delitem__
|
__delitem__ (self, key) |
刪除容器中指定元素的行為 | |
__iter__ (self) |
定義迭代器中元素的行為 | |
__reversed__ (self) |
當調(diào)用reversed()函數(shù)時 | 僅當序列可以是有序的時候?qū)崿F(xiàn)它咐熙,例如對于列表或者元組。 |
__contains__ (self, item) |
成員運算符in/ not in的行為 |
class FunctionalList:
''' 實現(xiàn)了內(nèi)置類型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) #調(diào)用迭代器行為
>>> 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循環(huán)的對象統(tǒng)稱為可迭代對象分瘦,使用isinstance()判斷一個對象是否是Iterable對象蘸泻。
>>> from collections import Iterable
>>> isinstance({}, Iterable)
True
>>> isinstance('hhh', Iterable)
True
>>> isinstance(10, Iterable)
False
2、迭代器Iterator:可以被next()函數(shù)調(diào)用并不斷返回下一個值的對象稱為迭代器嘲玫,使用isinstance()判斷一個對象是否是Iterator對象悦施。
- 內(nèi)置函數(shù):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()函數(shù)
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
- 魔法方法:
__iter__
(self)顷窒,__next__
(self)
>>> string = 'abcd'
>>> it = iter(string)
#每次調(diào)用next(it),就計算出it的下一個元素的值,直到計算到最后一個元素鞋吉,沒有更多的元素時鸦做,拋出StopIteration的錯誤。
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
'd'
>>> next(it)
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
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:#控制迭代數(shù)量
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__()
方法赊锚,就可以直接對實例(對象)進行調(diào)用治筒。
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') #直接對實例進行調(diào)用,即是把這個類型的對象當作函數(shù)來使用
My name is Bob...
My friend is Tim...
#對實例進行直接調(diào)用就好比對一個函數(shù)進行調(diào)用一樣舷蒲,所以你完全可以把對象看成函數(shù)矢炼,把函數(shù)看成對象。
如何判斷一個變量是對象還是函數(shù)阿纤?(即判斷一個對象是否能被調(diào)用,能被調(diào)用的對象就是一個Callable
對象)
#用callabel()函數(shù)判斷
>>> callable(Person('Bob', 'male'))
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
11夷陋、__str__
與__repr__
__str__()
返回用戶看到的字符串欠拾,__repr__()
返回程序開發(fā)者看到的字符串,即__repr__()
是為調(diào)試服務的骗绕。
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,打印出來的仍是不好看酬土,因為直接顯示變量調(diào)用的不是__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)