【Python之旅】第四篇(三):Python面向對象編程詳解

    終因而來到了Python的面向對象編程,之前是沒有接觸過其它的面向對象編程的語言,所以學習這一部分是至關帶勁的,這裏也總結一下。
python


1.面向對象編程的相關名詞及解釋編程

    世界萬物,皆可分類,一切皆爲對象。ssh

    所謂的面向對象編程,指的是一種編程的思想,經過對具體代碼實現過程(面向過程編程)的不斷抽象,以造成一個個的類別,以提升咱們進行大型程序編寫的效率(面向對象的具體實現須要面向過程,大型程序也能夠用面向過程來編寫,只是比較麻煩)。對於面向對象編程的相關名詞和解釋以下:ide

對象 :類的實體\一個叫Rain的好色的男人函數

類:人\動物\機器學習

方法:人會走,會思考\狗會叫,會咬人\定義一個類的各個功能測試

消息傳遞:狗叫了,人聽見了,就叫通訊ui

繼承:狗都四條腿走路this

封裝:人不能引用狗的特性,好比四條腿走路spa

多態性:一個叫的功能,多是低吼,也但是大聲叫

抽象性:簡化複雜的現實問題的途徑,它能夠爲具體問題找到最恰當的類定義

    對「類」這個名詞舉個簡單的例子:

杯子:類

這個杯子:對象(實體),屬於「杯子」這個類

    對「方法」這個名詞舉個例子:動態方法(動態屬性)與靜態方法(靜態方法能夠理解爲類的屬性)。


2.類、對象、方法

    關於三者的關係,用下面的一張圖片能夠很是清晰明確的說明:

wKiom1YNXzXhVqtqAAG2f59r2RI144.jpg


3.類的語法:第一個面向對象程序

程序代碼以下:
class dog:                    #定義一個類
	def name(self):       #定義類的方法
		print "Hello master, my name is Python."

D = dog()    #類的實例化,產生類的對象,若是不實例化,將沒法訪問該類
D.name()     #使用類的方法

執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python myclass4.py 
Hello master, my name is Python.

 

4.類的方法:有關self

    類的方法(類函數)與普通的函數只有一個特別的區別——它們必須有一個額外的第一個參數名稱,可是在調用這個方法的時候你不爲這個參數賦值,Python會提供這個值。這個特別的變量指對象自己,按照慣例它的名稱是self。

    如你有一個類稱爲MyClass和這個類的一個實例MyObject。當你調用這個對象的方法MyObject.method(arg1, arg2)的時候,這會由Python自動轉爲MyClass.method(MyObject, arg1, arg2)。

    類中若是有多個不一樣的方法,而且類中的變量須要相互訪問,這時候就須要使用self即用類自身來做爲中間變量以進行變量在不一樣類中的傳遞了,能夠看下面的一個例子:

程序代碼以下:
class Dog:
	class_object = 'food' #這是一個類變量
	def sayHi(self):
		print 'Hi master , I am your little dog, who do you want me to bite...'
		favorate_food = 'bone'
		self.FavorFood = favorate_food    #將favorate_food變爲全局變量,讓其它方法能夠引用
	def eat(self, food_type):
		if food_type == self.FavorFood:  #不一樣類方法之間的變量調用,須要經過類自己
			print 'I like it very much..thanks'
		else:
			print 'Do not give me this bull shit...'

d = Dog()
d.sayHi()   '''若是不調用sayHi(),則沒法將favorrate_food變成一個類變量,所謂類變量,
			便是類下定義的第一級變量'''
d.eat('bone') 

執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python class7.py 
Hi master , I am your little dog, who do you want me to bite...
I like it very much..thanks


    固然這裏其實是把sayHi方法中的favorate_food變量變爲變中的全局變量,須要注意的是,下面的方法是不能夠的:

程序代碼以下:
class Dog:
	class_object = 'food' 
	def sayHi(self):
		print 'Hi master , I am your little dog, who do you want me to bite...'
		favorate_food = 'bone'

	def eat(self, food_type):
		if food_type == self.sayHi().favorate_food: #這樣使用是不行的
			print 'I like it very much..thanks'
		else:
			print 'Do not give me this bull shit...'

d = Dog()
d.sayHi()   
d.eat('bone') 

執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python class7.py 
Hi master , I am your little dog, who do you want me to bite...
Hi master , I am your little dog, who do you want me to bite...
Traceback (most recent call last):
  File "class7.py", line 16, in <module>
    d.eat('bone') 
  File "class7.py", line 9, in eat
    if food_type == self.sayHi().favorate_food: 
AttributeError: 'NoneType' object has no attribute 'favorate_food'

    上面程序不行是由於,self.sayHi()時,其實會執行sayHi()方法,由於它原本就是一個函數,而且沒有定義返回值,所以會返回None值,致使程序執行出錯。

    上面的例子意在說明,不一樣類方法之間調用變量時,須要將其變爲全局變量,而且要經過self即類自己進行引用。即類下的各個方法(函數,name也認爲是一個函數),不能獨自相互通訊,必需要經過類做爲中間人來進行通訊,self即表明self自己,經過self.name便可完成通訊,因此在定義類下的方法時,必需要加一個參數,即self,來表明類自己,不然這個方法就不能被使用固然參數不必定要是self。


5.類的構造函數__init__()與解構函數__del__()

    關於兩個函數的說明,能夠看下面一個例子:

程序代碼以下:
class Person:
	def __init__(self,name,age):                #構造函數,只要把類實例化,就會執行此函數
		print 'I am being called right now...'
		self.Name = name
		self.Age = age

	def sayHi(self):                            #類的普通方法
		print 'Hi my name is %s, i am %s years old' % (self.Name,self.Age)

	def __del__(self):                          #解構函數,當實例在內存中釋放時,纔會執行此函數
		print 'I got killed just now...bye...', self.Name

p = Person('Alex',29)
p.sayHi()
#del p
print '*'*60

p2 = Person('Jack',40)
p2.sayHi()
#del p2

執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python person_class5.py 
I am being called right now...
Hi my name is Alex, i am 29 years old
************************************************************
I am being called right now...
Hi my name is Jack, i am 40 years old
I got killed just now...bye... Jack
I got killed just now...bye... Alex

    能夠看到程序最後有兩行相同內容的輸出(除了名字不同),是由於程序結束時,會把該程序在內存中佔用的空間釋放,因而執行兩次解構函數(由於進行了兩次類的實例化),纔出現這樣的狀況爲了驗證這個結論,將程序代碼修改成以下並執行:

程序代碼以下:
class Person:
	def __init__(self,name,age):
		print 'I am being called right now...'
		self.Name = name
		self.Age = age

	def sayHi(self):
		print 'Hi my name is %s, i am %s years old' % (self.Name,self.Age)

	def __del__(self):
		print 'I got killed just now...bye...', self.Name

p = Person('Alex',29)
p.sayHi()
del p    #其它不變,只是在作類的另外一個實例前,刪除該實例
print '*'*60

p2 = Person('Jack',40)
p2.sayHi()
#del p2

執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python person_class5.py 
I am being called right now...
Hi my name is Alex, i am 29 years old
I got killed just now...bye... Alex
************************************************************
I am being called right now...
Hi my name is Jack, i am 40 years old
I got killed just now...bye... Jack

    上面的程序代碼及執行結果即可以說明,解構函數在類完成調用而且在內存中釋放時纔會執行。


6.類的公有屬性與私有屬性及其調用

    類的公有屬性便是類下的普通方法,這些方法是能夠直接被調用的;而類的私有屬性只有在類中不一樣方法之間能夠相互調用,通常狀況下是不能在類外直接調用的。

    能夠看下面一個例子:

程序代碼以下:
class Person:
	def __init__(self,name,age):
		print 'I am being called right now...'
		self.Name = name
		self.Age = age

	def sayHi(self):
		print 'Hi my name is %s, i am %s years old' % (self.Name,self.Age)
		self.__talk()    #能夠引用類中的私有屬性

	def __talk(self):    #私有屬性的定義方法
		print "I'm private..."

p=Person('Alex',29)
p.sayHi()

執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python private6.py 
I am being called right now...
Hi my name is Alex, i am 29 years old
I'm private...

    若是將代碼修改成以下,即在類外調用私有屬性:

程序代碼以下:
class Person:
	def __init__(self,name,age):
		print 'I am being called right now...'
		self.Name = name
		self.Age = age

	def sayHi(self):
		print 'Hi my name is %s, i am %s years old' % (self.Name,self.Age)
		self.__talk()

	def __talk(self):
		print "I'm private..."

p=Person('Alex',29)
p.sayHi()
p.__talk()    #在類外經過普通方式調用類的私有屬性

執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python private6.py 
I am being called right now...
Hi my name is Alex, i am 29 years old
I'm private...
Traceback (most recent call last):
  File "private6.py", line 17, in <module>
    p.__talk()
AttributeError: Person instance has no attribute '__talk' #會出現找不到類方法的錯誤提示

    固然能夠經過特殊的方法引用類中的私有屬性:

程序代碼:
class Person:
	def __init__(self,name,age):
		print 'I am being called right now...'
		self.Name = name
		self.Age = age

	def sayHi(self):
		print 'Hi my name is %s, i am %s years old' % (self.Name,self.Age)
		self.__talk()

	def __talk(self):
		print "I'm private..."

p=Person('Alex',29)
p.sayHi()

print '*'*30

p._Person__talk()

執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python private6.py 
I am being called right now...
Hi my name is Alex, i am 29 years old
I'm private...
******************************
I'm private...


7.類的繼承

    類有分父類和子類,在有多個子類的狀況下,子類經過繼承父類的屬性或方法,就能夠節省不少代碼空間。能夠先看下面一個沒有參數傳遞的類的繼承簡單例子:

程序代碼以下:
class person:                #父類

	def tell(self, name):
		print 'hi my name is', name

class student(person):       #子類,繼承父類

	def study(sefl):
		print 'I am studying Py right now.'

s = student()

s.study()
s.tell('MengFanHao') #子類繼承父類後即可以直接調用父類的方法
                     #在有多個須要同時執行相同功能的子類狀況下,使用類的繼承能夠節省代碼空間
執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python cla_with_no_arg9.py 
I am studying Py right now.
hi my name is MengFanHao

    看過上面的例子後,再看下面一個含有參數傳遞的例子,經過該例子也能夠很好地理解前面第2點中關於類、對象、方法的理解:

class SchoolMember:        #父類,學校成員
	school_name = 'Oldboy Linux edu.'    #第一級變量,即類屬性
	def __init__(self,name,gender,nationality = 'CN'):    #構造函數
		self.name = name
		self.gender = gender
		self.nation = nationality

	def tell(self):    #普通的類方法
		print 'Hi, my name is %s , I am from %s' % (self.name,self.nation)

class Student(SchoolMember):    #子類,學生,繼承父類學校成員的相關屬性
	def __init__(self, Name, Gender, Class, Score, Nation = 'US'): #子類下的方法
		SchoolMember.__init__(self, Name, Gender, Nation) #讓父類使用子類傳遞過去的參數
		self.Class = Class
		self.Score = Score

	def payTuition(self, amount):    #子類下的方法
		if amount < 6499:
			print 'Get the fuck off...'
		else:
			print 'Welcome onboard!'

class Teacher(SchoolMember):    #子類,老師,繼承父類學校成員的相關屬性
	def __init__(self, Name, Gender, Course, Salary, Nation = 'FR'):
		SchoolMember.__init__(self, Name, Gender, Nation)
		self.course = Course
		self.Salary = Salary

	def teaching(self):
		print 'I am teaching %s, i am making %s per month !' % (self.course, self.Salary)

S1 = Student('WangFanHao', 'Male', 'Python', 'C+', 'JP') #實例化一個子類對象,學生
S1.tell()    #直接繼承父類中的tell()方法
S1.payTuition(4999)    #使用子類Student()自身中的類方法
print S1.school_name   #直接繼承父類的一個屬性   

print '*'*60

S2 = Student('ShitTshirt', 'Male', 'Linux', 'B')
S2.tell()
S2.payTuition(6500)
#S2.age = 29
#print S2.age

print '*'*60

T1 = Teacher('Alex', 'Male', 'C++', 5000)   #實例化一個子類對象,學生
T1.tell()            #直接繼承父類中的tell()方法
T1.teaching()        #直接繼承父類的一個屬性

print 'S1.name:', S1.name    #測試用,觀察輸出結果
print 'S2.name:', S2.name
print 'T1.name:', T1.name

執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python class_continue8.py
Hi, my name is WangFanHao , I am from JP
Get the fuck off...
Oldboy Linux edu.
************************************************************
Hi, my name is ShitTshirt , I am from US
Welcome onboard!
************************************************************
Hi, my name is Alex , I am from FR
I am teaching C++, i am making 5000 per month !
S1.name: WangFanHao
S2.name: ShitTshirt
T1.name: Alex	

    經過上面的例子即可以很好地理解類的繼承的做用了,即若是須要在多個子類中執行相同的代碼功能時,就能夠經過類的繼承來節省代碼空間。


8.類的屬性修改:setattr(),delattr()與getattr()

    以上面程序爲例,作以下的演示:

首先在交互器中導入該類:

>>> import class_continue8
Hi, my name is WangFanHao , I am from JP
Get the fuck off...
Oldboy Linux edu.
************************************************************
Hi, my name is ShitTshirt , I am from US
Welcome onboard!
************************************************************
Hi, my name is Alex , I am from FR
I am teaching C++, i am making 5000 per month !
S1.name: WangFanHao
S2.name: ShitTshirt
T1.name: Alex
>>> import tab
>>> class_continue8.
class_continue8.S1                 class_continue8.__hash__(
class_continue8.S2                 class_continue8.__init__(
class_continue8.SchoolMember       class_continue8.__name__
class_continue8.Student            class_continue8.__new__(
class_continue8.T1                 class_continue8.__package__
class_continue8.Teacher            class_continue8.__reduce__(
class_continue8.__class__(         class_continue8.__reduce_ex__(
class_continue8.__delattr__(       class_continue8.__repr__(
class_continue8.__dict__           class_continue8.__setattr__(
class_continue8.__doc__            class_continue8.__sizeof__(
class_continue8.__file__           class_continue8.__str__(
class_continue8.__format__(        class_continue8.__subclasshook__(
class_continue8.__getattribute__(


setattrr():在類中添加屬性

1.子類有的屬性,屬於該子類的對象沒有定義,會繼承該屬性;若是有定義,則不會繼承子類的該屬性
>>> class_continue8.Student.age    #原來Student類中並無age屬性
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class Student has no attribute 'age'
>>> setattr(class_continue8.S)
class_continue8.S1            class_continue8.SchoolMember
class_continue8.S2            class_continue8.Student
>>> setattr(class_continue8.Student,'age',29)    #在Student類中添加age屬性
>>> class_continue8.Student.age    #再次查看age屬性
29
>>> class_continue8.S1.age    #S1與S2做爲Student類的實例化對象,也會有age屬性
29
>>> class_continue8.S2.age
29
>>> setattr(class_continue8.S1,'age',26)
>>> class_continue8.S1.age        #此時S1對象有定義,所以不會繼承所屬子類的該屬性
26

2.子類的對象有的屬性,但在其所屬子類沒有定義,則該子類不會反繼承該屬性
>>> setattr(class_continue8.S1,'tuition',5000)    #若是僅僅是在對象中添加tuition屬性
>>> class_continue8.Student.age
29
>>> class_continue8.Student.tuition    #則在S1對象所屬的類Student中並不會有tuition屬性
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class Student has no attribute 'tuition'
>>> class_continue8.S1.tuition
5000
>>> class_continue8.S2.tuition        #所以S2對象也不會有該屬性
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Student instance has no attribute 'tuition'

3.父類有的屬性,子類中沒有定義,則子類會繼承該屬性;若是子類中有定義,則不會繼承
>>> setattr(class_continue8.SchoolMember,'name','Python')    #在子類在沒有定義
>>> class_continue8.SchoolMember.name
'Python'
>>> class_continue8.Student.name    #所以子類會繼承該屬性
'Python'
>>> setattr(class_continue8.SchoolMember,'age',25)    #在父類中定義子類有定義的屬性
>>> class_continue8.SchoolMember.age
25
>>> class_continue8.Student.age    #子類已經定義了age屬性,所以不會再繼承父類的該屬性
29

    從上面的例子能夠看出,對於父類、子類、對象,屬性的優先級以本地定義的爲主,若是本地沒有定義,則繼承上一級的屬性,若是有定義,則使用本地的。


delattr():刪除類中的屬性

>>> class_continue8.SchoolMember.name
'Python'
>>> delattr(class_continue8.SchoolMember,'name')
>>> class_continue8.SchoolMember.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class SchoolMember has no attribute 'name'


getattr()的主要用法以下:

程序代碼以下:
class person:

	def tell(self, name):
		print 'hi my name is', name

	def study(sefl):
		print 'I am studying Py right now.'

class student(person):

	def study(sefl):
		print 'I am studying Py right now.'

p = person()

vars = ['tell', 'study']

v1 = vars[0]

getattr(p, v1)('Oldboy')

執行狀況以下:
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python cla_getattr_with_no_arg10.py 
hi my name is Oldboy

    對於該程序,若是直接使用p.v1('name')來訪問類中的方法是不行的,會有以下的提示錯誤:

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day4$ python cla_getattr_with_no_arg10.py 
Traceback (most recent call last):
  File "cla_getattr_with_no_arg10.py", line 21, in <module>
    p.v1('Oldboy')
AttributeError: person instance has no attribute 'v1'

    所以getattr()的主要用途就在於有一個類有多個方法時,須要對該類的全部方法都執行一遍,那麼能夠將類中的方法都寫入一個列表中,再經過getattr()和循環的方法來完成,這樣就能夠節省不少代碼空間。

相關文章
相關標籤/搜索