__init__()方法意義重大的原因有兩個。第乙個原因是在物件生命週期中初始化是最重要的一步;每個物件必須正確初始化後才能正常工作。第二個原因是__init__()引數值可以有多種形式。
因為有很多種方式為__init__()提供引數值,對於物件建立有大量的用例,我們可以看看其中的幾個。我們想盡可能的弄清楚,因此我們需要定義乙個初始化來正確的描述問題區域。
在我們接觸__init__()方法之前,無論如何,我們都需要粗略、簡單地看看在python中隱含的object類的層次結構。
在這一章,我們看看不同形式的簡單物件的初始化(例如:打牌)。在這之後,我們還可以看看更複雜的物件,就像包含集合的hands物件以及包含策略和狀態的players。
隱含的超類——object
每乙個python類都隱含了乙個超類:object。它是乙個非常簡單的類定義,幾乎不做任何事情。我們可以建立object的例項,但是我們不能用它做太多,因為許多特殊的方法容易丟擲異常。
當我們自定義乙個類,object則為超www.cppcns.com類。下面是乙個類定義示例,它使用新的名稱簡單的繼承了object:
class x:
pass
下面是和自定義類的一些互動:
>>> x.__class__
>>> x.__class__.__base__
我們可以看到該類是type類的乙個物件,且它的基類為object。
就像在每個方法中看到的那樣,我們也看看從object繼承的預設行為。在某些情況下,超類特殊方法的行為是我們所想要的。在其他情況下,我們需要覆蓋這個特殊方法。
基類物件的init()方法
物件生命週期的基礎是建立、初始化和銷毀。我們將建立和銷毀的高階特殊方法推遲到後面的章節中,目前只關注初始化。
所有類的超類object,有乙個預設包含pass的__init__()實現,我們不需要去實現__init__()。如果不實現它,則在物件建立後就不會建立例項變數。在某些情況下,這種預設行為是可以接受的。
我們總是給物件新增屬性,該物件為基類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__()方法中設定所有屬性。至此我們走的很順利。乙個可選屬性其實就是乙個子類,只是沒有真正的正式宣告為子類。我們建立多型在某種程度上可能會引起混亂以及if語句的不恰當使用所造成的盤繞。雖然未初始化的屬性可能是有用的,但很有可能是糟糕設計的前兆。
《python之禪》中的建議:
"顯式比隱式更好。"
乙個__init__()方法應該讓例項變數顯式。
可憐的多型
靈活和愚蠢就在一念之間。
當我們覺得需要像下面這樣寫的時候,我們正從靈活的邊緣走向愚蠢:
if 'x' in self.__dict__:
或者:try:
self.x
except attributeerror:
是時候重新考慮api並新增乙個通用的方法或屬性。重構比新增if語句更明智。
在超類中實現init()
我們通過實現__init__()方法來初始化物件。當乙個物件被建立,python首先建立乙個空物件,然後為那個新物件呼叫__init__()方法。這個方法函式通常用來建立物件的例項變數並執行任何其他一次性處理。
下面是card類示例定義的層次結構。我們將定義card超類和三個子類,這三個子類是card的變種。兩個例項變數直接由引數值設定,兩個變數通過初始化方法計算:
cl card:
def __init__(self, rank, suit):
self.suit = suit
self.rank = rank
self.hard, self.soft = self._points()
cl 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。
這是一種常見的多型設計。每乙個子類都提供乙個唯一的_points()方法實現。所有子類都有相同的簽名:有相同的方法和屬性。這三個子類的物件在乙個應用程式中可以交替使用。
如果我們為花色使用簡單的字元,我們可以建立card例項,如下所示:
cards = [acecard('a', '?'), numbercard('2','?'), numbercard('3','?'),]
我們在列表中列舉出一些牌的類、牌值和花色。從長遠來說,我們需要更智慧型的工廠函式來建立card例項;用這個方法列舉52張牌無聊且容易出錯。在我們接觸工廠函式之前,我們看一些其他問題。
使用init()建立顯式常量
可以給牌定義花色類。在二十一點中,花色無關緊要,簡單的字串就可以。
我們使用花色建構函式作為建立常量物件的示例。在許多情況下,我們應用中小部分物件可以通過常量集合來定義。小部分的靜態物件可能是實現策略模式或狀態模式的一部分。
在某些情況下,我們會有乙個在初始化或配置檔案中建立的常量物件池,或者我們可以基於命令列引數建立常量物件。我們會在第十六章《通過命令進行複製》中獲取初始化設計和啟動設計的詳細資訊。
python沒有簡單正式的機制來定義乙個不可變物件,我們將在第三章《屬性訪問、方法屬性和描述符》看看保證不可變性的相關技術。在本示例中,花色不可變是有道理的。
下面這個類,我們將用於建立四個顯式常量:
class suit:
def __init__(self, name, symbol):
self.name= name
self.symbol= symbol
下面是通過這個類建立的常量:
club, diamond, heart, spade = suit('club','?'), suit('diamond','?'), suit('heart','?'), suit('spade','?')
現在我們可以通過下面展示的**片段建立cards:
cards = [acecard('a', spade), numbercard('2', spade), numbercard('3', spade),]
這個小示例,這種方法對於單個特性的花色**來說並不是乙個巨大的進步。在更複雜的情況下,會有一些策略或狀態物件通過這個方式建立。通過從小的、靜態的常量物件中復用可以使策略或狀態設計模式更有效率。
我們必須承認,在python中這些物件並不是技術上一成不變的,它是可變的。進行額外的編碼使得這些物件真正不變可能會有一些好處。
無關緊要的不變性
不變性很有吸引力但卻容易帶來麻煩。有時候被神話般的「惡意程式設計師」在他們的應用程式中通過修改常量值進行調整。從設計上考慮,這是非常愚蠢的。這些神話般的、惡意的程式設計師不會停止這樣做,因為已經沒有更好的方法去更簡潔簡單的在python中編碼。惡意程式設計師訪問到原始碼並且修改它僅僅是希望盡可能輕鬆地編寫**來修改乙個常數。
在定義不可變物件的類的時候最好不要掙扎太久。在第三章《屬性訪問、方法屬性和描述符》中,我們將通過在有bug的程式中提供合適的診斷資訊來展示如何實現不變性。
本文標題: 詳細解讀python中的__init__()方法
本文位址: /jiaoben/python/123941.html
python通俗解讀 Python中 init
python是物件導向的程式語言,因此我從class instance以及屬性 property attribute 的角度出發解釋。init 根據其英文意思 initialize 用來初始化乙個類 class 的新成員 instance 它是乙個constructor.當新成員被建立時,這個方程會...
詳細解讀Python中的 init
init 方法意義重大的原因有兩個。第乙個原因是在物件生命週期中初始化是最重要的一步 每個物件必須正確初始化後才能正常工作。第二個原因是init 引數值可以有多種形式。因為有很多種方式為init 提供引數值,對於物件建立有大量的用例,我們可以看看其中的幾個。我們想盡可能的弄清楚,因此我們需要定義乙個...
詳細解讀mysql 詳細解讀MySQL中的許可權
一 前言 很多文章中會說,資料庫的許可權按最小許可權為原則,這句話本身沒有錯,但是卻是一句空話。因為最小許可權,這個東西太抽象,很多時候你並弄不清楚具體他需要哪些許可權。現在很多mysql用著root賬戶在操作,並不是大家不知道用root許可權太大不安全,而是很多人並不知道該給予什麼樣的許可權既安全...