摘要:這些基本的特殊方法在類中定義中幾乎總是需要的。和方法對于一個對象,有兩種字符串表示方法。這些都和內置函數(shù)以及方法緊密結合。帶有說明符的合理響應是返回。
注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python
有許多特殊方法允許類與Python緊密結合,標準庫參考將其稱之為基本,基礎或本質可能是更好的術語。這些特殊方法構成了創(chuàng)建與其他Python特性無縫集成的類的基礎。
例如,對于給定對象的值,我們需要字符串表示?;悺ο蠖加心J的__repr__()和__str__()用于提供對象的字符串表示。遺憾的是,這些默認表示不提供信息。我們總是想要覆蓋這些默認定義中的一個或兩個。我們可以看看__format__(),這個更復雜但目的和前面是一樣的。
我們也可以看看其他轉換,特別是__hash__()、__bool__()、和__bytes__()。這些方法將一個對象轉換成數(shù)字、true/false值或字符串的字節(jié)。例如,當我們實現(xiàn)__bool__()時,可以在if語句中使用對象,如下:if someobject:。
然后,我們可以看看實現(xiàn)比較操作符的特殊方法__lt__()、__le__()、__eq__()、__ne__()、__gt__()和__ge__()。
這些基本的特殊方法在類中定義中幾乎總是需要的。
最后我們來看看__new__()和__del__(),因為這些方法的用例相當復雜。每當我們需要其他基本特殊方法時,是不需要這些的。
我們會詳細看看如何添加這些特殊方法來擴大一個簡單的類定義。我們需要看一下兩個從對象繼承的默認行為,這樣我們可以了解需要什么樣的覆蓋以及何時真正需要。
__repr__() 和 __str__() 方法對于一個對象,Python有兩種字符串表示方法。這些都和內置函數(shù)__repr__()、__str__()、__print__()以及string.format()方法緊密結合。
str()方法表示的對象通常是適用于人理解的,由對象的__str__()方法創(chuàng)建。
repr()方法表示的對象通常是適用于解釋器理解的,可能是完整的Python表達式來重建對象。文檔中是這樣說的:對于許多類型,這個函數(shù)試圖返回一個字符串,將該字符串傳遞給eval()會重新生成對象。
這是由對象的__repr__()方法創(chuàng)建的。
print()函數(shù)會使用str()準備對象用于打印。
字符串的format()方法也可以訪問這些方法。當我們使用{!r}或{!s}格式,我們分別需要__repr__()或__str__()。
首先讓我們看看默認實現(xiàn)。
下面是一個簡單的類層次結構:
pythonclass Card: insure = False def __init__(self, rank, suit): self.suit = suit self.rank = rank self.hard, self.soft = self._points() class NumberCard(Card): def _points(self): return int(self.rank), int(self.rank)
我們已經定義了帶有四個屬性的兩個簡單類。
以下是一個與其中一個類對象的交互:
>>> x=NumberCard( "2", "?") >>> str(x) "<__main__.NumberCard object at 0x1013ea610>" >>> repr(x) "<__main__.NumberCard object at 0x1013ea610>" >>> print(x) <__main__.NumberCard object at 0x1013ea610>
從這個輸出知道默認的__str__()和__repr__()實現(xiàn)不是很豐富。
當我們需要覆蓋__str__()和__repr__()時我們考慮下面兩個廣泛的設計用例:
非集合對象:一個不包含其他對象集合的“簡單”對象,通常不涉及非常復雜格式的集合。
集合對象:一個包含一組更復雜格式的對象。
1. 非集合__str__()和__repr__()正如我們之前看到的,__str__()和__repr__()的輸出不是很豐富。我們幾乎總是需要覆蓋它們。以下是當沒有包含集合的時候覆蓋__str__()和__repr__()的方法。這些方法從屬于之前定義的Card類:
pythondef __repr__(self): return "{__class__.__name__}(suit={suit!r}, rank={rank!r})".format( __class__=self.__class__, **self.__dict__) def __str__(self): return "{rank}{suit}".format(**self.__dict__)
這兩個方法依賴于傳遞內部對象的實例變量__dict__()給format()函數(shù)。對于對象使用__slots__是不適合的;通常,這些是不可變的對象。在格式說明符中使用名稱會使得格式化更顯式。當然也使得格式模板更長了。在__repr__()中,我們傳遞內部__dict__加上對象的__class__作為關鍵字參數(shù)值給format()函數(shù)。
模板字符串使用兩種格式說明符:
{__class__.__name__}模板也可以寫成{__class__.__name__!s}從而當只提供簡單字符串的類名時變得更顯式。
{suit!r}和{rank!r }模板都使用!r格式說明符生成屬性值的repr()方法。
在__str__()中,我們只有傳遞內部__dict__對象。格式化使用隱式的{!s}格式說明符來生成屬性值的str()方法。
2. 集合__str__()和__repr__()當包含一個集合時,我們需要格式化集合中的每個項目以及整個容器。以下是帶有__str__()和__repr__()方法的簡單集合:
pythonclass Hand: def __init__(self, dealer_card, *cards): self.dealer_card = dealer_card self.cards = list(cards) def __str__(self): return ", ".join(map(str, self.cards)) def __repr__(self): return "{__class__.__name__}({dealer_card!r}, {_cards_str})".format( __class__=self.__class__, _cards_str=", ".join( map(repr, self.cards)),**self.__dict__)
__str__()方法是一個簡單的設計,如下:
映射str()到集合中的每一項。這將在生成的每個字符串值上創(chuàng)建一個迭代器。
使用", ".join()合并所有項的字符串到一個長字符串中。
__repr__()方法是一個多部分的設計,如下:
映射repr()到集合中的每一項。這將在生成的每個字符串值上創(chuàng)建一個迭代器。
使用", ".join()合并所有項的字符串。
創(chuàng)建一組帶有__class__的關鍵字、集合字符串和來自__dict__的各種屬性。我們已經命名集合字符串為_card_str,與現(xiàn)有的屬性不沖突。
使用"{__class__.__name__}({dealer_card!r}, {_card_str})".format()將類名與項目值的長字符串結合。我們使用!r進行格式化以確保屬性也使用了repr()轉換。
在某些情況下,這可以使一些事情變得稍微簡單些。位置參數(shù)的格式化可以一定程度上縮短模板字符串的長度。
__format__() 方法和內置函數(shù)format()一樣,string.format()使用__format__()方法。這兩個接口都是用來從給定對象得到像樣的字符串的方式。
以下是將參數(shù)提供給__format__()的兩種方法:
someobject.__format__(""):發(fā)生在應用程序使用format(someobject)或等價的"{0}".format(someobject)的時候。在這些情況下,提供了長度為零的字符串說明符。這會產生一個默認格式。
someobject.__format__(specification):發(fā)生在應用程序使用format(someobject, specification)或等價的"{0:specification}".format(someobject)的時候。
請注意,類似于"{0!r}".format()或"{0!s}".format()的方法沒有使用__format__()方法。它們直接使用了__repr__()或__str__()。
帶有""說明符的合理響應是返回str(self)。它對各種對象的字符串提供了顯式的一致性表示。
格式說明符必須都是文本,且在格式字符串的":"之后。當我們寫"{0:06.4f}",06.4f是適用于第0項參數(shù)列表的格式說明符。
Python標準庫文檔中的6.1.3.1節(jié)定義了一個復雜的數(shù)值說明符作為九個部分字符串,這是格式說明符的一種迷你語言。有如下語法:
[[fill]align][sign][#][0][width][,][.precision][type]
我們可以用正則表達式解析這些標準說明符,如下面代碼片段所示:
re.compile( r"(?P.?[<>=^])?" "(?P [-+ ])?" "(?P #)?" "(?P 0)?" "(?P d*)" "(?P ,)?" "(?P .d*)?" "(?P [bcdeEfFgGnosxX%])?" )
這個正則將說明符拆分成八組。第一組和原說明符一樣有fill和alignment字段。我們可以使用這些得出我們已定義類的格式化數(shù)值數(shù)據(jù)。
然而,Python的格式說明符迷你語言可能不適用于我們的類定義。因此,我們需要定義我們自己的說明符迷你語言并在類的__format__方法中執(zhí)行。如果我們定義數(shù)值類型,我們應該堅持預定義的迷你語言。然而,對于其他類型則沒有理由再堅持預定義的語言。
作為一個示例,這里有個微不足道的語言,使用字符%r和%s分別給我們展示牌值和花色。在結果字符串中%%字符變成%。所有其他字符是重復的。
我們可以通過格式化擴展我們的Card類,如下面代碼片段所示:
pythondef __format__(self, format_spec): if format_spec == "": return str(self) rs = format_spec.replace("%r", self.rank).replace("%s", self.suit) rs = rs.replace("%%", "%") return rs
這個定義會檢查格式說明符。如果沒有說明符,則使用str()函數(shù)。如果提供了一個說明符,會合攏牌值、花色和任何%字符格式說明符,將其轉化為輸出字符串。
這允許我們像下面這樣格式化Card:
pythonprint( "Dealer Has {0:%r of %s}".format(hand.dealer_card))
格式說明符("%r of %s")被作為format的參數(shù)傳遞給我們的__format__()方法。使用這個,我們能夠提供一個一致的接口來表示我們已經定義的類的對象。
或者,我們可以定義如下:
pythondefault_format = "some specification" def __str__(self): return self.__format__(self.default_format) def __format__(self, format_spec): if format_spec == "": format_spec = self.default_format # process the format specification.
這個的優(yōu)勢在于把所有字符串放置到__format__()方法,而不是分開到的__format__()和__str__()。劣勢在于,我們不總是需要實現(xiàn)__format__(),但我們幾乎總是需要實現(xiàn)__str__()。
1. 嵌套格式化說明符string.format()方法可以處理嵌套的{}實例來執(zhí)行簡單的關鍵字置換到格式說明符中。這個置換完成,會創(chuàng)建最終格式字符串并傳遞給類的__format__()方法。這種嵌套置換通過參數(shù)化通用說明符簡化了某些相對復雜的數(shù)值格式。
下面的例子,我們可以在format參數(shù)中很容易的修改width:
pythonwidth = 6 for hand, count in statistics.items(): print( "{hand} {count:{width}d}".format(hand=hand, count=count, width=width))
我們定義了一個通用的格式,"{hand:%r%s } {count:{width}d}",這需要一個width參數(shù)讓它變成適用的格式說明符。
為format()方法提供width=參數(shù)的值被用于替代{width}嵌套說明符。一旦被替換,最終格式會作為一個整體提供給__format__()方法。
2. 集合與委托格式說明符格式化一個包括集合的復雜對象,有兩個格式化問題:如何格式化整體對象以及如何格式化集合中的項目。當我們看到Hand,例如,我們看到我們有多帶帶的Card類集合。我們需要Hand委托格式化細節(jié)給多帶帶的Card實例。
下面是一個適用于Hand的__format__()方法:
pythondef __format__(self, format_specification): if format_specification == "": return str(self) return ", ".join("{0:{fs}}".format(c, fs=format_specification) for c in self.cards)
format_specification參數(shù)將用于每個Hand集合里面的Card實例。格式說明符"{0:{fs}}"使用嵌套格式說明符技術將format_specification字符串置入到應用于Card實例的創(chuàng)建。使用這種方法我們可以格式化Hand對象、player_hand,如下所示:
python"Player: {hand:%r%s}".format(hand=player_hand)
這將應用%r%s格式說明符到Hand對象中的Card實例。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://www.ezyhdfw.cn/yun/45391.html
摘要:比較運算符方法有六個比較運算符。根據(jù)文檔,其映射工作如下第七章創(chuàng)建數(shù)字我們會再次回到比較運算符這塊。同一個類的對象的比較實現(xiàn)我們來看看一個簡單的同一類的比較通過觀察一個更完整的類現(xiàn)在我們已經定義了所有六個比較運算符。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python __bool__()方法 Python對假有個很...
摘要:有三個用例通過和方法定義相等性檢測和值不可變對象對于有些無狀態(tài)對象,例如這些不能被更新的類型。請注意,我們將為不可變對象定義以上兩個。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python __hash__() 方法 內置hash()函數(shù)會調用給定對象的__hash__()方法。這里hash就是將(可能是復雜的)值縮減...
摘要:當引用計數(shù)為零,則不再需要該對象且可以銷毀。這表明當變量被刪除時引用計數(shù)正確的變?yōu)榱?。方法只能在循環(huán)被打破后且引用計數(shù)已經為零時調用。這兩步的過程允許引用計數(shù)或垃圾收集刪除已引用的對象,讓弱引用懸空。這允許在方法設置對象屬性值之前進行處理。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python __del__()方法 ...
摘要:第二章與的無縫集成基本特殊方法筆記中有有一些特殊的方法它們允許我們的類和更好的集成和方法通常方法表示的對象對用戶更加友好這個方法是有對象的方法實現(xiàn)的什么時候重寫跟非集合對象一個不包括其他集合對象的簡單對象這類對象格式通常不會特別復 第二章 與Python的無縫集成----基本特殊方法.(Mastering Objecting-oriented Python 筆記) python中有有一...
閱讀 1419·2023-04-26 00:35
閱讀 2801·2023-04-25 18:32
閱讀 3476·2021-11-24 11:14
閱讀 827·2021-11-22 15:24
閱讀 1483·2021-11-18 10:07
閱讀 6992·2021-09-22 10:57
閱讀 2835·2021-09-07 09:58
閱讀 3619·2019-08-30 15:54