總結(jié)自廖雪峰老師的Python教程!實(shí)力點(diǎn)贊!教程地址:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319117128404c7dd0cf0e3c4d88acc8fe4d2c163625000
1、類和對象
面向?qū)ο缶幊讨凶钪匾母拍罹褪?strong>類(class)和對象(instance)。我們首先來定義一個(gè)類,如一個(gè)動(dòng)物類:
class Animal():
def __init__(self,name,weight):
self.name = name
self.weight = weight
我們用class關(guān)鍵字定義了一個(gè)類,同時(shí)通過__init__方法定義了該類的對象所必需綁定的一些屬性。注意到__init__方法的第一個(gè)參數(shù)永遠(yuǎn)是self,表示創(chuàng)建的實(shí)例本身,因此,在__init__方法內(nèi)部,就可以把各種屬性綁定到self,因?yàn)閟elf就指向創(chuàng)建的實(shí)例本身。接下來,我們可以創(chuàng)建一個(gè)Animal的實(shí)例,必需傳入name和weight兩個(gè)屬性值:
dog = Animal('hasky',90)
這樣,我們就可以通過.來獲取實(shí)例的屬性值,同時(shí)可以直接通過賦值的方式修改屬性值。
print(dog.weight) #90
dog.weight = 100
print(dog.weight) #100
在類中還可以定義方法,比如我們給增加一個(gè)打印功能:
class Animal():
def __init__(self,name,weight):
self.name = name
self.weight = weight
def print_weight(self):
print("%s : %s" % (self.name,self.weight))
dog = Animal('hasky',90)
dog.print_weight() # hasky : 90
2、訪問限制
像剛才我們這種定義屬性的方式,屬性可以被隨意的訪問和修改,我們?nèi)绾伪苊膺@種外部的隨意訪問呢?在java中我們可以通過private來設(shè)置變量為私有變量,這樣我們只能通過get和set方法來進(jìn)行訪問和修改,其實(shí)在python中也是可以這樣做的,只需要在變量名前面加上__即可。
class Animal():
def __init__(self,name,weight):
self.__name = name
self.__weight = weight
def print_weight(self):
print("%s : %s" % (self.name,self.weight))
def get_name(self):
return self.__name
def get_weight(self):
return self.__weight
def set_name(self,name):
self.__name = name
def set_weight(self,weight):
self.__weight = weight
dog = Animal('hasky',90)
print(dog.get_name()) # hasky
print(dog.__name) # AttributeError: 'Animal' object has no attribute '__name'
dog.set_weight(100)
print(dog.get_weight()) # 100
print(dog.__weight) # AttributeError: 'Animal' object has no attribute '__weight'
可以看到,在給變量名前面加上__之后,不能通過.來訪問屬性或者修改屬性了,否則會(huì)報(bào)AttributeError。
有以下幾點(diǎn)需要注意的:
1、需要注意的是,在Python中,變量名類似__xxx__的,也就是以雙下劃線開頭,并且以雙下劃線結(jié)尾的,是特殊變量,特殊變量是可以直接訪問的,不是private變量,所以,不能用__name__、__score__這樣的變量名。
2、有時(shí)候變量前有一個(gè)下劃線,比如_name,這樣的實(shí)例變量外部是可以訪問的,但是,按照約定俗成的規(guī)定,我們一般將其也視為私有變量,不要隨意訪問。
3、雙下劃線開頭的實(shí)例變量是不是一定不能從外部訪問呢?其實(shí)也不是??梢钥聪聦?shí)例的__dict__:

可以看到,我們原先的屬性名稱已經(jīng)被改為_Animal__name。
3、繼承和多態(tài)
在面向?qū)ο缶幊讨?,我們都知道一個(gè)類可以被其他類繼承,這些類被稱為子類(Subclass),子類可以繼承父類的所有屬性和方法。同時(shí)可以給類中增加新的方法或者覆蓋父類的方法:
class Animal():
def __init__(self,name,weight):
self.name = name
self.weight = weight
def print_weight(self):
print("%s : %s" % (self.name,self.weight))
def run(self):
print('Animal is running')
class Dog(Animal):
def eat(self):
print('Dog is eating')
class Cat(Animal):
def run(self):
print('Cat is running')
dog = Dog('hasky',10)
dog.run() # Animal is running
dog.eat() # Dog is eating
cat = Cat('tt',20)
cat.run() # Cat is running
可以看到,在Animal類的基礎(chǔ)上,我們定義了Dog和Cat兩個(gè)類,Dog中繼承了Animal類的name和weight屬性,以及run和print_weight方法,同時(shí)增加了eat方法。而Cat類則是覆蓋了Animal中的run方法。同時(shí),在創(chuàng)建Dog和Cat的實(shí)例時(shí),也調(diào)用了Animal的__init__方法,所以必須傳入這兩個(gè)值。
如果我們想在Dog中的增加新的屬性值怎么辦,考慮下面的寫法:
class Dog(Animal):
def __init__(self,height):
self.height = height
def eat(self):
print('Dog is eating')
dog = Dog(20)
dog.name # 'Dog' object has no attribute 'name'
這樣我們父類Animal中的兩個(gè)屬性值就沒有了,如何在保留著兩個(gè)屬性的基礎(chǔ)上增加新的屬性呢?需要通過super().__init__()來調(diào)用父類的__init__()方法。
class Dog(Animal):
def __init__(self,name,weight,height):
super().__init__(name,weight)
self.height = height
def eat(self):
print('Dog is eating')
dog = Dog('hasky',10,20)
dog.name #hasky
有了子類和父類,隨之就出現(xiàn)了多態(tài)的概念。簡單來說,在繼承關(guān)系中,如果一個(gè)實(shí)例的數(shù)據(jù)類型是某個(gè)子類,那它的數(shù)據(jù)類型也可以被看做是父類。但是,反過來就不行,這就是多態(tài)。python中,我們可以用isinstance來判斷一個(gè)實(shí)例是否屬于某一類。我們來看下面的例子:
class Animal():
def __init__(self,name,weight):
self.name = name
self.weight = weight
def print_weight(self):
print("%s : %s" % (self.name,self.weight))
def run(self):
print('Animal is running')
class Dog(Animal):
def eat(self):
print('Dog is eating')
a = Dog("hasky",90)
b = Animal('tt',10)
print(isinstance(a,Dog))# True
print(isinstance(a,Animal))# True
print(isinstance(b,Animal))# True
print(isinstance(b,Dog))# False
這里我們提一下python這種動(dòng)態(tài)語言和java等動(dòng)態(tài)語言的區(qū)別。比如我們設(shè)計(jì)一個(gè)run_twice()函數(shù):
def run_twice(obj):
obj.run()
obj.run()
a = Dog("hasky",90)
b = Animal('tt',10)
run_twice(a)
run_twice(b)
在java中,如果需要傳入Animal類型,那么傳入的實(shí)例必須是Animal類型或者它的子類,但是在python中,我們只需要保證傳入的實(shí)例有run方法就行,比如:
class Timer(object):
def run(self):
print('start....')
timer = Timer()
run_twice(timer)
# start....
# start....
4、獲取對象信息
如果要獲得一個(gè)對象的所有屬性和方法,可以使用dir()函數(shù),它返回一個(gè)包含字符串的list,比如,獲得一個(gè)str對象的所有屬性和方法:

可以看到,有許多前后都帶著下劃線的方法,在python中,類似__xxx__的屬性和方法在Python中都是有特殊用途的,比如__len__方法返回長度。在Python中,如果你調(diào)用len()函數(shù)試圖獲取一個(gè)對象的長度,實(shí)際上,在len()函數(shù)內(nèi)部,它自動(dòng)去調(diào)用該對象的__len__()方法,所以,下面的代碼是等價(jià)的:
print('ABC'.__len__())
print(len('ABC'))
配合getattr()、setattr()以及hasattr(),我們可以直接操作一個(gè)對象的狀態(tài):
class Animal():
def __init__(self,name,weight):
self.name = name
self.weight = weight
def print_weight(self):
print("%s : %s" % (self.name,self.weight))
def run(self):
print('Animal is running')
dog = Animal('hasky',20)
print(hasattr(dog,'name')) # True
setattr(dog,'height',20)
print(getattr(dog,'height')) # 20
如果獲取的屬性可能不存在,我們可以添加一個(gè)默認(rèn)值:
print(getattr(dog,'type','dog')) #dog
當(dāng)然也可以獲取方法:
f = getattr(dog,'run')
print(f) # <bound method Animal.run of <__main__.Animal object at 0x104f33cc0>>
f() # Animal is running
5、實(shí)例屬性和類屬性
由于Python是動(dòng)態(tài)語言,根據(jù)類創(chuàng)建的實(shí)例可以任意綁定屬性。給實(shí)例綁定屬性的方法是通過實(shí)例變量,或者通過self變量:
class Animal():
def __init__(self,name,weight):
self.name = name
self.weight = weight
dog = Animal('hasky',90)
dog.height = 20
print(dog.height) # 20
但是,如果Animal類本身需要綁定一個(gè)屬性呢?可以直接在class中定義屬性,這種屬性是類屬性,歸Animal類所有:
class Animal():
tt = 'ss'
def __init__(self,name,weight):
self.name = name
self.weight = weight
print(Animal.tt) # ss
dog = Animal('hasky',90)
print(dog.tt) # ss
dog.tt = 'aa'
print(dog.tt) # aa
print(Animal.tt)# ss
可以看到,我們創(chuàng)建了Animal類的一個(gè)類屬性tt,所有實(shí)例都可以訪問該屬性,而在編寫程序的時(shí)候,千萬不要對實(shí)例屬性和類屬性使用相同的名字,因?yàn)橄嗤Q的實(shí)例屬性將屏蔽掉類屬性,但是當(dāng)你刪除實(shí)例屬性后,再使用相同的名稱,訪問到的將是類屬性。