摘要:第一是在對象生命周期中初始化是最重要的一步每個對象必須正確初始化后才能正常工作。第二是參數(shù)值可以有多種形式。基類對象的方法對象生命周期的基礎(chǔ)是它的創(chuàng)建初始化和銷毀。在某些情況下,這種默認行為是可以接受的。
注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python
__init__()方法意義重大的原因有兩個。第一是在對象生命周期中初始化是最重要的一步;每個對象必須正確初始化后才能正常工作。第二是__init__()參數(shù)值可以有多種形式。
因為有很多種方式為__init__()提供參數(shù)值,所以對于對象創(chuàng)建有大量的使用案例,我們可以看看其中的幾個。我們想盡可能的弄清楚,因此我們需要定義一個初始化來正確的描述問題域。
在我們接觸__init__()方法之前,無論如何,我們都需要簡單粗略地看看Python中隱含的object類的層次結(jié)構(gòu)。
在這一章,我們看看不同形式的簡單對象的初始化(例如:打牌)。在這之后,我們還可以看看更復雜的對象,就像包含集合的hands以及包含策略和狀態(tài)的players。
隱式超類——object每一個Python類都隱含了一個超類:object。它是一個非常簡單的類定義,幾乎不做任何事情。我們可以創(chuàng)建object的實例,但是我們不能用它做太多,因為許多特殊的方法容易拋出異常。
當我們自定義一個類,object則為超類。下面是一個類定義示例,它使用新的名稱簡單的繼承了object:
class X: pass
下面是和自定義類的一些交互:
>>> X.__class__>>> X.__class__.__base__
我們可以看到該類是type類的一個對象,且它的基類為object。
就像在每個方法中看到的那樣,我們也看看從object繼承的默認行為。在某些情況下,超類的特殊方法是我們想要的。而在其他情況下,我們又需要覆蓋這個特殊方法。
基類對象的__init__()方法對象生命周期的基礎(chǔ)是它的創(chuàng)建、初始化和銷毀。我們將創(chuàng)建和銷毀推遲到后面章節(jié)的高級特殊方法中講,目前只關(guān)注初始化。
所有類的超類object,有一個默認包含pass的__init__()方法,我們不需要去實現(xiàn)它。如果不實現(xiàn)它,則在對象創(chuàng)建后就不會創(chuàng)建實例變量。在某些情況下,這種默認行為是可以接受的。
我們總是給對象添加屬性,該對象為基類object的子類。思考下面的類,它需要兩個實例變量但不初始化它們:
class Rectangle: def area(self): return self.length * self.width
Rectangle類有一個使用兩個屬性來返回一個值的方法。這些屬性沒有初始化,是合法的Python代碼。它可以明確地避免設置屬性,雖然感覺有點奇怪,但是合法。
下面是與Rectangle類的交互:
>>> r = Rectangle() >>> r.length, r.width = 13, 8 >>> r.area() 104
顯然這是合法的,但這也是容易混淆的根源,所以也是我們需要避免的原因。
無論如何,這個設計給予了很大的靈活性,這樣有時候我們不用在__init__()方法中設置所有屬性。至此我們走的很順利。一個可選屬性其實就是一個子類,只是沒有真正的正式聲明為子類。我們創(chuàng)建多態(tài)在某種程度上可能會引起混亂,以及if語句的不恰當使用所造成的盤繞。雖然未初始化的屬性可能是有用的,但也很有可能是糟糕設計的前兆。
《Python之禪》中的建議:
"顯式比隱式更好。"
一個__init__()方法應該讓實例變量顯式。
非常差的多態(tài)
靈活和愚蠢就在一念之間。
當我們覺得需要像下面這樣寫的時候,我們正從靈活的邊緣走向愚蠢:
if "x" in self.__dict__:
或者:
try: self.x except AttributeError:
是時候重新考慮API并添加一個通用的方法或?qū)傩?。重?gòu)比添加if語句更明智。
在超類中實現(xiàn)__init__()我們通過實現(xiàn)__init__()方法來初始化對象。當一個對象被創(chuàng)建,Python首先創(chuàng)建一個空對象并為該新對象調(diào)用__init__()方法。這個方法函數(shù)通常用來創(chuàng)建對象的實例變量并執(zhí)行任何其他一次性處理。
下面是Card類示例定義的層次結(jié)構(gòu)。我們將定義Card超類和三個子類,這三個子類是Card的變種。兩個實例變量直接由參數(shù)值設置,并通過初始化方法計算:
class Card: 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) class AceCard(Card): def _points(self): return 1, 11 class FaceCard(Card): def _points(self): return 10, 10
在這個示例中,我們提取__init__()方法到超類,這樣在Card超類中的通用初始化可以適用于三個子類NumberCard、AceCard和FaceCard。
這是一種常見的多態(tài)設計。每一個子類都提供一個唯一的_points()方法實現(xiàn)。所有子類都有相同的簽名:有相同的方法和屬性。這三個子類的對象在一個應用程序中可以交替使用。
如果我們?yōu)榛ㄉ褂煤唵蔚淖址?,我們可以?chuàng)建Card實例,如下所示:
cards = [AceCard("A", "?"), NumberCard("2","?"), NumberCard("3","?"),]
我們在列表中枚舉出一些牌的類、牌值和花色。從長遠來說,我們需要更智能的工廠函數(shù)來創(chuàng)建Card實例,用這個方法枚舉52張牌無聊且容易出錯。在我們接觸工廠函數(shù)之前,我們看一些其他問題。
使用__init__()創(chuàng)建顯而易見的常量可以給牌定義花色類。在二十一點中,花色無關(guān)緊要,簡單的字符串就可以。
我們使用花色構(gòu)造函數(shù)作為創(chuàng)建常量對象示例。在許多情況下,我們應用中小部分對象可以通過常量集合來定義。小部分的靜態(tài)對象可能是實現(xiàn)策略模式或狀態(tài)模式的一部分。
在某些情況下,我們會有一個在初始化或配置文件中創(chuàng)建的常量對象池,或者我們可以基于命令行參數(shù)創(chuàng)建常量對象。我們會在第十六章《命令行處理》中獲取初始化設計和啟動設計的詳細信息。
Python沒有簡單正式的機制來定義一個不可變對象,我們將在第三章《屬性訪問、特性和描述符》中看看保證不可變性的相關(guān)技術(shù)。在本示例中,花色不可變是有道理的。
下面這個類,我們將用于創(chuàng)建四個顯而易見的常量:
class Suit: def __init__(self, name, symbol): self.name = name self.symbol = symbol
下面是通過這個類創(chuàng)建的常量:
Club, Diamond, Heart, Spade = Suit("Club","?"), Suit("Diamond","?"), Suit("Heart","?"), Suit("Spade","?")
現(xiàn)在我們可以通過下面展示的代碼片段創(chuàng)建cards:
cards = [AceCard("A", Spade), NumberCard("2", Spade), NumberCard("3", Spade),]
這個小示例的方法對于單個字符花色的代碼來說并沒有多大改進。在更復雜的情況下,會通過這個方式創(chuàng)建一些策略或狀態(tài)對象。從小的靜態(tài)常量池中復用對象使得策略或狀態(tài)設計模式效率更高。
我們必須承認,在Python中這些對象并不是技術(shù)上一成不變的,它是可變的。進行額外的編碼使得這些對象真正不可變可能會有一些好處。
無關(guān)緊要的不變性
不變性很有吸引力但卻容易帶來麻煩。有時候神話般的“惡意程序員”在他們的應用程序中通過修改常量值進行調(diào)整。從設計上考慮,這是非常愚蠢的。這些神話般的、惡意的程序員不會停止這樣做。在Python中沒有更好的方法保證沒有白癡的代碼。惡意程序員訪問到源碼并且修改它僅僅是希望盡可能輕松地編寫代碼來修改一個常數(shù)。
在定義不可變對象的類的時候最好不要掙扎太久。在第三章《屬性訪問、特性和描述符》中,我們將在有bug的程序中提供合適的診斷信息來展示如何實現(xiàn)不變性。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/37510.html
摘要:簡單復合對象復合對象也可被稱為容器。它難以序列化對象并像這樣初始化來重建。接口仍然會導致多種方法計算。還要注意一些不完全遵循點規(guī)則的方法功能。逐步增加項目的方法和一步加載所有項目的方法是一樣的。另一個方法就是之前那樣的類定義。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python 在各個子類中實現(xiàn)__init_...
摘要:工廠類的函數(shù)就是包裝一些目標類層次結(jié)構(gòu)和復雜對象的構(gòu)造。連貫的工廠類接口在某些情況下,我們設計的類在方法使用上定義好了順序,按順序求方法的值很像函數(shù)。這個工廠類可以像下面這樣使用首先,我們創(chuàng)建一個工廠實例,然后我們使用那個實例創(chuàng)建實例。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python 通過工廠函數(shù)對 __init_...
摘要:同時,有多個類級別的靜態(tài)構(gòu)造函數(shù)的方法。這個累贅,無論如何,是被傳遞到每個單獨的對象構(gòu)造函數(shù)表達式中。我們可能只有幾個特定的擔憂,提供額外關(guān)鍵字參數(shù)給構(gòu)造函數(shù)。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python 沒有__init__()的無狀態(tài)對象 下面這個示例,是一個簡化去掉了__init__()的類。這是一個常見...
摘要:提議以下的新的生成器語法將被允許在生成器的內(nèi)部使用其中表達式作用于可迭代對象,從迭代器中提取元素。子迭代器而非生成器的語義被選擇成為生成器案例的合理泛化。建議如果關(guān)閉一個子迭代器時,引發(fā)了帶返回值的異常,則將該值從調(diào)用中返回給委托生成器。 導語: PEP(Python增強提案)幾乎是 Python 社區(qū)中最重要的文檔,它們提供了公告信息、指導流程、新功能的設計及使用說明等內(nèi)容。對于學習...
閱讀 1622·2019-08-30 13:18
閱讀 1599·2019-08-29 12:19
閱讀 2151·2019-08-26 13:57
閱讀 4171·2019-08-26 13:22
閱讀 1219·2019-08-26 10:35
閱讀 3017·2019-08-23 18:09
閱讀 2553·2019-08-23 17:19
閱讀 711·2019-08-23 17:18