最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

簡明Python教程·面向對象編程&輸入與輸出

2023-02-14 08:00 作者:琉璃汐陽  | 我要投稿

上一篇專欄

面向對象編程?

在至今我們編寫的所有程序中,我們曾圍繞函數(shù)設計我們的程序,也就是那些能夠處理數(shù)據(jù) 的代碼塊。這被稱作面向過程(Procedure-oriented)的編程方式。還有另外一種組織起你的 程序的方式,它將數(shù)據(jù)與功能進行組合,并將其包裝在被稱作“對象”的東西內。在大多數(shù)情況下,你可以使用過程式編程,但是當你需要編寫一個大型程序或面對某一更適合此方法的問 題時,你可以考慮使用面向對象式的編程技術。

類與對象是面向對象編程的兩個主要方面。一個類(Class)能夠創(chuàng)建一種新的類型 (Type),其中對象(Object)就是類的實例(Instance)??梢赃@樣來類比:你可以擁有 類型 int 的變量,也就是說存儲整數(shù)的變量是 int 類的實例(對象)。?

針對靜態(tài)編程語言程序員的提示

請注意,即使是整數(shù)也會被視為對象( int 類的對象)。這不同于 C++ 與 Java(1.5 版之前),在它們那兒整數(shù)是原始內置類型。1

?有關類的更多詳細信息,請參閱 help(int) 。

?C# 與 Java 1.5 程序員會發(fā)現(xiàn)這與裝箱與拆箱(Boxing and Unboxing)概念 2 頗有相似 之處。

?對象可以使用屬于它的普通變量來存儲數(shù)據(jù)。這種從屬于對象或類的變量叫作字段 (Field)。對象還可以使用屬于類的函數(shù)來實現(xiàn)某些功能,這種函數(shù)叫作類的方法 (Method)。這兩個術語很重要,它有助于我們區(qū)分函數(shù)與變量,哪些是獨立的,哪些又是 屬于類或對象的??傊侄闻c方法通稱類的屬性(Attribute)。

字段有兩種類型——它們屬于某一類的各個實例或對象,或是從屬于某一類本身。它們被分 別稱作實例變量(Instance Variables)與類變量(Class Variables)。

?通過 class 關鍵字可以創(chuàng)建一個類。這個類的字段與方法可以在縮進代碼塊中予以列出。


?self?

類方法與普通函數(shù)只有一種特定的區(qū)別——前者必須有一個額外的名字,這個名字必須添加 到參數(shù)列表的開頭,但是你不用在你調用這個功能時為這個參數(shù)賦值,Python 會為它提供。 這種特定的變量引用的是對象本身,按照慣例,它被賦予 self 這一名稱。

盡管你可以為這一參數(shù)賦予任何名稱,但是強烈推薦你使用 self 這一名稱——其它的任何 一種名稱絕對會引人皺眉。使用一個標準名稱能帶來諸多好處——任何一位你的程序的讀者 能夠立即認出它,甚至是專門的 IDEIntegrated Development Environments,集成開發(fā)環(huán)境)也可以為你提供幫助,只要你使用了 self 這一名稱。

針對 C++/Java/C# 程序員的提示

Python 中的 self 相當于 C++ 中的指針以及 Java C# 中的 this 指針。?

你一定會在想 Python 是如何給 self 賦值的,以及為什么你不必給它一個值。一個例子或許 會讓這些疑問得到解答。假設你有一個 MyClass 的類,這個類下有一個實例 myobject 。當 你調用一個這個對象的方法,如 myobject.method(arg1, arg2) 時,Python 將會自動將其轉 換成 MyClass.method(myobject, arg1, arg2) ——這就是 self 的全部特殊之處所在。

這同時意味著,如果你有一個沒有參數(shù)的功能,你依舊必須擁有一個參數(shù)—— self 。


最簡單的類(Class)可以通過下面的案例來展示(保存為 oop_simplestclass.py ):

輸出:

它是如何工作的?

我們通過使用 class 語句與這個類的名稱來創(chuàng)建一個新類。在它之后是一個縮進的語句塊, 代表這個類的主體。在本案例中,我們創(chuàng)建的是一個空代碼塊,使用 pass 語句予以標明。

然后,我們通過采用類的名稱后跟一對括號的方法,給這個類創(chuàng)建一個對象(或是實例,我 們將在后面的章節(jié)中了解有關實例的更多內容)。為了驗證我們的操作是否成功,我們通過 直接將它們打印出來來確認變量的類型。結果告訴我們我們在 Person 類的 __main__ 模塊 中擁有了一個實例。?

要注意到在本例中還會打印出計算機內存中存儲你的對象的地址。案例中給出的地址會與你 在你的電腦上所能看見的地址不相同,因為 Python 會在它找到的任何空間來存儲對象。

?

方法

面向對象編程 100我們已經(jīng)在前面討論過類與對象一如函數(shù)那般都可以帶有方法(Method),唯一的不同在于 我們還擁有一個額外的 self 變量?,F(xiàn)在讓我們來看看下面的例子(保存為 oop_method.py ):

輸出:

它是如何工作的?

這里我們就能看見 self 是如何行動的了。要注意到 say_hi 這一方法不需要參數(shù),但是依 舊在函數(shù)定義中擁有 self 變量。


?__init__ 方法?

Python 的類中,有不少方法的名稱具有著特殊的意義?,F(xiàn)在我們要了解的就是 __init__ 方法的意義。

??__init__??方法會在類的對象被實例化(Instantiated)時立即運行。這一方法可以對任何你想 進行操作的目標對象進行初始化(Initialization)操作。這里你要注意在 init 前后加上的雙下 劃線。

?案例(保存為 oop_init.py ):

輸出:

它是如何工作的?

在本例中,我們定義 __init__ 方法用以接受 name 參數(shù)(與更普遍的 self 一道)。在這 里,我們創(chuàng)建了一個字段,同樣稱為 name 。要注意到盡管它們的名字都是“name”,但這是 兩個不相同的變量。雖說如此,但這并不會造成任何問題,因為點號 self.name 意味著這個 叫作“name”的東西是某個叫作“self”的對象的一部分,而另一個 name 則是一個局部變量。由 于我們已經(jīng)如上這般明確指出了我們所指的是哪一個名字,所以它不會引發(fā)混亂。?

當我們在 Person 類下創(chuàng)建新的實例 p 時,我們采用的方法是先寫下類的名稱,后跟括在括號中的參數(shù),形如: p = Person('Swaroop') 。?

我們不會顯式地調用 __init__ 方法。 這正是這個方法的特殊之處所在。?

現(xiàn)在,我們可以使用我們方法中的 self.name 字段了,使用的方法在 say_hi 方法中已經(jīng)作過說明。


類變量與對象變量?3

我們已經(jīng)討論過了類與對象的功能部分(即方法),現(xiàn)在讓我們來學習它們的數(shù)據(jù)部分。數(shù) 據(jù)部分——也就是字段——只不過是綁定(Bound)到類與對象的命名空間(Namespace) 的普通變量。這就代表著這些名稱僅在這些類與對象所存在的上下文中有效。這就是它們被 稱作“命名空間”的原因。

?字段(Filed)有兩種類型——類變量與對象變量,它們根據(jù)究竟是類還是對象擁有這些變量 來進行分類。

類變量(Class Variable)是共享的(Shared)——它們可以被屬于該類的所有實例訪問。 該類變量只擁有一個副本,當任何一個對象對類變量作出改變時,發(fā)生的變動將在其它所有 實例中都會得到體現(xiàn)。

?對象變量(Object variable)由類的每一個獨立的對象或實例所擁有。在這種情況下,每個 對象都擁有屬于它自己的字段的副本,也就是說,它們不會被共享,也不會以任何方式與其 它不同實例中的相同名稱的字段產(chǎn)生關聯(lián)。下面一個例子可以幫助你理解(保存為 oop_objvar.py ):

輸出:

它是如何工作的?

這是一個比較長的案例,但是它有助于展現(xiàn)類與對象變量的本質。在本例中, population 屬 于 Robot 類,因此它是一個類變量。 name 變量屬于一個對象(通過使用 self 分配),因 此它是一個對象變量。

因此,我們通過 Robot.population 而非 self.population 引用 population 類變量。我們對 于 name 對象變量采用 self.name 標記法加以稱呼,這是這個對象中所具有的方法。要記住 這個類變量與對象變量之間的簡單區(qū)別。同時你還要注意當一個對象變量與一個類變量名稱 相同時,類變量將會被隱藏。

?除了 Robot.popluation ,我們還可以使用 self.__class__.population ,因為每個對象都通過 self.__class__ 屬性來引用它的類。

? how_many 實際上是一個屬于類而非屬于對象的方法。這就意味著我們可以將它定義為一個 classmethod(類方法) 或是一個 staticmethod(靜態(tài)方法) ,這取決于我們是否知道我們需不需 要知道我們屬于哪個類。由于我們已經(jīng)引用了一個類變量,因此我們使用 classmethod(類方法)

我們使用裝飾器(Decorator)how_many 方法標記為類方法。?

你可以將裝飾器想象為調用一個包裝器(Wrapper)函數(shù)的快捷方式,因此啟用 @classmethod 裝飾器等價于調用:?

你會觀察到 __init__ 方法會使用一個名字以初始化 Robot 實例。在這一方法中,我們將 population 按 1 往上增長,因為我們多增加了一臺機器人。你還會觀察到 self.name 的值 是指定給每個對象的,這體現(xiàn)了對象變量的本質。?

你需要記住你只能使用 self 來引用同一對象的變量與方法。這被稱作屬性引用(Attribute Reference)。

?在本程序中,我們還會看見針對類和方法的 文檔字符串(DocStrings) 的使用方式。我們可 以在運行時通過 Robot.__doc__ 訪問類的 文檔字符串,對于方法的文檔字符串,則可以使用 Robot.say_hi.__doc__ 。?

die 方法中,我們簡單地將 Robot.population 的計數(shù)按 1 向下減少。

?所有的類成員都是公開的。但有一個例外:如果你使用數(shù)據(jù)成員并在其名字中使用雙下劃線 作為前綴,形成諸如 __privatevar 這樣的形式,Python 會使用名稱調整(Name-mangling)來使其有效地成為一個私有變量。

?因此,你需要遵循這樣的約定:任何在類或對象之中使用的變量其命名應以下劃線開頭,其 它所有非此格式的名稱都將是公開的,并可以為其它任何類或對象所使用。請記得這只是一 個約定,Python 并不強制如此(除了雙下劃線前綴這點)。

針對 C++/Java/C# 程序員的提示?

所有類成員(包括數(shù)據(jù)成員)都是公開的,并且 Python 中所有的方法都是虛擬的 (Virtual)。

?

繼承

面向對象編程的一大優(yōu)點是對代碼的重用(Reuse),重用的一種實現(xiàn)方法就是通過繼承 (Inheritance)機制。繼承最好是想象成在類之間實現(xiàn)類型與子類型(Type and Subtype) 關系的工具。

現(xiàn)在假設你希望編寫一款程序來追蹤一所大學里的老師和學生。有一些特征是他們都具有 的,例如姓名、年齡和地址。另外一些特征是他們獨有的,一如教師的薪水、課程與假期, 學生的成績和學費。

你可以為每一種類型創(chuàng)建兩個獨立的類,并對它們進行處理。但增添一條共有特征就意味著 將其添加進兩個獨立的類。這很快就會使程序變得笨重。

?一個更好的方法是創(chuàng)建一個公共類叫作 SchoolMember ,然后讓教師和學生從這個類中繼承 (Inherit),也就是說他們將成為這一類型(類)的子類型,而我們就可以向這些子類型中添 加某些該類獨有的特征。

這種方法有諸多優(yōu)點。如果我們增加或修改了 SchoolMember 的任何功能,它將自動反映在子類型中。舉個例子,你可以通過簡單地向 SchoolMember 類進行操作,來為所有老師與學生 添加一條新的 ID 卡字段。不過,對某一子類型作出的改動并不會影響到其它子類型。另一大優(yōu)點是你可以將某一老師或學生對象看作 SchoolMember 的對象并加以引用,這在某些情況下 會大為有用,例如清點學校中的成員數(shù)量。這被稱作多態(tài)性(Polymorphism),在任何情 況下,如果父類型希望,子類型都可以被替換,也就是說,該對象可以被看作父類的實例。

同時還需要注意的是我們重用父類的代碼,但我們不需要再其它類中重復它們,當我們使用 獨立類型時才會必要地重復這些代碼。

在上文設想的情況中, SchoolMember 類會被稱作基類(Base Class4或是超類 (Superclass)。 Teacher Student 類會被稱作派生類(Derived Classes5或是子類 (Subclass)。

我們將通過下面的程序作為案例來進行了解(保存為 oop_subclass.py ):

輸出:

它是如何工作的?

要想使用繼承,在定義類 時我們需要在類后面跟一個包含基類名稱的元組。然后,我們會注 意到基類的 __init__ 方法是通過 self 變量被顯式調用的,因此我們可以初始化對象的基 類部分。下面這一點很重要,需要牢記——因為我們在 Teacher Student 子類中定義了 __init__ 方法,Python 不會自動調用基類 SchoolMember 的構造函數(shù),你必須自己顯式地 調用它。?

相反,如果我們沒有在一個子類中定義一個 __init__ 方法,Python 將會自動調用基類的構 造函數(shù)。

我們會觀察到,我們可以通過在方法名前面加上基類名作為前綴,再傳入 self 和其余變 量,來調用基類的方法。

在這里你需要注意,當我們使用 SchoolMember 類的 tell 方法時,我們可以將 Teacher Student 的實例看作 SchoolMember 的實例。?

同時,你會發(fā)現(xiàn)被調用的是子類型的 tell 方法,而不是 SchoolMember tell 方法。理 解這一問題的一種思路是 Python 總會從當前的實際類型中開始尋找方法,在本例中即是如 此。如果它找不到對應的方法,它就會在該類所屬的基本類中依順序逐個尋找屬于基本類的 方法,這個基本類是在定義子類時后跟的元組指定的。?

這里有一條有關術語的注釋——如果繼承元組(Inheritance Tuple)中有超過一個類,這種情 況就會被稱作多重繼承(Multiple Inheritance)。?

end 參數(shù)用在超類的 tell() 方法的 print 函數(shù)中,目的是打印一行并允許下一次打印在 同一行繼續(xù)。這是一個讓 print 能夠不在打印的末尾打印出 \n (新行換行符)符號的小竅門。


?總結

我們已經(jīng)探索了有關類和對象的各個方面,還有與它們相關的各類術語。我們還了解了面向 對象編程的益處與陷阱。Python 是高度面向對象的,從長遠來看,了解這些概念對你大有幫 助。

接下來,我們將學習如何處理輸入與輸出,以及如何在 Python 中訪問文件。

  1. 原文作 Primitive native types,沈潔元譯本表達為“把整數(shù)純粹作為類型”。Primitive type 翻譯作“原始類型”,也稱作“內置類型”,因此此處也可以翻譯成“基本內置類型”。 ??

  2. 沈潔元譯本譯作“封裝與解封裝”。 ?

  3. 本節(jié)標題原文作 Class And Object Variables,沈潔元譯本譯作“類與對象的方法”。 ??

  4. 沈潔元譯本譯作“基本類”。 ?

  5. ?沈潔元譯本譯作“導出類”。 ??

  6. 此處的類即派生類或子類。 ?

輸入與輸出?

有些時候你的程序會與用戶產(chǎn)生交互。舉個例子,你會希望獲取用戶的輸入內容,并向用戶 打印出一些返回的結果。我們可以分別通過 input() 函數(shù)與 print 函數(shù)來實現(xiàn)這一需求。

對于輸入,我們還可以使用 str String,字符串)類的各種方法。例如,你可以使用 rjust 方法來獲得一個右對齊到指定寬度的字符串。你可以查看 help(str) 來了解更多細節(jié)。

另一個常見的輸入輸出類型是處理文件。創(chuàng)建、讀取與寫入文件對于很多程序來說是必不可 少的功能,而我們將在本章探討這一方面。?


用戶輸入內容?

將以下程序保存為 io_input.py

輸出:

它是如何工作的?

我們使用切片功能翻轉文本。我們已經(jīng)了解了我們可以通過使用 seq[a:b] 來從位置 a 開 始到位置 b 結束來對序列進行切片 。我們同樣可以提供第三個參數(shù)來確定切片的步長 (Step)。默認的步長為 1 ,它會返回一份連續(xù)的文本。如果給定一個負數(shù)步長,如 -1 , 將返回翻轉過的文本。

?input() 函數(shù)可以接受一個字符串作為參數(shù),并將其展示給用戶。爾后它將等待用戶輸入內 容或敲擊返回鍵。一旦用戶輸入了某些內容并敲下返回鍵, input() 函數(shù)將返回用戶輸入的文本。?

我們獲得文本并將其進行翻轉。如果原文本與翻轉后的文本相同,則判斷這一文本是回文。

?作業(yè)練習?

要想檢查文本是否屬于回文需要忽略其中的標點、空格與大小寫。例如,“Rise to vote, sir.”是 一段回文文本,但是我們現(xiàn)有的程序不會這么認為。你可以改進上面的程序以使它能夠識別 這段回文嗎??

如果你需要一些提示,那么這里有一個想法…… 1


文件

你可以通過創(chuàng)建一個屬于 file 類的對象并適當使用它的 read 、 readline 、 write 方法 來打開或使用文件,并對它們進行讀取或寫入。讀取或寫入文件的能力取決于你指定以何種 方式打開文件。最后,當你完成了文件,你可以調用 close 方法來告訴 Python 我們已經(jīng)完 成了對該文件的使用。

?案例(保存為 io_using_file.py ):?

輸出:

它是如何工作的?

首先,我們使用內置的 open 函數(shù)并指定文件名以及我們所希望使用的打開模式來打開一個 文件。打開模式可以是閱讀模式( 'r' ),寫入模式( 'w' )和追加模式( 'a' )。我們還 可以選擇是通過文本模式( 't' )還是二進制模式( 'b' )來讀取、寫入或追加文本。實際 上還有其它更多的模式可用, help(open) 會給你有關它們的更多細節(jié)。在默認情況 下, open() 會將文件視作文本(text)文件,并以閱讀(read)模式打開它。

在我們的案例中,我們首先采用寫入模式打開文件并使用文件對象的 write 方法來寫入文 件,并在最后通過 close 關閉文件。?

接下來,我們重新在閱讀模式下打開同一個文件。我們不需要特別指定某種模式,因為“閱讀 文本文件”是默認的。我們在循環(huán)中使用 readline 方法來讀取文件的每一行。這一方法將會 一串完整的行,其中在行末尾還包含了換行符。當一個空字符串返回時,它表示我們已經(jīng)到 達了文件末尾,并且通過 break 退出循環(huán)。

最后,我們最終通過 close 關閉了文件。

現(xiàn)在,你可以檢查 poem.txt 文件的內容來確認程序確實對該文件進行了寫入與讀取操作。


Pickle 2

Python 提供了一個叫作 Pickle 的標準模塊,通過它你可以將任何純 Python 對象存儲到一 個文件中,并在稍后將其取回。這叫作持久地(Persistently)存儲對象。?

案例(保存為 io_pickle.py ):

輸出:

它是如何工作的

要想將一個對象存儲到一個文件中,我們首先需要通過 open 以寫入(write)二進制 (binary)模式打開文件,然后調用 pickle 模塊的 dump 函數(shù)。這一過程被稱作封裝 (Pickling)。

接著,我們通過 pickle 模塊的 load 函數(shù)接收返回的對象。這個過程被稱作拆封 (Unpickling)。


?Unicode 3

截止到現(xiàn)在,當我們編寫或使用字符串、讀取或寫入某一文件時,我們用到的只是簡單的英 語字符。

?注意:如果你正在使用 Python 2,我們又希望能夠讀寫其它非英語語言,我們需要使用 unicode 類型,它全都以字母 u 開頭,例如 u"hello world" 。

當我們閱讀或寫入某一文件或當我們希望與互聯(lián)網(wǎng)上的其它計算機通信時,我們需要將我們 的 Unicode 字符串轉換至一個能夠被發(fā)送和接收的格式,這個格式叫作“UTF-8”。我們可以在 這一格式下進行讀取與寫入,只需使用一個簡單的關鍵字參數(shù)到我們的標準 open 函數(shù)中:

它是如何工作的?

現(xiàn)在你可以忽略 import 語句,我們會在模塊章節(jié)章節(jié)探討有關它的更多細節(jié)。?

每當我們諸如上面那番使用 Unicode 字面量編寫一款程序時,我們必須確保 Python 程序已經(jīng) 被告知我們使用的是 UTF-8,因此我們必須將 # encoding=utf-8 這一注釋放置在我們程序的 頂端。4?

我們使用 io.open 并提供了“編碼(Encoding)”與“解碼(Decoding)”參數(shù)來告訴 Python 我們正在使用 Unicode。?

你可以閱讀以下文章來了解有關這一話題的更多內容:

  • ?"The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets"?

  • Python Unicode Howto?

  • Pragmatic Unicode talk by Nat Batchelder?


總結

我們已經(jīng)討論了有關輸入和輸出的多種類型,這些內容有關文件處理,有關 pickle 模塊還有 關于 Unicode。

?接下來,我們將探索一些異常的概念。?

  1. 使用一個元組(你可以在這里找到一份列出所有標點符號的列表)來保存所有需要禁 用的字符,然后使用成員資格測試來確定一個字符是否應該被移除,即 forbidden = ( ! , ? , . , ...)?!瓡???

  2. 沈潔元譯本將本節(jié)標題譯作“儲存器”,兩版原書在本節(jié)的標題相同,但是內容已大不相同。???

  3. Unicode 有“統(tǒng)一碼”“萬國碼”“國際碼”等多種譯名。出于交流習慣的考慮,此處全部采 用原文。 ??

  4. 可能你已經(jīng)注意到,在前面章節(jié)的一些程序文件中開頭標注了采用 UTF-8 編碼。這是 在中文版翻譯過程中為了修改程序中使用三引號括起的說明性字符串,同時要保證程序 代碼能被 Python 正常識別而作出的改動。 ?

下一篇專欄


簡明Python教程·面向對象編程&輸入與輸出的評論 (共 條)

分享到微博請遵守國家法律
正安县| 西贡区| 陇西县| 武城县| 广元市| 革吉县| 尼玛县| 喜德县| 孟州市| 海阳市| 牙克石市| 崇州市| 华容县| 沂南县| 漠河县| 朝阳区| 祁东县| 鞍山市| 公安县| 清水河县| 闸北区| 凉城县| 五大连池市| 阳高县| 太谷县| 祥云县| 平和县| 乌鲁木齐县| 晋江市| 曲阜市| 芜湖县| 宜川县| 松江区| 连城县| 黑水县| 双江| 金门县| 辽阳县| 桑日县| 铁岭市| 台东市|