面向過程就是所有的事情已過程為主,關注的重點是過程,即第一步、第二步、第三步是什麼。C語言就是典型的面向過程的語言。C語言需要只要先找到main函數,在main函數中一步一步的往下執行,比如如下代碼:
Print('step1')
print('step2')
print('step3')
面向對象的關注點則是對象。比如一個人是一個對象,這個人身高、體重就是對象的屬性,而跑、跳則是這個對象可以執行的相關操作。比如如下代碼:
class 類名:
def 方法1();
def 方法2():
簡而言之,編寫函數就是“面向過程”,編寫類就是“面向對象”。
對象:具有行為和屬性。在進行描述的時候,屬性多為名字。行為多為動詞。
類:指一個類別。具有相同屬性和行為的“對象”構成的一個整體。
類和對象之間的關系:
類是對象的抽象表現,對象是類的具體表現
類是設計的藍圖,對象是根據藍圖設計出來的具體產物。
在Python中,通過class關鍵字來定義一個類。這個類中可以包含零個或多個屬性和方法。
class 類名:
def 方法1();
def 方法2():
當可以實現一個類之後,就可以通過這個類來實例化一個對象。這個對象包含了這個類的全部屬性和方法。
class person: #類
def a_name(self): #方法
print('xiaoming')
xiaoming = person() #根據person類實例一個對象
xiaohong = person() #根據person類實例一個對象
zhangsan = person() #根據person類實例一個對象
xiaoming.a_name() #調用對象的方法
xiaohong.a_name() #調用對象的方法
zhangsan.a_name() #調用對象的方法
結果:
xiaoming
xiaoming
xiaoming
很多時候我們需要根據需求給對象添加特殊的屬性或方法,而這些屬性和方法又不能跟其他對象共用,此時就需要動態來添加屬性和方法了。
class person: #類
def a_name(self): #方法
print('xiaoming')
def age(self): #動態增加的方法
print("18")
xiaoming = person() #根據person類實例一個對象
xiaohong = person() #根據person類實例一個對象
zhangsan = person() #根據person類實例一個對象
xiaoming.gender = 'boy' #動態增加屬性gender
xiaoming.age = age #動態增加方法age
xiaoming.a_name() #調用對象的方法
xiaohong.a_name() #調用對象的方法
zhangsan.a_name() #調用對象的方法
print(xiaoming.gender)
xiaoming.age(xiaoming)
print(zhangsan.gender)
zhangsan.age(zhangsan)
結果:
xiaoming
xiaoming
xiaoming
boy
18
Traceback (most recent call last):
File "d:/python/test_project/test.py", line 37, in <module>
print(zhangsan.gender)
AttributeError: 'person' object has no attribute 'gender'
Traceback (most recent call last):
File "d:/python/test_project/test.py", line 38, in <module>
zhangsan.age()
AttributeError: 'person' object has no attribute 'age'
在python的init函數中,init前後各有2個下劃線。而Init在每次被調用創建對象時,都會“自動調用”一次。參數self是必不可少的。類中定義的方法,第一個參數是固定的,命名為"self"。
class person: #類
def __init__(self):
print("init")
def a_name(self): #方法
print('xiaoming')
xiaoming = person() #根據person類實例一個對象
xiaohong = person() #根據person類實例一個對象
zhangsan = person() #根據person類實例一個對象
結果:
init
init
init
很多時候,每個對象的屬性值都是不相同的,就比如我們上邊的代碼,創建一個“名字”的方法,但是這個方法打印的內容都是相同的。這樣就沒有版本區分不同的對象了。所以,我們可以通過傳入參數的方式賦值不同的屬性值。
class person: #類
def __init__(self,name,age):
self.name = name #屬性
self.age = age #屬性
def action(self): #方法
print('%s is running,age %d' %(self.name,self.age))
xiaoming = person('xiaoming',18) #根據person類實例一個對象
zhangsan = person('zhangsan',20) #根據person類實例一個對象
xiaoming.action() #對象執行方法
zhangsan.action() #對象執行方法
結果:
xiaoming is running,age 18
zhangsan is running,age 20
類屬性與當前類有綁定關系,與當前類創建的對象無關系。
對於類屬性,可以通過類名進行訪問,也可以通過對象進行訪問。但是,通過對象進行訪問,是“只讀”,不能進行修改。
對於實例屬性,只能通過創建的對象進行訪問,不能通過類名進行訪問。
class person: #類
city = 'beijing' #類屬性
def __init__(self,name,age):
self.name = name
self.age = age
def action(self): #方法
print('%s is running,age %d' %(self.name,self.age))
xiaoming = person('xiaoming',18) #根據person類實例一個對象
zhangsan = person('zhangsan',20) #根據person類實例一個對象
print(person.city) #打印類屬性
xiaoming.city = 'shanghai' #通過對象修改類屬性
print(xiaoming.city) #打印對象類屬性
print(person.city) #打印類屬性
print(zhangsan.city) #打印對象類屬性
print('>>>>>>>>>>>>>>')
person.city = 'nanjing'
print(xiaoming.city) #打印對象類屬性
print(person.city) #打印類屬性
print(zhangsan.city) #打印對象類屬性
結果:
beijing
shanghai
beijing
beijing
>>>>>>>>>>>>>>
shanghai
nanjing
nanjing
從上述代碼中,通過對象修改了類屬性,看似修改成功了,其實並不是修改了類屬性,只是動態地給對象創建了一個屬性“city”,只不過這個屬性名跟類屬性的"city"屬性名名字相同罷了。
類方法需要使用@classmethod修飾
類方法的第一個參數是固定的,命名為cls
有兩種方式來訪問類方法,一種是通過類名來訪問,另一種是通過對象名來訪問(不建議)。
最好是通過類方法來訪問類屬性,通過實例方法來訪問實例屬性。
class person: #類
city = 'beijing' #類屬性
def __init__(self,name,age):
self.name = name
self.age = age
def action(self): #方法
print('%s is running,age %d' %(self.name,self.age))
@classmethod
def move_city(cls,city): #類方法
cls.city = city
xiaoming = person('xiaoming',18) #根據person類實例一個對象
zhangsan = person('zhangsan',20) #根據person類實例一個對象
print(person.city)
person.move_city('shenzhen') #通過類名訪問
print(person.city)
xiaoming.move_city("guangzhou") #通過對象名訪問
print(person.city)
結果:
beijing
shenzhen
guangzhou
如果希望類中的屬性為私有屬性,可以通過在屬性名稱前加兩個下劃線__來實現。此時只能通過內部進行訪問,外部不能訪問。
class person: #類
city = 'beijing' #類屬性
def __init__(self,name,age):
self.__name = name #私有屬性
self.__age = age #私有屬性
def action(self): #方法
print('%s is running,age %d' %(self.name,self.age))
def get_name(self): #訪問私有屬性
print(self.__name)
def get_age(self): #訪問私有屬性
print(self.__age)
xiaoming = person('xiaoming',18) #根據person類實例一個對象
zhangsan = person('zhangsan',20) #根據person類實例一個對象
xiaoming.get_name()
xiaoming.get_age()
結果:
xiaoming
18
如果直接調用實例進行訪問的話,則會報錯
print(xiaoming.__name)
結果:
Traceback (most recent call last):
File "d:/python/test_project/test.py", line 38, in <module>
print(xiaoming.__name)
AttributeError: 'person' object has no attribute '__name'
那為什麼要設置私有變量呢?因為這樣可以在方法中對傳入的參數進行判斷,避免無效的參數輸入。
class person: #類
def __init__(self,name,age):
self.__name = name #私有屬性
self.__age = age #私有屬性
def set_age(self,s_age):
if not isinstance(s_age,(int)):
raise TypeError('age false type')
if(s_age > 0 and s_age < 100):
self.__age = s_age
print('age %d' %self.__age)
else:
print('age not correct:%d' %s_age)
xiaoming.set_age(18)
xiaoming.set_age(200)
xiaoming.set_age('18')
結果:
age 18
age not correct:200
Traceback (most recent call last):
File "d:/python/test_project/test.py", line 52, in <module>
xiaoming.set_age('18')
File "d:/python/test_project/test.py", line 29, in set_age
raise TypeError('age false type')
TypeError: age false type
當有兩個屬性高度類似的類時,就可以使用繼承。如果類A繼承了類B,那麼A就是子類,B就是父類。子類一旦繼承了父類,那麼子類就會具備父類的一切特征。因此,父類能做的事情,子類也都可以做。
class fater: #父類
def __init__(self):
self.name = 'fater'
self.age = 58
class child(fater): #子類
def __init__(self):
super().__init__() #調用父類的init方法
self.gender = 'boy'
laowang = fater(); #實例父類
print(laowang.name) #打印父類屬性
xiaowang = child(); #實例子類
print(xiaowang.name) #打印子類繼承父類的屬性
print(xiaowang.gender) #打印子類特有的屬性
print(laowang.gender) #打印父類中子類的屬性(報錯)
結果:
fater
fater
boy
Traceback (most recent call last):
File "d:/python/test_project/test.py", line 64, in <module>
print(laowang.gender)
AttributeError: 'fater' object has no attribute 'gender'
有時一個對象需要繼承多個父類的屬性,這就是多重繼承。
class fater: #父類
def __init__(self,name,age):#屬性
self.name = name
self.age = age
def get_name(self):#方法
print('fater name:%s' %self.name)
class mother: #父類
def __init__(self,name,age) -> None: #屬性
self.name = name
self.age = age
def get_name(self):#方法
print('mother name:%s' %self.name)
class child(fater,mother):#子類,按照先後順序進行繼承
def __init__(self, name, age):#屬性
super().__init__(name, age) #調用父類的init方法
self.gender = 'boy'
ft = fater('laowang',58); #實例
mt = mother('laohong',55) #實例
kid = child('xiaoming',10) #實例
print(ft.name)
print(mt.name)
print(kid.name) #父類屬性
print(kid.age) #父類屬性
print(kid.gender) #子類屬性
kid.get_name() #父類方法
結果:
laowang
laohong
xiaoming
10
boy
fater name:xiaoming
可以看到,雖然同時繼承了father和mother,但是調用父類的"name"方法時,調用的還是fater的方法。因為在繼承時,father在前。
那把mother改到前面後試試。
class child(mother,fater):#子類,按照先後順序進行繼承
結果:
mother name:xiaoming
class fater: #父類
def __init__(self,name,age):#屬性
self.name = name
self.age = age
def get_name(self):#方法
print('fater name:%s' %self.name)
class child(fater):#子類
def __init__(self, name, age):#屬性
super().__init__(name, age) #調用父類的init方法
self.gender = 'boy'
def get_name(self):
print('child name:%s' %self.name)
ft = fater('laowang',58); #實例
kid = child('xiaoming',10) #實例
kid.get_name()
結果:
child name:xiaoming
可以看到,child是從father處繼承了屬性和方法,child和father有同名的方法get_name,但是當調用child的get_name方法時,會自動調用自身的方法,而不會調用父類的方法。這就是多態。
對於一個變量,只需要知道父類型,無需確切地知道它的子類型,就可以放心地調用方法,而具體調用的方法是作用在father還是child,由運行時該對象的確切類型決定,這就是多態的威力。調用方只管調用,不管細節,而當需要新增一種子類型是,只要確保方法編寫正確,不用管原來的代碼是如何調用的。這就是著名的“開閉”原則。