@Author : Roger TX (425144880@qq.com)
@Link : https://github.com/paotong999
一、面向?qū)ο缶幊趟枷?/h2>
面向?qū)ο缶幊獭狾bject Oriented Programming,簡稱OOP,是一種程序設(shè)計(jì)思想。OOP把對象作為程序的基本單元,一個對象包含了數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)。
在Python中,所有數(shù)據(jù)類型都可以視為對象,當(dāng)然也可以自定義對象。自定義的對象數(shù)據(jù)類型就是面向?qū)ο笾械念悾–lass)的概念。
二、類與對象(實(shí)例)
在面向?qū)ο蟮某绦蛟O(shè)計(jì)過程中有兩個重要概念:類(class)和對象(object,也被稱為實(shí)例,instance)
- 類是某一批對象的抽象,可以把類理解成某種概念
- 對象是根據(jù)類創(chuàng)建出來的一個個具體的“對象”,一個具體存在的實(shí)體

Python定義類的簡單語法如下:
class 類名(object):
執(zhí)行語句...
零個到多個類變量...
零個到多個方法...
Python 的語法要求:如果從程序的可讀性方面來看,Python 的類名必須是由一個或多個有意義的單詞連綴而成的,每個單詞首字母大寫,其他字母全部小寫,單詞與單詞之間不要使用任何分隔符。
需要說明的是:
- 類中各成員之間的定義順序沒有任何影響,各成員之間可以相互調(diào)用。
-
(object)表示該類是從哪個類繼承下來的,繼承的概念我們后面再講,通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類。
對象(實(shí)例)的創(chuàng)建和使用:
class Person :
'這是一個學(xué)習(xí)Python定義的一個Person類'
# 下面定義了一個類變量
hair = 'black'
def __init__(self, name = 'Charlie', age=8):
# 下面為Person對象增加2個實(shí)例變量
self.name = name
self.age = age
def say(self, content):
# 下面定義了一個say方法
print(content)
print(self.age)
f = Person()
f.__init__(age=11) # 等同于Person(age=11)
f.say('123')
- 創(chuàng)建實(shí)例可以通過類名+()
Person()實(shí)現(xiàn) - 創(chuàng)建對象時默認(rèn)調(diào)用構(gòu)造方法,可以傳入?yún)?shù),初始化實(shí)例變量
-
__init__構(gòu)造函數(shù)只能return None
Python 類所包含的最重要的兩個成員就是變量和方法:
- 類變量屬于類本身,用于定義該類本身所包含的狀態(tài)數(shù)據(jù)
- 實(shí)例變量則屬于該類的實(shí)例對象,用于定義實(shí)例對象所包含的狀態(tài)數(shù)據(jù)
- 方法則用于定義該類的對象的行為或功能實(shí)現(xiàn)
在類中定義的方法默認(rèn)是實(shí)例方法,實(shí)例方法至少應(yīng)該定義一個參數(shù),該參數(shù)通常會被命名為 self。
- 定義實(shí)例方法的方法與定義函數(shù)的方法基本相同,只是實(shí)例方法的第一個參數(shù)會被綁定到方法的調(diào)用者(該類的實(shí)例)。
- 類所包含的類變量可以動態(tài)增加或刪除(程序在類體中為新變量賦值就是增加類變量),程序也可在任何地方為已有的類增加變量;程序可通過 del 語句刪除己有類的類變量。
- 實(shí)例對象的實(shí)例變量也可以動態(tài)增加或刪除(只要對新實(shí)例變量賦值就是增加實(shí)例變量),因此程序可以在任何地方為己有的對象增加實(shí)例變量;程序可通過 del 語句刪除已有對象的實(shí)例變量。
使用 __slots__ 限制實(shí)例的屬性
Python允許在定義class的時候,定義一個特殊的 __slots__ 變量,來限制該class實(shí)例能添加的屬性
class Student(object):
__slots__ = ('name', 'age') # 用tuple定義允許綁定的屬性名稱
- 由于'score'沒有被放到
__slots__中,所以不能綁定score屬性,試圖綁定score將得到AttributeError的錯誤 - 使用
__slots__要注意,__slots__定義的屬性僅對當(dāng)前類實(shí)例起作用,對繼承的子類是不起作用的
三、變量與方法
構(gòu)造方法
在實(shí)例方法中有一個特別的方法:
__init__,這個方法被稱為構(gòu)造方法。構(gòu)造方法用于構(gòu)造該類的對象,Python 通過調(diào)用構(gòu)造方法返回該類的對象(無須使用 new)。構(gòu)造方法是一個類創(chuàng)建對象的根本途徑,因此 Python 還提供了一個功能:如果開發(fā)者沒有為該類定義任何構(gòu)造方法,那么 Python 會自動為該類定義一個只包含一個 self 參數(shù)的默認(rèn)的構(gòu)造方法。
方法的調(diào)用
對象訪問方法或變量的語法是:
對象.變量|方法(參數(shù))。在這種方式中,對象是主調(diào)者,用于訪問該對象的變量或方法。在使用類調(diào)用實(shí)例方法時,Python 不會自動為第一個參數(shù)綁定調(diào)用者。實(shí)際上也沒法自動綁定,因此實(shí)例方法的調(diào)用者是類本身,而不是對象。
如果程序依然希望使用類來調(diào)用實(shí)例方法,則必須手動為方法的每一個參數(shù)傳入?yún)?shù)值。
類變量和實(shí)例變量

通常來說,實(shí)例變量是對于每個實(shí)例都獨(dú)有的數(shù)據(jù),而類變量是該類所有實(shí)例共享的屬性和方法。
- 類變量又叫全局變量,是屬于類的特性,實(shí)例先找實(shí)例化變量,然后再去找類變量
- 類變量可以用實(shí)例去調(diào)用
- 如果類變量有多重繼承關(guān)系, 就需要按照指定的路線進(jìn)行查找
下面代碼定義了一個 Address 類,并為該類定義了多個類變量:
class Address :
detail = '廣州'
def __init__(self, detail):
# 嘗試直接訪問類變量
#print(detail) # 報錯
# 通過類來訪問類變量
print(Address.detail) # 輸出 廣州
# 通過類來訪問Address類的類變量
print(Address.detail)
addr = Address('廣州')
# 修改addr實(shí)例的變量
addr.detail = '佛山'
print(Address.detail) # 輸出 廣州
print(addr.detail) # 輸出 佛山
- 因?yàn)閯討B(tài)語言的特性,
addr.detail = '佛山'并沒有改變類變量,只是新增了一個實(shí)例變量 - 在實(shí)例方法內(nèi)部,可以通過
類名.類變量來訪問類變量,還可以使用self__class__.類變量來訪問類變量
四、實(shí)例方法與類方法、靜態(tài)方法
實(shí)例方法
普通實(shí)例方法,第一個參數(shù)需要是 self,它表示一個具體的實(shí)例本身。
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
def __str__(self):
return ("{year}-{month}-{day}").format(year=self.year,month=self.month,day=self.day)
def yesterday(self):
self.day-=1
date=Date(2018,10,20)
print(date)
date.yesterday()
print(date)
類方法
在一個方法的上面,加上裝飾器 @classmethod 就表示這個方法是類方法。
對于類方法,它的第一個參數(shù)不是self,是cls,它表示這個類本身。
靜態(tài)方法
在一個方法的上面,加上裝飾器 @staticmethod 就表示這個方法是靜態(tài)方法。
靜態(tài)方法對參數(shù)沒有要求,可以不傳。
class Foo(object):
X = 1
Y = 14
@staticmethod
def averag(*mixes):
# "父類中的靜態(tài)方法"
return sum(mixes) / len(mixes)
@staticmethod
def static_method():
# "父類中的靜態(tài)方法"
print ("父類中的靜態(tài)方法")
return Foo.averag(Foo.X, Foo.Y)
@classmethod
def class_method(cls):
# 父類中的類方法
print ("父類中的類方法" )
return cls.averag(cls.X, cls.Y)
class Son(Foo):
X = 3
Y = 5
@staticmethod
def averag(*mixes):
# "子類中重載了父類的靜態(tài)方法"
print ("子類中重載了父類的靜態(tài)方法")
return sum(mixes) / 3
p = Son()
print (p.averag(1,5)) # 最后一段,結(jié)果為2.0
print (p.static_method()) # static_method段,結(jié)果為7.5
print (p.class_method()) # class_method段,最后一段,結(jié)果為2.6
五、成員可見性
成員的可見性
- 公開的,外部可以訪問的,public。
- 私有的,外部不可訪問的,private。
可見性關(guān)鍵字
- 其它語言中使用public和private關(guān)鍵字,python中沒有
- python中使用
__雙下劃線區(qū)分公開和私有,__表示成員是私有的
class Student():
sum1 = 1
def __init__(self,name,age):
self.name = name
self.age = age
self.score = 0 #增加變量score,并給他初始值
def __marking(self,score):
#加雙下劃線,變?yōu)樗接?,在外部不能引? if score < 0:
return "該同學(xué)的成績不正常"
self.score = score
print(self.name,"同學(xué)本次考試的成績?yōu)?",self.score)
student = Student('李華',18)
result = student.__marking(10)
print(result)
執(zhí)行后 'Student' object has no attribute '__marking' 錯誤,因?yàn)?__marking 對于 Student 不可見
通過 student.__dict__ 以及 dir(student) 兩個內(nèi)置的函數(shù)可以查看到stu對象的所有數(shù)據(jù)成員和方法
{'name': '李華', 'age': 18, 'score': 0}
['_Student__marking', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'score', 'sum1']
可以看到原本的 __marking 變成了 _Student__marking
訪問私有成員
class Address :
def __init__(self, detail):
self.__detail = detail
addr = Address('廣州')
addr.__detail = '佛山'
print(addr.__dict__) # {'_Address__detail': '廣州', '__detail': '佛山'}
print(addr._Address__detail) # 廣州
私有變量可以使用 _Address__detail 進(jìn)行強(qiáng)制訪問
私有變量不可見,但是對其進(jìn)行賦值操作,并不會報錯,這是因?yàn)镻ython動態(tài)語言的特性, 賦值只是新增了一個實(shí)例變量,并沒有改變原有的私有變量