Python進(jìn)階——如何正確使用魔法方法?(下)
比較操作
__cmp____eq____ne____lt____gt__
__cmp__
__cmp__ 來實現(xiàn)比較操作。class Person(object):
def __init__(self, uid):
self.uid = uid
def __cmp__(self, other):
if self.uid == other.uid:
return 0
if self.uid > other.uid:
return 1
return -1
p1 = Person(1)
p2 = Person(2)
print p1 > p2 # False
print p1 < p2 # True
print p1 == p2 # False
如果 __cmp__返回大于 0 的整數(shù)(一般為1),說明 self > other如果 __cmp__返回大于 0 的整數(shù)(一般為-1),說明 self < other如果 __cmp__返回 0,說明 self == other
__cmp__ 就無法很好地實現(xiàn)這個邏輯了,所以它只適用于通用的比較邏輯。__eq__、__ne__、__lt__、__gt__ 這些魔法方法了,我們看下面這個例子。# coding: utf8
class Person(object):
def __init__(self, uid, name, salary):
self.uid = uid
self.name = name
self.salary = salary
def __eq__(self, other):
"""對象 == 判斷"""
return self.uid == other.uid
def __ne__(self, other):
"""對象 != 判斷"""
return self.uid != other.uid
def __lt__(self, other):
"""對象 < 判斷 根據(jù)len(name)"""
return len(self.name) < len(other.name)
def __gt__(self, other):
"""對象 > 判斷 根據(jù)alary"""
return self.salary > other.salary
p1 = Person(1, 'zhangsan', 1000)
p2 = Person(1, 'lisi', 2000)
p3 = Person(1, 'wangwu', 3000)
print p1 == p1 # uid 是否相同
print p1 != p2 # uid 是否不同
print p2 < p3 # name 長度比較
print p3 > p2 # salary 比較
__eq__
__eq__ 我們在上一篇文章已經(jīng)介紹過,它配合 __hash__ 方法,可以判斷兩個對象是否相等。uid 這個屬性。__ne__
__ne__ 方法,在這個例子中,我們也是根據(jù) uid 來判斷的。__lt__
__lt__ 方法,在這個例子中,我們根據(jù) name 的長度來做的比較。__gt__
__gt__ 方法,在這個例子中,我們根據(jù) salary 屬性判斷。在 Python3 中, __cmp__被取消了,因為它和其他魔法方法存在功能上的重復(fù)。
容器類操作
__setitem____getitem____delitem____len____iter____contains____reversed__
字典 元組 列表 字符串
# coding: utf8
class MyList(object):
"""自己實現(xiàn)一個list"""
def __init__(self, values=None):
# 初始化自定義list
self.values = values or []
def __setitem__(self, key, value):
# 添加元素
self.values[key] = value
def __getitem__(self, key):
# 獲取元素
return self.values[key]
def __delitem__(self, key):
# 刪除元素
del self.values[key]
def __len__(self):
# 自定義list的元素個數(shù)
return len(self.values)
def __iter__(self):
# 可迭代
return self
def next(self):
# 迭代的具體細(xì)節(jié)
# 如果__iter__返回self 則必須實現(xiàn)此方法
if self._index >= len(self.values):
raise StopIteration()
value = self.values[self._index]
self._index += 1
return value
def __contains__(self, key):
# 元素是否在自定義list中
return key in self.values
def __reversed__(self):
# 反轉(zhuǎn)
return list(reversed(self.values))
# 初始化自定義list
my_list = MyList([1, 2, 3, 4, 5])
print my_list[0] # __getitem__
my_list[1] = 20 # __setitem__
print 1 in my_list # __contains__
print len(my_list) # __len__
print [i for i in my_list] # __iter__
del my_list[0] # __del__
reversed_list = reversed(my_list) # __reversed__
print [i for i in reversed_list] # __iter__
list 一樣,通過切片的方式添加、獲取、刪除、迭代元素了。__setitem__
my_list[1] = 20 時,就會調(diào)用 __setitem__ 方法,這個方法主要用于向容器內(nèi)添加元素。__getitem__
my_list[0] 時,就會調(diào)用 __getitem__ 方法,這個方法主要用于從容器中讀取元素。__delitem__
del my_list[0] 時,就會調(diào)用 __delitem__ 方法,這個方法主要用于從容器中刪除元素。__len__
len(my_list) 時,就會調(diào)用 __len__ 方法,這個方法主要用于讀取容器內(nèi)元素的數(shù)量。__iter__
[i for i in my_list]?就是因為我們定義了 __iter__。返回 iter(obj):代表使用obj對象的迭代協(xié)議,一般obj是內(nèi)置的容器對象返回 self:代表迭代的邏輯由本類來實現(xiàn),此時需要重寫next方法,實現(xiàn)自定義的迭代邏輯
__iter__ 返回的是 self,所以我們需要定義 next 方法,實現(xiàn)自己的迭代細(xì)節(jié)。next 方法使用一個索引變量,用于記錄當(dāng)前迭代的位置,這個方法每次被調(diào)用時,都會返回一個元素,當(dāng)所有元素都迭代完成后,這個方法會返回 StopIteration 異常,此時 for 會停止迭代。在 Python3 中,已不再使用 next 方法,取而代之的是 __next__。
__contains__
1 in my_list 時觸發(fā),用于判斷某個元素是否存在于容器中。__reversed__
reversed(my_list) 時觸發(fā),用于反轉(zhuǎn)容器的元素,具體的反轉(zhuǎn)邏輯我們也可以自己實現(xiàn)。可調(diào)用對象
__call__。# coding: utf8
class Circle(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __call__(self, x, y):
self.x = x
self.y = y
c = Circle(10, 20) # __init__
print c.x, c.y # 10 20
c(100, 200) # 調(diào)用instance() 觸發(fā)__call__
print c.x, c.y # 100 200
Circle 實例 c,此時會調(diào)用 __init__ 方法,這個很好理解。c 又做了調(diào)用 c(100, 200),注意,此時的 c 是一個實例對象,當(dāng)我們這樣執(zhí)行時,其實它調(diào)用的就是 __call__。這樣一來,我們就可以把實例當(dāng)做一個方法來執(zhí)行。__call__ 方法,就可以傳入自定義參數(shù)實現(xiàn)自己的邏輯。序列化
pickle,當(dāng)我們使用這個模塊序列化一個實例時,也可以通過魔法方法來實現(xiàn)自己的邏輯,這些魔法方法包括:__getstate____setstate__
# coding: utf8
class Person(object):
def __init__(self, name, age, birthday):
self.name = name
self.age = age
self.birthday = birthday
def __getstate__(self):
# 執(zhí)行 pick.dumps 時 忽略 age 屬性
return {
'name': self.name,
'birthday': self.birthday
}
def __setstate__(self, state):
# 執(zhí)行 pick.loads 時 忽略 age 屬性
self.name = state['name']
self.birthday = state['birthday']
person = Person('zhangsan', 20, date(2017, 2, 23))
pickled_person = pickle.dumps(person) # __getstate__
p = pickle.loads(pickled_person) # __setstate__
print p.name, p.birthday
print p.age # AttributeError: 'Person' object has no attribute 'age'
__getstate__
Person 對象,其中包括 3 個屬性:name、age、birthday。pickle.dumps(person) 時,__getstate__ 方法就會被調(diào)用,在這里我們忽略了 Person 對象的 age 屬性,那么 person 在序列化時,就只會對其他兩個屬性進(jìn)行保存。__setstate__
pickle.loads(pickled_person) 時,__setstate__ 會被調(diào)用,其中入?yún)⒕褪?nbsp;__getstate__ 返回的結(jié)果。__setstate__ 方法,我們從入?yún)⒅腥〉昧吮恍蛄谢?nbsp;dict,然后從 dict 中取出對應(yīng)的屬性,就達(dá)到了反序列的效果。其他魔法方法
總結(jié)
list、dict 那樣,方便地去獲取容器里的元素、迭代數(shù)據(jù)等等。可調(diào)用對象魔法方法,可以把一個實例當(dāng)做方法來調(diào)用。序列化的魔法方法,可以修改一個實例的序列化和反序列化邏輯。更多閱讀
特別推薦

點擊下方閱讀原文加入社區(qū)會員
評論
圖片
表情
