【魔術方法】
特殊屬性
__name__ 類,函數,方法等的名字
__module__ 類定義所在的模塊名
__class__ 對象或類所屬的類
__bases__ 類的基類元組,順序爲它們在基類列表中出現的順序
__doc__ 類 函數的字符串,如果沒有定義則爲None
__mro__ 類的mro ,class.mro() 的 結果保存在__mro__中
__dict__ 類或者實例的屬性,可寫的字典
查看屬性
__dir__ 返回類或者對象的所有成員名稱,dir( )函數就是調用__dir__,如果提供__dir__(),則返回屬性的列表,否則會盡量從__dict__屬性中收集信息
如果dir( )參數包含方法__dir__()方法將被調用,如果不包含__dir__(),改方法將最大限度的收集參數信息
dir()對於不同類型的對象具有不同的行爲
如果對象是模塊對象,列表包含模塊的屬性名
如果對象是類型或者類對象,列表包含類的屬性名,及它的基類的屬性名
否則,列表包含對象的屬性名,它的類的屬性名,和類的基類的屬性名
class Animal: x = 123 def __init__(self,name): self.name = name self.__age = 10 self.weight = 20 print('animal Module\'s name ={}'.format(dir(Animal))) #指定模塊名詞空間內的屬性 print('object's__dict ={}'.format(sorted(object.__dict__.keys())) #object的字典 print('Animal's dir() = {}'.format(dir(Animal))) 類Animal的dir()
hash
散列,哈希就是得到散列值,哈希可能一樣,但是對象不是一個,稱爲哈希衝突
使用哈希取模法,來得到一個個不同的哈希值
__hash__ 內建函數hash()調用的返回值,返回一個整數,如果定義這個方法該類的實例就可hash
使用 hash() 相當於調用 **.__hash__( )
__hash__方法只能判斷兩個值的引用或者值是否相等,但是去重得用__eq__來判斷是否他們的內存地址是否一樣
__eq__ 對應== 操作符,判斷2個對象是否相等,返回bool值
#a == b 等價於 a.__eq__(b) 運算符的實現方法
set集合 會先使用__eq__方法判斷值是否一樣,如果一樣就會去重,即使內存地址一樣
__hash__方法只是返回一個hash值作爲set的key,但是去重,還需要__eq__來判斷2個對象是否相等
hash 值相等,只是hash衝突,不能說明兩個對象是相等的
因此,一般來說提供__hash__方法是爲了作爲set或者dict的key的,所以去重要同時提供__eq__方法
可hash對象必須提供__hash__方法,沒有提供的話,isinstance(p1,collections.Hashable)一定爲False
去重要提供__eq方法
__bool__ 內建函數bool()或者對象放在邏輯表達式的位置,調用這個函數返回布爾值
沒有定義__bool__(),就找__len__()返回長度,非0爲真,如果__len__()也沒有定義,那麼所有實例都返回真
可視化:
方法:
除了print ,str , format之外其他大部分調用的都是__repr__
__repr__ 內建函數repr()對一個對象獲取字符串表達式,如果一個類定義了__repr__( )但沒有定義__str__,那麼在請求該類的實例的‘ 非正式 ’的字符串時也將調用__repr__()
__str__ str()函數,內建函數format、print()函數調用,需要返回對象的字符串表達
運算符重載
operator模塊提供以下的特殊方法,可以將實例使用下面的操作符來操作
運算符 | 特殊方法 | 含義 |
<,<=,==,>,>=,!= | __lt__,__le__,__eq__,__gt__,__ge__,__ne__ | 比較運算符 |
+,-,*,/,%,//,**,divmod | __add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__ | 算數運算符,移位,位運算也有對應的方法 |
+=,-=,*=,/=,%=,//=,**= | __iadd__,__isub__,__mul__,iturediv__,__imod__,__ifloordiv__,__ipow__ |
返回類型,
實現連加(鏈式編程),返回實例本身
class A: def __init__(self,x): self.x = x def __sub__(self, other): return self.x - other.x def __isub__(self, other): tmp = self.x - other.x return tmp def __str__(self): return str(self.x) x = A(4) y = A(5) print(x - y ,x.__sub__(y)) x-=y
習題1:
完成point類設計,實現判斷點相等的方法,並完成向量的加法
class Point: def __init__(self,x,y): self.x = x self.y = y def __eq__(self, other): return self.x == other.x and self.y == other.y def add(self,other): #提供格式化後的追加打印 return Point(self.x+other.x,self.y+other.y) def __add__(self, other): #運算符重載,會提供 + 號的方法 return (self.x + other.x , self.y + other.y) def __str__(self): #可視化 return '{},{}'.format(self.x,self.y) a =Point(1,3) b =Point(1,1) point = (a,b) #print(point[0].add(point[1])) print(point[0]+point[1]) point(Point(*(point[0]+ point[1])))
print(a==b)
運算符重載應用場景
往往使用面向對象實現的類,需要做大量的運算,而運算符是這種運算在數學上最常見的表達方式,例如,上例中的對 + 進行了運算符重載,實現了Point類的二元操作,重新定義爲Point+Point
提供運算符重載,比直接提供加法方法要更加適合該領域內使用者的習慣
int類,基本實現了所有的操作符,可以參考
容器相關方法
方法 | 意義 |
__len__ | 內建輸入法len( ),返回對象的長度(>=0的正數),其實即使把對象當做容器類型來看,就如同list或者dict,bool()函數調用的時候,如果沒有__bool__()方法,則會看__len__()方法是否存在,存在返回非0爲真 |
__iter__ | 迭代容器時,調用,返回一個新的迭代器對象 |
__contains__ | in成員運算符,沒有實現,就調用__iter__方法遍歷 |
__getitem__ | 實現self[key]訪問,序列對象,key接受整數位索引,或者切片,對於set和dict,key位hashable,key不存在引發KeyError異常 |
__setitem__ | 和__getitem__的訪問類似,是設置值的方法 |
__missing__ | 字典使用__getitem__()調用時,key不存在執行該方法 |
練習,將購物車改造成方便操作的容器類
# 將購物車類改造成方便的操作的容器 class Cart: def __init__(self): self.__items = [] def additem(self, item): self.__items.append(item) def __len__(self): return len(self.__items) def __iter__(self): # return iter(self.__items) yield from self.__items def __getitem__(self, index): # 使用索引訪問 print(index, '#############') return self.__items[index] def __setitem__(self, index, value): # 索引賦值 self.__items[index] = value def __add__(self, other): # 實現購物車加的功能 self.__items.append(other) return self cart = Cart() cart.additem(1) cart.additem('abc') cart.additem(3) cart.additem(4) # print(len(cart.items)) print(len(cart)) for x in cart: print(x) # print(cart['1']) # cart[1] = 2 cart + 11 + 12 # 鏈式編程 cart.__add__(13).__add__(14) print(list(cart))
可調用對象
python中一切皆對象,函數也不例外
def foo():
print(foo.__module__,foo.__name__)
foo()
等價於 foo.__call__()
函數即對象,對象foo加上(),就等於調用對象的__call__( )方法
可調用對象
方法 | 意義 |
__call__ | 類中的第一個方法,實例就可以向函數一樣調用 |
可調用對象,應以一個類,並實例化得到其實例,就可以將其向函數一樣調用
class Point: def __init__(self,x,y): self.x = x self.y = y def __call__(self, *args, **kwargs): #1-1 return self.x + self.y a = Point(4,5)
ret =0 for x in args: ret += x self.ret = ret return ret a =Adder() print(a(4,5,6))
定義一個斐波那契數列方便調用
class Fib: def __init__(self): """使用緩衝,減少計算""" self.items = [0, 1, 1] def __call__(self, index): # if index >= len(self.items): for i in range(3, index + 1): self.items.append(self.items[i - 1] + self.items[i - 2]) return self.items[index] def __getitem__(self, index): # return self.items[item] return self(index) def __len__(self): return len(self.items) - 1 """可迭代""" def __iter__(self): return iter(self.items) fib = Fib() print(fib[10]) for x in fib: print(x)
練習題:
# 用鏈表實現linkedlist 鏈表 # 單向鏈表實現append,literes方法 # 雙向鏈表實現append . pop, insert. remove. iternodes方法 class Link: """創建一個節點""" def __init__(self, val, next=None, prev=None): self.val = val self.next = next self.prev = prev def __repr__(self): return str(self.val) def __str__(self): return str(self.val) class Linkist: """容器類,某種方式存儲一個個節點""" def __init__(self): self.nodes = [] # 不需要插入的列表來說,檢索方便,但是insert、remove不合適 self.head = None self.tail = None def __len__(self): return len(self.nodes) def __getitem__(self, item): return self.nodes[item] def append(self, val): node = Link(val) # 實例化的一個新節點 prev = self.tail if prev is None: self.head = node else: prev.next = node self.nodes.append(val) self.tail = node def iternodes(self, reverse=False): current = self.head while current: yield current current = current.next ll = Link() ll.append(5) ll.append(7) for node in ll.iternodes(): print(node) print(ll[0]) print(ll[1])
雙鏈表
# 雙向鏈表實現append . pop, insert. remove. iternodes方法 # 雙向鏈表: # 0) append尾部追加 # 1) pop尾部彈出 # 2) insert 頭部插入、中間插入、尾部插入 # 3) remove 頭部刪除、中間刪除、尾部刪除 class SingleNode: def __init__(self, val, next=None, prev=None): self.val = val self.next = next self.prev = prev def __repr__(self): return str(self.val) def __str__(self): return str(self.val) class LinkedList: def __init__(self): # self.nodes = [] 加上鍊表二不想 self.head = None self.tail = None def append(self, val): node = SingleNode(val) # prev = self.tail # if prev is None: if self.head is None: # only one self.head = node else: # >1 self.tail.next = node node.prev = self.tail # self.nodes.append(node) self.tail = node def iternodes(self, reverse=False): current = self.tail if reverse else self.head while current: yield current current = current.prev if reverse else current.next def pop(self):#尾巴移除 if self.tail is None: # 0個數據 raise Exception('Empty')#該鏈表爲空 報異常 tail = self.tail# node prev = tail.prev# item # next = tail.next #尾的下一個恆定爲None if prev is None: #當移走一個裏面只有一個元素,表示爲空 """清空元素""" self.head = None self.tail = None else: # >1 self.tail = prev #修正尾巴 prev.next = None#後一個元素的沒有不用修改 return tail.val def getitem(self, index): # index不考慮傳入str類型,全當做int類型 if index < 0: return None current = None for i, node in enumerate(self.iternodes()): if i == index: current = node break if current is not None: return current def insert(self, index, val): if index < 0: # 不考慮負索引 raise Exception('Error') current = None for i, node in enumerate(self.iternodes()):#迭代元素 if i == index:#判斷索引是否超界 current = node #當找到當前節點 break # else : 在不遇到break 時不返回 #找到元素插入點和元素 if current is None: # 考慮元素爲空、爲一個 self.append(val) return # 尾部、頭部、中間追加 prev = current.prev# 前一個節點 next = current.next#後一個節點 node = SingleNode(val)#待加入節點對象 if prev is None: # 頭部插入 self.head = node #加入新的元素時 改變頭 # node.next = current # current.prev = node else: #中間加入 node.prev = prev#中間加入比較特殊 修改四個箭頭 # node.next = current #和前面條件共同部分抽出來放後面 # current.prev = node prev.next = node node.next = current current.prev = node def remove(self, index): if self.tail is None:# 全空 raise Exception('Empty') if index < 0:#全空 raise ValueError('Wrong Index {}'.format(index)) current = None """超界""" for i, node in enumerate(self.iternodes()): if i == index: current = node break if current is None: # Not found raise ValueError('Wrong Index {}. Out of boundary'.format(index)) #找到了要移除的結點 prev = current.prev next = current.next if prev is None and next is None: # only one node self.head = None# 頭尾修改 self.tail = None elif prev is None: # 刪頭部 self.head = next next.prev = None elif next is None: # 刪尾部 self.tail = prev prev.next = None else: # 中間 """中間的倆邊 手拉手""" prev.next = next next.prev = prev del current # def __getitem__(self, item): # return self.nodes[item] ll = LinkedList() ll.append('abc') ll.append(1) ll.append(2) ll.append(3) ll.append(4) ll.append(5) ll.append('def') ll.insert(6, 6) # 邊界 ll.insert(7, 7) # 尾部 ll.insert(8, 8) # 尾部 ll.insert(0, 123) # 頭部 ll.insert(100, 100) # 超界 # ll.remove(100) # ll.pop() # ll.pop() for node in ll.iternodes(False): # False print(node) print('~' * 30) # print(ll[0]) # print(ll[1]) ll.remove(5) ll.remove(6) ll.remove(0) ll.remove(1) for node in ll.iternodes(False): # False print(node)