国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

Python的對象與型式

2017-09-28 19:02喬林
計算機(jī)教育 2017年9期
關(guān)鍵詞:型式對象

喬林

摘 要:作為動態(tài)語言,Python與其他靜態(tài)編譯語言有很大差別,其對象與型式的概念及關(guān)系非常讓人迷惑。文章討論P(yáng)ython程序設(shè)計語言中對象、型式與量的基本概念和關(guān)系,闡釋這些概念的內(nèi)涵與外延,指出初學(xué)者容易混淆之處,并給出教學(xué)過程中的一點心得體會。

關(guān)鍵詞:Python;對象;型式;量

0 引 言

對于Python程序設(shè)計語言對象與型式的概念、關(guān)系等基本概念,如果沒有清晰的認(rèn)知,那么所編寫的Python程序(腳本)重者無法運(yùn)行或者得到錯誤的結(jié)果,輕者可能隱含難以察覺和調(diào)試的邏輯錯誤。因此,了解Python程序設(shè)計語言中對象、型式與量的基本概念和關(guān)系,掌握文字與量、名空間與作用域、全局量與局部量的概念以及這些基礎(chǔ)概念對量(對象)可能造成的影響至關(guān)重要。

1 對象與型式

1.1 對 象

對象(object)是一種數(shù)據(jù)抽象或數(shù)據(jù)結(jié)構(gòu)抽象,用來表示程序中需要處理或已處理的信息。在Python程序設(shè)計語言中,對象具有3個基本特征:本征值(identity)、型式(type)和值(value)。

本征值是用于區(qū)分不同對象的信息,因而特征之一是應(yīng)具有唯一性。在Python程序設(shè)計語言中,本征值的表示方式與Python程序設(shè)計語言的具體實現(xiàn)有關(guān),一種典型的實現(xiàn)策略是使用對象在內(nèi)存中的存儲地址,如CPython的實現(xiàn)。本征值的另外一個特征是有常性(immutability),即一經(jīng)創(chuàng)設(shè)就不可改變。

在Python程序設(shè)計語言中,可以使用本征值函數(shù)id()返回某個特定對象的本征值,如在Cpython實現(xiàn)中,id(1)與id(obj)分別返回對象1與對象obj的本征值。相應(yīng)地,也可以使用型式函數(shù)type()獲取某個對象的型式。

在Python程序設(shè)計語言中,依據(jù)該對象是否可被改變,而分為有常對象(immutable)和無常對象(mutable)。

一般而言,Python程序設(shè)計語言中的對象無常性由其型式確定。典型的示例,如數(shù)值(numeric)、字符串(string)和元組(tuple)為有常對象,詞典(dictionary)和列表(list)為無常對象;此外,有常容器(container)對象可能包含無常元素對象,前者值不可變,后者則不然。對于這兩點,初學(xué)者必須時刻保持警覺,教師在教學(xué)過程中也必須闡釋清楚。

1.2 型 式

型式(type),簡稱型,也稱類型。在純面向?qū)ο笳Z言出現(xiàn)之前,type用來表示相同性質(zhì)的數(shù)據(jù)集合。該集合雖然具有明確的操作集,厘定了可在該集合上實施的操作,但是并未在語言層面上對其進(jìn)行明晰的操作集定義,即數(shù)據(jù)及其操作是分離的。

型,其最主要的目的是構(gòu)造該型式的對象。這意味著任何對象都必須有確切的型式,且一般不可改變。

在面向?qū)ο蠹夹g(shù)出現(xiàn)之后,程序設(shè)計語言一般使用專用關(guān)鍵字來表示特定的將數(shù)據(jù)與操作辯證統(tǒng)一的數(shù)據(jù)結(jié)構(gòu)——類,如C++程序設(shè)計語言中的關(guān)鍵字class(含擴(kuò)充定義的struct)。在類中,對象屬性(attribute)和行為(behavior)被統(tǒng)一描述和管理:對象屬性是類的數(shù)據(jù)成員;對象行為是該類或該類的某個對象上可執(zhí)行的操作成員,也稱為方法(method)。

語言學(xué)上,class的翻譯為“類”,作為型(type)的一種,也可以稱為“類型”。這使得其與早期術(shù)語type之間,容易出現(xiàn)一定混淆——早期非class類型的type也被翻譯成“類型”。為避免引起誤解,將type更正為“型式”更佳,有助于區(qū)分class與type ——兩者在程序設(shè)計語言層面上并非同一概念。

Python程序設(shè)計語言作為一種純粹的面向?qū)ο笳Z言,凡物皆為對象,這導(dǎo)致學(xué)生在學(xué)習(xí)時會面臨以下兩方面的困難。

(1)class與type的本質(zhì)完全相同,類即為型,而型亦為類。此時,討論其他編程語言中這兩者的差異,就沒有任何意義。因此,很多學(xué)習(xí)過其他面向?qū)ο笳Z言的學(xué)生在學(xué)習(xí)Python程序設(shè)計語言時,反而會面臨概念理解上的困難。這一點,授課教師必須在教學(xué)過程中表述清楚,以減少學(xué)生的困惑。

(2)型也是可以在程序中操作的對象??梢哉J(rèn)為,型就是構(gòu)造對象的模板,然而在實際語言實現(xiàn)中,存在這樣一種情況,即一個對象本身實際上可以作為該型另外一個對象的模板。這意味著,型本身也可以作為對象來存儲和管理,并在程序運(yùn)行過程中作為模板,用于構(gòu)造該型的對象。因此,我們可以將程序編譯從靜態(tài)引向動態(tài)。

示例代碼一:

>>> id(int)

1707211232

>>> type(int)

>>> id(float)

1707205632

>>> type(float)

>>> int is float

False

# 試試將一個浮點型賦值給一個整型

>>> int = float

>>> id(int)

1707205632

>>> type(int)

>>> id(float)

1707205632

>>> type(float)

>>> int is float

True

>>>

1.3 型之相

在Python程序設(shè)計語言中,類具有明確的型。類的定義語句負(fù)責(zé)創(chuàng)建(構(gòu)造)一個類型,而類型用于創(chuàng)建(構(gòu)造)該類型的對象(object)。

構(gòu)造類的一個實際對象的過程稱為具象化(instantiation),也稱為實例化。實例化的結(jié)果為具象(instance object),也稱實例對象。

實際上,在Python程序設(shè)計語言中,類定義結(jié)束時,系統(tǒng)將構(gòu)造(創(chuàng)建)出該類的一個型象。如前所述,型亦為對象,因此,按照此型定義出的對象稱為對象(class object),也稱類象或類對象。

對于初學(xué)者而言,這種概念上的差別非常容易讓人迷惑。類(型)本身就是構(gòu)造對象的模板,那么類對象或型對象是什么?在教學(xué)過程中,教師必須特別強(qiáng)調(diào)Python程序設(shè)計語言的類(型)動態(tài)性,這一點與C語言和C++語言有很大的區(qū)別。因而,我們更傾向于將由類定義而創(chuàng)建(構(gòu)造)出來的類對象稱為型相,即使用“相”字區(qū)分實際對象的“象”字。

也就是說,對于任意一個類,其類定義創(chuàng)建(構(gòu)造)了該類的一個型相;而通過該型相,程序員可以創(chuàng)建(構(gòu)造)該類的具象——具體的象。對于前者,構(gòu)造型相的過程,我們稱為體化(型體化);對于后者,構(gòu)造具象的過程被稱為具象化(象化)。體化的書面意義是指“以自己的行動感化別人”,而“體”本身指“事物的本身或全部”“物質(zhì)存在的狀態(tài)或形狀”或“事物的格局、規(guī)矩”,因此用“體化”描述這個過程似乎更恰當(dāng)。

2 量與對象

2.1 文字與量

文字(literal)是內(nèi)置類型的有常值,類似于數(shù)學(xué)或物理中的常數(shù),如0、3.1 416、2.718 28j、“Python”等。一方面,在Python程序設(shè)計語言中,文字亦對象;另一方面,量(variable)是引用特定對象的標(biāo)識符,類似數(shù)學(xué)中的代數(shù)。

在Python程序設(shè)計語言中,量的處理相當(dāng)特殊。事實上,它已經(jīng)與C語言、C++語言等早期語言中的概念有了明顯差別。

首先,量可以隨意引用數(shù)值、字符串或其他類型的對象;其次,量是標(biāo)識符(identifier),是名稱(name),但并不是該對象本身,僅僅是對“象”的引用(reference),即對該“對象”的“象”的引用;最后,對于Python程序設(shè)計語言中的量而言,賦值(assignment)即定義(definition),因而并不需要在使用量前作預(yù)先定義。這意味著賦值的目的并不是將賦值操作符右邊表達(dá)式的結(jié)果,存入該量所對應(yīng)的存儲空間,而是將代表該量的名稱(name)與賦值操作符右邊表達(dá)式的結(jié)果所對應(yīng)的值,束定(bind)或重新束定(rebind)在一起,以修改無常對象的屬性或值。

這樣就引申出一個問題,即量的同一性(identity)問題——我們?nèi)绾闻袛鄡蓚€量是否引用了同一個對象。雖然Python程序設(shè)計語言提供兩個關(guān)鍵字(操作符)is和is not,用于測試量的同一性,但是實際問題是量的同一性依賴于Python程序設(shè)計語言的具體實現(xiàn),如我們可以認(rèn)定a = [] 與b = [] 總是引用不同的有常對象(空列表),a = b = [] 總是引用同一有常對象,卻并不能保證a = 1與b = 1是否引用了值為1的同一有常對象。

2.2 名空間與作用域

在概念上,名空間(namespace)是從名稱到對象的映射。在實現(xiàn)上,大多數(shù)名空間表現(xiàn)為符號表,以字典的形式來組織,而變量存儲其中。在Python腳本中存在多個相互獨立的名空間。

一般而言,名空間具有如下特性:

(1)標(biāo)識符獨立性:不同名空間的同名標(biāo)識符沒有任何關(guān)聯(lián);

(2)標(biāo)識符唯一性:同一名空間中的標(biāo)識符不得重名;

(3)名空間嵌套:一個名空間可以包含另外一個名空間。

在Python程序設(shè)計語言中,名空間分為內(nèi)置名空間(built-in namespace)、全局名空間和局部名空間這3類。其中,內(nèi)置名空間為內(nèi)置名稱集合,如內(nèi)置函數(shù)名、內(nèi)置異常名等;全局名空間為模塊內(nèi)部的全局名稱集合;而局部名空間為函數(shù)調(diào)用時的本地名稱集合、對象的屬性集合、嵌套函數(shù)的本地名稱集合或類成員函數(shù)的本地名稱集合等。

需要說明的是,全局名空間也稱模塊全局名空間,在讀入模塊定義時創(chuàng)建,且正常情況下在Python解釋器退出時刪除。此處所說的“全局”僅指在該模塊內(nèi)部為全局的。每個模塊都有獨立的全局名空間,導(dǎo)入模塊后方可訪問其中的全局標(biāo)識符;同時,模塊中可能存在非全局標(biāo)識符,即使導(dǎo)入模塊也不可訪問,這一點是初學(xué)者很容易犯糊涂的地方,教師在教學(xué)時須予以重視。

另一個相關(guān)的概念是作用域(scope)。定義上,作用域是指可訪問名空間中標(biāo)識符的文法區(qū)域,即在Python程序文本的某處,是否可以使用該名空間中的標(biāo)識符。

雖然作用域與名空間在概念上有強(qiáng)關(guān)聯(lián),但是二者并不相同。一般認(rèn)為,不在某名空間中,就不能訪問該名空間中的標(biāo)識符;同時,在某名空間中,也不一定能訪問該名空間中的標(biāo)識符。

在Python程序設(shè)計語言中,作用域分為局部作用域、外層函數(shù)閉包作用域、全局作用域和內(nèi)置作用域這4類。其中,局部作用域位于最內(nèi)層,為函數(shù)(類成員函數(shù))、類或Lambda表達(dá)式形成的文法區(qū)域;外層函數(shù)閉包作用域為嵌套函數(shù)的外層函數(shù)形成的文法區(qū)域;全局作用域為模塊形成的文法區(qū)域;內(nèi)置作用域位于最外層,為包含內(nèi)置名稱的文法區(qū)域。

在進(jìn)行標(biāo)識符查找時,按照由內(nèi)向外的順序查找上述4種作用域。

2.3 全局量與局部量

一般將定義于類、函數(shù)、類成員函數(shù)或Lambda表達(dá)式之外場合的量稱為全局量;將定義于函數(shù)、類成員函數(shù)或Lambda表達(dá)式中的量稱為局部量。函數(shù)的形式參數(shù)與局部量類似,但是出于參數(shù)傳遞的原因,其與普通局部量有細(xì)微差異。

需要說明的是,全局量與局部量位于不同的名空間,因而可重名;同時,全局量和局部量重名時,局部量可能遮蓋全局量的作用域,使其不可見。endprint

在賦值即定義的原則下,Python程序設(shè)計語言中,全局量和局部量的差別經(jīng)常讓初學(xué)者迷惑,如以下3段代碼。

示例代碼二:

# n為全局量,位于全局名空間,其后代碼(包括函數(shù)內(nèi)部)均可訪問

n = 42

# 形式參數(shù)x也為局部量,位于局部名空間,函數(shù)內(nèi)部可訪問

def double(x):

# 訪問全局量n

print( "Before being doubled in double(): n = ", n )

# m為局部量,位于局部名空間,函數(shù)內(nèi)部可訪問

m = x * 2

print( "After being doubled in double(): m = ", m )

print( "After being doubled in double(): n = ", n )

return m

print( "Before calling double() in __main__: n = ", n )

# m為全局量,位于全局名空間,與函數(shù)內(nèi)部m為獨立的兩個對象

m = double(n)

print( "After calling double() in __main__: m = ", m )

print( "After calling double() in __main__: n = ", n )

示例代碼二的輸出結(jié)果如下:

# 調(diào)用函數(shù)前,全局量n值為42

Before calling double() in __main__: n = 42

# 調(diào)用函數(shù)中,全局量n值為42(加倍前)

Before being doubled in double(): n = 42

# 調(diào)用函數(shù)中,局部量m值為84(加倍后)

After being doubled in double(): m = 84

# 調(diào)用函數(shù)中,全局量n值為42(加倍后)

After being doubled in double(): n = 42

# 調(diào)用函數(shù)后,全局量m接受加倍值84

After calling double() in __main__: m = 84

# 調(diào)用函數(shù)后,全局量n值維持42不變

After calling double() in __main__: n = 42

在示例代碼二中,n為全局量;m既可能為全局量,又可能為局部量,這與其所在的文法區(qū)域有關(guān),如函數(shù)內(nèi)部的m為局部量,函數(shù)定義之后使用的m為全局量。

示例代碼三如下:

# n為全局量,位于全局名空間,其后代碼(包括函數(shù)內(nèi)部)均可訪問

n = 42

def double(x):

# 注釋下一條語句,否則無法束定局部量n,引發(fā)UnboundLocalError異常

# print( "Before being doubled in double(): n = ", n )

# 定義同名局部量n(賦值即定義),新對象具有局部作用域,整個函數(shù)內(nèi)部均有效

# 局部量n遮蓋同名全局量n的部分作用域,使其不可見

# 局部量n定義前雖不能訪問,但仍不允許上條注釋語句訪問全局量n

# 換言之,即使前述被注釋的那條語句出現(xiàn)在局部量n定義之前,n也被解釋為局部量

n = x * 2

print( "After being doubled in double(): n = ", n )

return n

print( "Before calling double() in __main__: n = ", n )

m = double(n)

print( "After calling double() in __main__: m = ", m )

print( "After calling double() in __main__: n = ", n )

示例代碼三的輸出結(jié)果如下:

# 調(diào)用函數(shù)前,全局量n值為42

Before calling double() in __main__: n = 42

# 調(diào)用函數(shù)中,局部量n值為 42

After being doubled in double(): n = 84

# 調(diào)用函數(shù)中,全局量m接受加倍值84

After calling double() in __main__: m = 84

# 調(diào)用函數(shù)后,全局量n維持原值42不變

# 即全局量n與局部量n雖同名,但不是同一對象

After calling double() in __main__: n = 42

在示例代碼三中,視定義n時的文法區(qū)域,n可能為全局量,也可能為局部量;m則為全局量。相關(guān)代碼解釋已列入程序注釋,此處不再贅述。

再看示例代碼四:

# n為全局量,位于全局名空間,其后代碼(包括函數(shù)內(nèi)部)均可訪問

n = 42

# 直接使用全局量n,無需傳遞參數(shù)endprint

def double():

# 聲明全局量n,函數(shù)內(nèi)部對其賦值不會構(gòu)造新的局部對象

global n

print( "Before being doubled in double(): n = ", n )

# 直接寫入全局量,而不是構(gòu)造新的局部對象

n = n * 2

print( "After being doubled in double(): n = ", n )

return n

print( "Before calling double() in __main__: n = ", n )

m = double()

print( "After calling double() in __main__: m = ", m )

print( "After calling double() in __main__: n = ", n )

示例代碼四的輸出結(jié)果如下:

# 調(diào)用函數(shù)前,全局量n值為42

Before calling double() in __main__: n = 42

# 調(diào)用函數(shù)中,全局量n值為 42(加倍前)

Before being doubled in double(): n = 42

# 調(diào)用函數(shù)中,全局量n值更新為84(加倍后)

After being doubled in double(): n = 84

# 調(diào)用函數(shù)后,全局量m接受加倍值84

After calling double() in __main__: m = 84

# 調(diào)用函數(shù)后,全局量n維持更新后的值84不變

After calling double() in __main__: n = 84

由于賦值定義的特性,要在Python函數(shù)內(nèi)部修改全局量的值,就必須使用global將其聲明為全局量,原因是在函數(shù)內(nèi)部可以自由引用全局量,但不能對其賦值——賦值隱含著構(gòu)造新的同名局部對象。

類似的關(guān)鍵字還有nonlocal,用于將其后標(biāo)識符解釋為非局部非全局的。查找標(biāo)識符時,Python解釋器將從最內(nèi)層嵌套名空間向外查找,一直到全局名空間(不含);若未找到該標(biāo)識符,系統(tǒng)將引發(fā)SyntaxError異常。

無論是global還是nonlocal,標(biāo)識符在其定義所在的代碼塊各處均有效,包括該聲明之前。

3 結(jié) 語

本文探討了Python程序設(shè)計語言中的對象、型式與量的基本概念和關(guān)系以及對象與型式的基本概念,指出了型相與具象的本質(zhì)及兩者之間的差異,并解釋了文字與量、名空間與作用域、全局量與局部量的概念,指出了這些基礎(chǔ)概念對量(對象)可能造成的影響。在實際編程時,對這些概念的理解偏差極大地影響程序的健壯性和正確性,因而需要在教學(xué)過程中予以詳盡的說明。endprint

猜你喜歡
型式對象
曬曬全國優(yōu)秀縣委書記擬推薦對象
攻略對象的心思好難猜
圖說車事
垂直電梯安全鉗受力分析及型式試驗
垂直電梯安全鉗受力分析及型式試驗
鋼板樁工藝溝槽支護(hù)施工方案
淺析新農(nóng)村河道生態(tài)護(hù)岸型式及選用
660MW火力發(fā)電廠直流系統(tǒng)蓄電池的選擇
個性簽名