<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基礎(chǔ)】Python中必須知道的5對(duì)魔術(shù)方法

          共 7168字,需瀏覽 15分鐘

           ·

          2020-10-03 16:05

          原文作者:Yong Cui

          翻譯:Lemon

          譯文出品:Python數(shù)據(jù)之道

          Photo by?Liz Hixon?on?Unsplash

          簡(jiǎn)介

          在使用Python命名函數(shù)時(shí),我們可以使用下劃線以及字母和數(shù)字。在單詞之間使用下劃線時(shí),它們沒(méi)有多大意義,它們只是通過(guò)在單詞之間創(chuàng)建空格來(lái)提高可讀性。這就是蛇形命名方式。例如,?calculate_mean_score?比?calculatemeanscore?更易于閱讀。您可能已經(jīng)知道,除了這種使用下劃線的常用方式之外,我們還為函數(shù)名稱(chēng)加上一個(gè)或兩個(gè)下劃線(例如,?_func,?__func),以表示這些函數(shù)供類(lèi)或模塊內(nèi)的私有使用。沒(méi)有下劃線前綴的名稱(chēng)被認(rèn)為是公共API。
          下劃線在函數(shù)命名中的另一種用法是魔術(shù)方法(magic methods),也稱(chēng)為特殊方法。具體來(lái)說(shuō),我們?cè)诤瘮?shù)名稱(chēng)之前放置兩個(gè)下劃線,在函數(shù)名稱(chēng)之后放置兩個(gè)下劃線-類(lèi)似于?__func__。由于使用了雙下劃線,因此某些人將特殊方法稱(chēng)為 “dunder方法” 或簡(jiǎn)稱(chēng)為 “dunders” 。在本文中,我想回顧五對(duì)緊密相關(guān)的常用魔術(shù)方法,每對(duì)方法代表一個(gè) Python 概念。
          「Python數(shù)據(jù)之道」注:dunder 是 double underscore 的縮寫(xiě),即雙下劃線。

          1. 實(shí)例化:?__new__?和?__init__

          在學(xué)習(xí)了 Python 數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)知識(shí)(例如字典,列表)之后,您應(yīng)該已經(jīng)看到了一些定義自定義類(lèi)的示例,在這些示例中,您第一次接觸到了魔術(shù)方法?__init__。此方法用于定義實(shí)例對(duì)象的初始化行為。具體來(lái)說(shuō),在?__init__?方法中,您想要為創(chuàng)建的實(shí)例對(duì)象設(shè)置初始屬性。這是一個(gè)簡(jiǎn)單的示例:
          1. class Product:

          2. def __init__(self, name, price):

          3. self.name = name

          4. self.price = price

          當(dāng)我們使用?__init__方法時(shí),我們不會(huì)直接調(diào)用它。取而代之的是,?__init__方法成為該類(lèi)的構(gòu)造函數(shù)方法的構(gòu)建基礎(chǔ),該類(lèi)的構(gòu)造函數(shù)與?__init__方法具有相同的功能簽名。例如,要?jiǎng)?chuàng)建一個(gè)新的?Product?實(shí)例,請(qǐng)使用以下代碼:
          1. product = Product("Vacuum", 150.0)

          與?__init__方法最接近的是?__new__?方法,我們通常不會(huì)在自定義類(lèi)中實(shí)現(xiàn)該方法。本質(zhì)上,?__new__?方法實(shí)際上創(chuàng)建了實(shí)例對(duì)象,該實(shí)例對(duì)象被傳遞給?__init__?方法以完成初始化過(guò)程。
          換句話說(shuō),構(gòu)造新的實(shí)例對(duì)象(稱(chēng)為實(shí)例化的過(guò)程)涉及依次調(diào)用?__new__?和?__init__方法。
          以下代碼展示了這樣的一系列反應(yīng):
          1. >>> class Product:

          2. ... def __new__(cls, *args):

          3. ... new_product = object.__new__(cls)

          4. ... print("Product __new__ gets called")

          5. ... return new_product

          6. ...

          7. ... def __init__(self, name, price):

          8. ... self.name = name

          9. ... self.price = price

          10. ... print("Product __init__ gets called")

          11. ...

          12. >>> product = Product("Vacuum", 150.0)

          13. Product __new__ gets called

          14. Product __init__ gets called

          2. 字符串(String)的表現(xiàn)形式:?__repr__?和?__str__

          這兩種方法對(duì)于為自定義類(lèi)設(shè)置字符串表現(xiàn)形式都很重要。在解釋它們之前,讓我們快速看一下下面的實(shí)現(xiàn)過(guò)程:
          1. class Product:

          2. def __init__(self, name, price):

          3. self.name = name

          4. self.price = price


          5. def __repr__(self):

          6. return f"Product({self.name!r}, {self.price!r})"


          7. def __str__(self):

          8. return f"Product: {self.name}, ${self.price:.2f}"

          __repr__?方法應(yīng)該返回一個(gè)字符串,該字符串顯示如何創(chuàng)建實(shí)例對(duì)象。具體來(lái)說(shuō),可以將該字符串傳遞給?eval()來(lái)重新構(gòu)造實(shí)例對(duì)象。以下代碼段向您展示了這樣的操作:
          1. >>> product = Product("Vacuum", 150.0)

          2. >>> repr(product)

          3. "Product('Vacuum', 150.0)"

          4. >>> evaluated = eval(repr(product))

          5. >>> type(evaluated)

          6. <class '__main__.Product'>

          __str__?方法可以返回有關(guān)實(shí)例對(duì)象的更多描述。應(yīng)該注意的是,?print()?函數(shù)使用?__str__方法來(lái)顯示與實(shí)例相關(guān)的信息,如下所示:
          1. >>> print(product)

          2. Product: Vacuum, $150.00

          盡管這兩種方法都應(yīng)返回字符串,但是?__repr__?方法通常是供開(kāi)發(fā)人員使用的,因此我們希望顯示實(shí)例化信息,而?__str__?方法是針對(duì)常規(guī)用戶(hù)的,因此我們希望顯示更多的信息。

          3. 迭代:?__iter__?和?__next__

          我們可以使代碼自動(dòng)化的一個(gè)關(guān)鍵操作是為我們重復(fù)執(zhí)行一項(xiàng)工作,該工作的實(shí)現(xiàn)涉及到 for 循環(huán)作為邏輯流程。就相關(guān)對(duì)象而言,它可以在 for 循環(huán)中使用。for 循環(huán)的最基本形式如下所示:
          1. for item in iterable:

          2. # Operations go here

          在底層,可迭代對(duì)象轉(zhuǎn)換為迭代器,該迭代器為每個(gè)循環(huán)顯示可迭代對(duì)象。一般來(lái)說(shuō),迭代器是 Python 對(duì)象,可用于渲染要迭代的變量。轉(zhuǎn)換是通過(guò)實(shí)現(xiàn)?__iter__?特殊方法來(lái)完成的。另外,檢索迭代器的下一項(xiàng)涉及?__next__?特殊方法的實(shí)現(xiàn)。讓我們繼續(xù)前面的示例,并使我們的 Product 類(lèi)作為 for 循環(huán)中的迭代器工作:
          1. >>> class Product:

          2. ... def __init__(self, name, price):

          3. ... self.name = name

          4. ... self.price = price

          5. ...

          6. ... def __str__(self):

          7. ... return f"Product: {self.name}, ${self.price:.2f}"

          8. ...

          9. ... def __iter__(self):

          10. ... self._free_samples = [Product(self.name, 0) for _ in range(3)]

          11. ... print("Iterator of the product is created.")

          12. ... return self

          13. ...

          14. ... def __next__(self):

          15. ... if self._free_samples:

          16. ... return self._free_samples.pop()

          17. ... else:

          18. ... raise StopIteration("All free samples have been dispensed.")

          19. ...

          20. >>> product = Product("Perfume", 5.0)

          21. >>> for i, sample in enumerate(product, 1):

          22. ... print(f"Dispense the next sample #{i}: {sample}")

          23. ...

          24. Iterator of the product is created.

          25. Dispense the next sample #1: Product: Perfume, $0.00

          26. Dispense the next sample #2: Product: Perfume, $0.00

          27. Dispense the next sample #3: Product: Perfume, $0.00

          如上所示,我們創(chuàng)建了一個(gè)對(duì)象列表,該對(duì)象列表在?__iter__?方法中保存了免費(fèi)樣本 (free samples),這些樣本為自定義類(lèi)實(shí)例提供了迭代器。為了實(shí)現(xiàn)迭代行為,我們通過(guò)提供免費(fèi)樣本列表中的對(duì)象來(lái)實(shí)現(xiàn)?__next__?方法。當(dāng)我們用完免費(fèi)樣本時(shí),迭代結(jié)束。

          4. 上下文管理器:?__enter__?和?__exit__

          當(dāng)我們使用 Python 處理文件對(duì)象時(shí),你遇到的最常見(jiàn)的語(yǔ)法可能是這樣的:
          1. with open('filename.txt') as file:

          2. # Your file operations go here

          with?語(yǔ)句的使用被稱(chēng)為上下文管理器技術(shù)。具體來(lái)說(shuō),在上面的文件操作示例中,?with?語(yǔ)句將為文件對(duì)象創(chuàng)建一個(gè)上下文管理器,并且在文件操作之后,上下文管理器將幫助我們關(guān)閉文件對(duì)象,以便共享資源(即文件) 可以用于其他進(jìn)程。
          因此,通常來(lái)說(shuō),上下文管理器是 Python 對(duì)象,它們?yōu)槲覀児芾砉蚕碣Y源,例如打開(kāi)和關(guān)閉。沒(méi)有它們,我們必須手動(dòng)管理它們,這很容易出錯(cuò)。
          為了通過(guò)自定義類(lèi)實(shí)現(xiàn)這種行為,我們的類(lèi)需要實(shí)現(xiàn)?__enter__?和?__exit__?方法。?__enter__?方法設(shè)置了上下文管理器,該上下文管理器為我們進(jìn)行操作準(zhǔn)備了所需的資源,而?__exit__?方法則是清理應(yīng)釋放的所有已使用資源,以使其可用。讓我們考慮下面的簡(jiǎn)單示例,其中包含先前的 “ Product” 類(lèi):
          1. >>> class Product:

          2. ... def __init__(self, name, price):

          3. ... self.name = name

          4. ... self.price = price

          5. ...

          6. ... def __str__(self):

          7. ... return f"Product: {self.name}, ${self.price:.2f}"

          8. ...

          9. ... def _move_to_center(self):

          10. ... print(f"The product ({self}) occupies the center exhibit spot.")

          11. ...

          12. ... def _move_to_side(self):

          13. ... print(f"Move {self} back.")

          14. ...

          15. ... def __enter__(self):

          16. ... print("__enter__ is called")

          17. ... self._move_to_center()

          18. ...

          19. ... def __exit__(self, exc_type, exc_val, exc_tb):

          20. ... print("__exit__ is called")

          21. ... self._move_to_side()

          22. ...

          23. >>> product = Product("BMW Car", 50000)

          24. >>> with product:

          25. ... print("It's a very good car.")

          26. ...

          27. __enter__ is called

          28. The product (Product: BMW Car, $50000.00) occupies the center exhibit spot.

          29. It's a very good car.

          30. __exit__ is called

          31. Move Product: BMW Car, $50000.00 back.

          如你所見(jiàn),當(dāng)實(shí)例對(duì)象嵌入在 with 語(yǔ)句中時(shí),將調(diào)用?__enter__?方法。當(dāng)在 with 語(yǔ)句中完成該操作時(shí),將調(diào)用?__exit__?方法。
          但是,應(yīng)該注意,我們可以實(shí)現(xiàn)?__enter__和?__exit__方法來(lái)創(chuàng)建上下文管理器。使用上下文管理器“裝飾器”函數(shù),可以更輕松地完成此操作。

          5. 更精細(xì)的屬性訪問(wèn)控制:?__getattr__?和?__setattr__

          如果您有其他語(yǔ)言的編程經(jīng)驗(yàn),則可能已習(xí)慣于為實(shí)例屬性設(shè)置顯式的 getter 和 setter 。在 Python 中,我們不需要為每個(gè)單獨(dú)的屬性使用這些訪問(wèn)控制技術(shù)。但是,有可能通過(guò)實(shí)現(xiàn)?__getattr__和?__setattr__方法來(lái)進(jìn)行控制。具體來(lái)說(shuō),訪問(wèn)實(shí)例對(duì)象的屬性時(shí)將調(diào)用?__getattr__?方法,而當(dāng)我們?cè)O(shè)置實(shí)例對(duì)象的屬性時(shí)將調(diào)用?__setattr__?方法。
          1. >>> class Product:

          2. ... def __init__(self, name):

          3. ... self.name = name

          4. ...

          5. ... def __getattr__(self, item):

          6. ... if item == "formatted_name":

          7. ... print(f"__getattr__ is called for {item}")

          8. ... formatted = self.name.capitalize()

          9. ... setattr(self, "formatted_name", formatted)

          10. ... return formatted

          11. ... else:

          12. ... raise AttributeError(f"no attribute of {item}")

          13. ...

          14. ... def __setattr__(self, key, value):

          15. ... print(f"__setattr__ is called for {key!r}: {value!r}")

          16. ... super().__setattr__(key, value)

          17. ...

          18. >>> product = Product("taBLe")

          19. __setattr__ is called for 'name': 'taBLe'

          20. >>> product.name

          21. 'taBLe'

          22. >>> product.formatted_name

          23. __getattr__ is called for formatted_name

          24. __setattr__ is called for 'formatted_name': 'Table'

          25. 'Table'

          26. >>> product.formatted_name

          27. 'Table'

          每當(dāng)我們嘗試設(shè)置對(duì)象的屬性時(shí),都會(huì)調(diào)用?__setattr__?方法。若要正確使用它,必須通過(guò)使用?super()?使用超類(lèi)方法。否則,它將陷入無(wú)限遞歸。
          設(shè)置好 formatted_name 屬性后,該屬性將成為?__dict__?對(duì)象的一部分,因此不會(huì)調(diào)用?__getattr__
          附帶說(shuō)明一下,還有另一種與訪問(wèn)控制緊密相關(guān)的特殊方法稱(chēng)為?__getattribute__,它類(lèi)似于?__getattr__,但是每次訪問(wèn)屬性時(shí)都會(huì)被調(diào)用。在這方面,它類(lèi)似于?__setattr__,同樣,你應(yīng)使用super()實(shí)現(xiàn)?__getattribute__?方法,以避免無(wú)限遞歸錯(cuò)誤。

          小結(jié)

          在本文中,我們回顧了五對(duì)重要的特殊方法,通過(guò)它們我們學(xué)習(xí)了有關(guān)的五個(gè) Python 概念。我希望您對(duì)這些概念以及如何在自己的 Python 項(xiàng)目中使用特殊方法有更好的理解。
          原文作者:Yong Cui
          來(lái)源:
          https://medium.com/better-programming/5-pairs-of-magic-methods-in-python-you-should-know-f98f0e5356d6

          ---------End---------




          往期精彩回顧





          獲取一折本站知識(shí)星球優(yōu)惠券,復(fù)制鏈接直接打開(kāi):

          https://t.zsxq.com/662nyZF

          本站qq群704220115。

          加入微信群請(qǐng)掃碼進(jìn)群(如果是博士或者準(zhǔn)備讀博士請(qǐng)說(shuō)明):


          瀏覽 64
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  轻轻操影院 | 波多野吉衣高清无码 | 黄色日逼免费网站 | 婷婷午夜天 | 五月亭亭六月丁香 |