Python 面向物件(一)
一.如何定義一個類
在進行python面向物件程式設計之前,先來了解幾個術語:類,類物件,例項物件,屬性,函式和方法。
類是對現實世界中一些事物的封裝,定義一個類可以採用下面的方式來定義:
class className:
block
注意類名後面有個冒號,在block塊裡面就可以定義屬性和方法了。當一個類定義完之後,就產生了一個類物件。類物件支援兩種操作:引用和例項化。引用操作是通過類物件去呼叫類中的屬性或者方法,而例項化是產生出一個類物件的例項,稱作例項物件。比如定義了一個people類:
class people: name = 'jack' #定義了一個屬性 #定義了一個方法 def printName(self): print (self.name)
people類定義完成之後就產生了一個全域性的類物件,可以通過類物件來訪問類中的屬性和方法了。當通過people.name(至於為什麼可以直接這樣訪問屬性後面再解釋,這裡只要理解類物件這個概念就行了)來訪問時,people.name中的people稱為類物件,這點和C++中的有所不同。當然還可以進行例項化操作,p=people( ),這樣就產生了一個people的例項物件,此時也可以通過例項物件p來訪問屬性或者方法了(p.name).
理解了類、類物件和例項物件的區別之後,我們來了解一下Python中屬性、方法和函式的區別。
在上面程式碼中註釋的很清楚了,name是一個屬性,printName( )是一個方法,與某個物件進行繫結的函式稱作為方法。一般在類裡面定義的函式與類物件或者例項物件綁定了,所以稱作為方法;而在類外定義的函式一般沒有同對象進行繫結,就稱為函式。
二.屬性
在類中我們可以定義一些屬性,比如:
class people:
name = 'jack'
age = 12
p = people()
print(p.name,p.age)
定義了一個people類,裡面定義了name和age屬性,預設值分別為'jack'和12。在定義了類之後,就可以用來產生例項化物件了,這句p = people( )例項化了一個物件p,然後就可以通過p來讀取屬性了。這裡的name和age都是公有的,可以直接在類外通過物件名訪問,如果想定義成私有的,則需在前面加2個下劃線 ' __'。
這段程式執行會報錯:
提示找不到該屬性,因為私有屬性是不能夠在類外通過物件名來進行訪問的。在Python中沒有像C++中public和private這些關鍵字來區別公有屬性和私有屬性,它是以屬性命名方式來區分,如果在屬性名前面加了2個下劃線'__',則表明該屬性是私有屬性,否則為公有屬性(方法也是一樣,方法名前面加了2個下劃線的話表示該方法是私有的,否則為公有的)。
Python提供了訪問私有屬性的方式,可用於程式的測試和除錯。
書寫格式:物件名._類名+私有成員名(寫程式時不建議使用,破壞了封裝性)
例如:(輸出內容為jack)
class people:
__name = 'jack'
age = 12
p = people()
print(p._people__name,p.age)
三.方法
在類中可以根據需要定義一些方法,定義方法採用def關鍵字,在類中定義的方法至少會有一個引數,,一般以名為'self'的變數作為該引數(用其他名稱也可以),而且需要作為第一個引數。下面看個例子:
class people:
__name = 'jack'
__age = 12
def getName(self):
return self.__name
def getAge(self):
return self.__age
p = people()
print(p.getName(),p.getAge())
如果對self不好理解的話,可以把它當做C++中類裡面的this指標一樣理解,就是物件自身的意思,在用某個物件呼叫該方法時,就將該物件作為第一個引數傳遞給self。
四.類中內建的方法
在Python中有一些內建的方法,這些方法命名都有比較特殊的地方(其方法名以2個下劃線開始然後以2個下劃線結束)。類中最常用的就是構造方法和析構方法。
構造方法__init__(self,....)在生成物件時呼叫,可以用來進行一些初始化操作,不需要顯示去呼叫,系統會預設去執行。構造方法支援過載,如果使用者自己沒有重新定義構造方法,系統就自動執行預設的構造方法。
析構方法__del__(self)在釋放物件時呼叫,支援過載,可以在裡面進行一些釋放資源的操作,不需要顯示呼叫。
還有其他的一些內建方法:
比如 __cmp__( ), __len( )__等,具體的用法可以參考這篇博文:
五.類屬性、例項屬性、類方法、例項方法以及靜態方法
在瞭解了類基本的東西之後,下面看一下python中這幾個概念的區別。
先來談一下類屬性和例項屬性
在前面的例子中我們接觸到的就是類屬性,顧名思義,類屬性就是類物件所擁有的屬性,它被所有類物件的例項物件所共有,在記憶體中只存在一個副本,這個和C++中類的靜態成員變數有點類似。對於公有的類屬性,在類外可以通過類物件和例項物件訪問。
class people:
name = 'jack' #公有的類屬性
__age = 12 #私有的類屬性
p = people()
print(p.name) #正確
print(people.name) #正確
print(p.__age) #錯誤,不能在類外通過例項物件訪問私有的類屬性
print(people.__age) #錯誤,不能在類外通過類物件訪問私有的類屬性
例項屬性是不需要在類中顯示定義的,比如:
class people:
name = 'jack'
p = people()
p.age =12
print(p.name) #正確
print(p.age) #正確
print(people.name) #正確
print(people.age) #錯誤
執行結果:
在類外對類物件people進行例項化之後,產生了一個例項物件p,然後p.age = 12這句給p添加了一個例項屬性age,賦值為12。這個例項屬性是例項物件p所特有的,注意,類物件people並不擁有它(所以不能通過類物件來訪問這個age屬性)。當然還可以在例項化物件的時候給age賦值。
class people:
name = 'jack'
#__init__()是內建的構造方法,在例項化物件時自動呼叫
def __init__(self,age):
self.age = age
p = people(12)
print(p.name) #正確
print(p.age) #正確
print(people.name) #正確
print(people.age) #錯誤
執行結果:
如果需要在類外修改類屬性,必須通過類物件去引用然後進行修改。如果通過例項物件去引用,會產生一個同名的例項屬性,這種方式修改的是例項屬性,不會影響到類屬性,並且之後如果通過例項物件去引用該名稱的屬性,例項屬性會強制遮蔽掉類屬性,即引用的是例項屬性,除非刪除了該例項屬性。
class people:
country = 'china'
print(people.country)
p = people()
print(p.country)
p.country = 'japan'
print(p.country) #例項屬性會遮蔽掉同名的類屬性
print(people.country)
del p.country #刪除例項屬性
print(p.country)
執行結果:
下面來看一下類方法、例項方法和靜態方法的區別。
類方法:是類物件所擁有的方法,需要用修飾器"@classmethod"來標識其為類方法,對於類方法,第一個引數必須是類物件,一般以"cls"作為第一個引數(當然可以用其他名稱的變數作為其第一個引數,但是大部分人都習慣以'cls'作為第一個引數的名字,就最好用'cls'了),能夠通過例項物件和類物件去訪問。
class people:
country = 'china'
#類方法,用classmethod來進行修飾
@classmethod
def getCountry(cls):
return cls.country
p = people()
print(p.getCountry()) #可以用過例項物件引用
print(people.getCountry()) #可以通過類物件引用
執行結果:
類方法還有一個用途就是可以對類屬性進行修改:
class people:
country = 'china'
#類方法,用classmethod來進行修飾
@classmethod
def getCountry(cls):
return cls.country
@classmethod
def setCountry(cls,country):
cls.country = country
p = people()
print(p.getCountry()) #可以用過例項物件引用
print(people.getCountry()) #可以通過類物件引用
p.setCountry('japan')
print(p.getCountry())
print(people.getCountry())
執行結果:
結果顯示在用類方法對類屬性修改之後,通過類物件和例項物件訪問都發生了改變。
例項方法:在類中最常定義的成員方法,它至少有一個引數並且必須以例項物件作為其第一個引數,一般以名為'self'的變數作為第一個引數(當然可以以其他名稱的變數作為第一個引數)。在類外例項方法只能通過例項物件去呼叫,不能通過其他方式去呼叫。
class people:
country = 'china'
#例項方法
def getCountry(self):
return self.country
p = people()
print(p.getCountry()) #正確,可以用過例項物件引用
print(people.getCountry()) #錯誤,不能通過類物件引用例項方法
靜態方法:需要通過修飾器"@staticmethod"來進行修飾,靜態方法不需要多定義引數。
class people:
country = 'china'
@staticmethod
#靜態方法
def getCountry():
return people.country
print(people.getCountry())
執行結果:
對於類屬性和例項屬性,如果在類方法中引用某個屬性,該屬性必定是類屬性,而如果在例項方法中引用某個屬性(不作更改),並且存在同名的類屬性,此時若例項物件有該名稱的例項屬性,則例項屬性會遮蔽類屬性,即引用的是例項屬性,若例項物件沒有該名稱的例項屬性,則引用的是類屬性;如果在例項方法更改某個屬性,並且存在同名的類屬性,此時若例項物件有該名稱的例項屬性,則修改的是例項屬性,若例項物件沒有該名稱的例項屬性,則會建立一個同名稱的例項屬性。想要修改類屬性,如果在類外,可以通過類物件修改,如果在類裡面,只有在類方法中進行修改。
從類方法和例項方法以及靜態方法的定義形式就可以看出來,類方法的第一個引數是類物件cls,那麼通過cls引用的必定是類物件的屬性和方法;而例項方法的第一個引數是例項物件self,那麼通過self引用的可能是類屬性、也有可能是例項屬性(這個需要具體分析),不過在存在相同名稱的類屬性和例項屬性的情況下,例項屬性優先順序更高。靜態方法中不需要額外定義引數,因此在靜態方法中引用類屬性的話,必須通過類物件來引用。