魔術方法

【魔術方法】


特殊屬性


__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)



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章