<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python進(jìn)階——如何正確使用魔法方法?(下)

          共 11825字,需瀏覽 24分鐘

           ·

          2021-03-04 00:06

          在上一篇文章Python進(jìn)階——如何正確使用魔法方法?(上)中,我們主要介紹了關(guān)于構(gòu)造與初始化、類的表示、訪問控制這幾類的魔法方法,以及它們的使用場景。
          這篇文章,我們繼續(xù)介紹剩下的魔法方法,主要包括:比較操作、容器類操作、可調(diào)用對象、序列化。

          比較操作

          比較操作的魔法方法主要包括以下幾種:
          • __cmp__
          • __eq__
          • __ne__
          • __lt__
          • __gt__

          __cmp__

          從名字我們就能看出來這個魔法方法的作用,當(dāng)我們需要比較兩個對象時,我們可以定義 __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
          當(dāng)然,這種比較方式有一定的局限性,如果我有 N 個屬性,當(dāng)比較誰大時,我們想用屬性 A 來比較。當(dāng)比較誰小時,我們想用屬性 B 來比較,此時 __cmp__ 就無法很好地實現(xiàn)這個邏輯了,所以它只適用于通用的比較邏輯。
          那如何實現(xiàn)復(fù)雜的比較邏輯?
          這就需要用到 __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__ 方法,可以判斷兩個對象是否相等。
          但在這個例子中,當(dāng)判斷兩個對象是否相等時,實際上我們比較的是 uid 這個屬性。

          __ne__

          同樣地,當(dāng)需要判斷兩個對象不相等時,會調(diào)用 __ne__ 方法,在這個例子中,我們也是根據(jù) uid 來判斷的。

          __lt__

          當(dāng)判斷一個對象是否小于另一個對象時,會調(diào)用 __lt__ 方法,在這個例子中,我們根據(jù) name 的長度來做的比較。

          __gt__

          同樣地,在判斷一個對象是否大于另一個對象時,會調(diào)用 __gt__ 方法,在這個例子中,我們根據(jù) salary 屬性判斷。
          在 Python3 中,__cmp__被取消了,因為它和其他魔法方法存在功能上的重復(fù)。

          容器類操作

          接下來我們來看容器類的魔法方法,主要包括:
          • __setitem__
          • __getitem__
          • __delitem__
          • __len__
          • __iter__
          • __contains__
          • __reversed__
          是不是很熟悉?我們在開發(fā)中多少都使用到過這些方法。
          在介紹容器的魔法方法之前,我們首先想一下,Python 中的容器類型都有哪些?
          是的,Python 中常見的容器類型有:
          • 字典
          • 元組
          • 列表
          • 字符串
          這些都是容器類型。為什么這么說?
          因為它們都是「可迭代」的。可迭代是因為,它們都實現(xiàn)了容器協(xié)議,也就是我們下面要介紹到的魔法方法。
          我們看下面這個例子。
          # 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([12345])

          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__
          在這個例子中,我們自己實現(xiàn)了一個 MyList 類,在這個類中,定義了很多容器類的魔法方法。這樣一來,我們這個 MyList 類就可以像操作普通 list 一樣,通過切片的方式添加、獲取、刪除、迭代元素了。

          __setitem__

          當(dāng)我們執(zhí)行 my_list[1] = 20 時,就會調(diào)用 __setitem__ 方法,這個方法主要用于向容器內(nèi)添加元素。

          __getitem__

          當(dāng)我們執(zhí)行 my_list[0] 時,就會調(diào)用 __getitem__ 方法,這個方法主要用于從容器中讀取元素。

          __delitem__

          當(dāng)我們執(zhí)行 del my_list[0] 時,就會調(diào)用 __delitem__ 方法,這個方法主要用于從容器中刪除元素。

          __len__

          當(dāng)我們執(zhí)行 len(my_list) 時,就會調(diào)用 __len__ 方法,這個方法主要用于讀取容器內(nèi)元素的數(shù)量。

          __iter__

          這個方法我們需要重點關(guān)注,為什么我們可以執(zhí)行 [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__

          從名字也能看出來,這個方法是在執(zhí)行 1 in my_list 時觸發(fā),用于判斷某個元素是否存在于容器中。

          __reversed__

          這個方法在執(zhí)行 reversed(my_list) 時觸發(fā),用于反轉(zhuǎn)容器的元素,具體的反轉(zhuǎn)邏輯我們也可以自己實現(xiàn)。

          可調(diào)用對象

          了解了容器類魔法方法,我們接著來看可調(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(1020)  # __init__
          print c.x, c.y     # 10 20

          c(100200)         # 調(diào)用instance() 觸發(fā)__call__
          print c.x, c.y      # 100 200
          仔細(xì)看這個例子,我們首先初始化一個 Circle 實例 c,此時會調(diào)用 __init__ 方法,這個很好理解。
          但是,我們對于實例 c 又做了調(diào)用 c(100, 200),注意,此時的 c 是一個實例對象,當(dāng)我們這樣執(zhí)行時,其實它調(diào)用的就是 __call__。這樣一來,我們就可以把實例當(dāng)做一個方法來執(zhí)行。
          如果不好理解,你可以多看幾遍這個例子,理解一下。
          也就是說,Python 中的實例,也是可以被調(diào)用的,通過定義 __call__ 方法,就可以傳入自定義參數(shù)實現(xiàn)自己的邏輯。
          這個魔法方法通常會用在類實現(xiàn)一個裝飾器、元類等場景中,當(dāng)你遇到這個魔法方法時,你能理解其中的原理就可以了。

          序列化

          我們知道 Python 提供了序列號模塊 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(2017223))
          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 個屬性:nameagebirthday
          當(dāng)我們調(diào)用 pickle.dumps(person) 時,__getstate__ 方法就會被調(diào)用,在這里我們忽略了 Person 對象的 age 屬性,那么 person 在序列化時,就只會對其他兩個屬性進(jìn)行保存。

          __setstate__

          同樣地,當(dāng)我們調(diào)用 pickle.loads(pickled_person) 時,__setstate__ 會被調(diào)用,其中入?yún)⒕褪?nbsp;__getstate__ 返回的結(jié)果。
          在 __setstate__ 方法,我們從入?yún)⒅腥〉昧吮恍蛄谢?nbsp;dict,然后從 dict 中取出對應(yīng)的屬性,就達(dá)到了反序列的效果。

          其他魔法方法

          好了,以上介紹的這些,就是我們平時遇到比較多的魔法方法。
          剩下的魔法方法還有很多,主要包括數(shù)值處理、算術(shù)操作、反射算術(shù)操作、增量賦值、類型轉(zhuǎn)換、反射這幾類,由于我們在開發(fā)中很少會見到,這里就不再過多介紹了,當(dāng)遇到時,我們直接查閱文檔了解即可。

          總結(jié)

          這篇文章,我們主要介紹了關(guān)于比較操作、容器類、可調(diào)用對象、序列化等魔法方法。
          其中,比較操作的魔法方法,可以用于自定義實例的比較邏輯。容器類魔法方法,可以幫我們實現(xiàn)一個自定義的容器類,然后我們就可以像操作 listdict 那樣,方便地去獲取容器里的元素、迭代數(shù)據(jù)等等。可調(diào)用對象魔法方法,可以把一個實例當(dāng)做方法來調(diào)用。序列化的魔法方法,可以修改一個實例的序列化和反序列化邏輯。
          Python 的魔法方法正如它的名字一樣,如果使用得當(dāng),我們的類就像被添加了魔法一樣,變得更易用。我們可以使用這些魔法方法,幫我們實現(xiàn)一些復(fù)雜的功能,例如裝飾器、元類等等。


          更多閱讀



          2020 年最佳流行 Python 庫 Top 10


          2020 Python中文社區(qū)熱門文章 Top 10


          5分鐘快速掌握 Python 定時任務(wù)框架

          特別推薦




          點擊下方閱讀原文加入社區(qū)會員

          瀏覽 37
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  乱婬妺妺躁爽A片 | 成人精品国产 | 日本在线视频播放69 | 小嫩苞一区二区三区 | 人人爱人人摸人人 |