Python 面向對象編程
一,類和實例
1,類是創建實例的模板,而實例則是一個一個具體的對象,各個實例擁有的數據都互相獨立,互不影響;
class Student(object):
pass
定義類:定義類是通過class
關鍵字,class
後面緊接著是類名,即Student
,類名通常是大寫開頭的單詞,緊接著是(object)
,
表示該類是從哪個類繼承下來的,通常,如果沒有合適的繼承類,就使用object
類,這是所有類最終都會繼承的類。
定義好了Student
類,就可以根據Student
類創建出Student
的實例,創建實例是通過類名+()實現的:
bart = Student()
2,方法就是與實例綁定的函數,和普通函數不同,方法可以直接訪問實例的數據;
方法就是類的功能,也就是定義在類裏面的函數,它實現了某個功能
通過在實例上調用方法,我們就直接操作了對象內部的數據,但無需知道方法內部的實現細節。
和靜態語言不同,Python允許對實例變量綁定任何數據,也就是說,對於兩個實例變量,雖然它們都是同一個類的不同實例,但擁有的變量名稱都可能不同
由於類可以起到模板的作用,因此,可以在創建實例的時候,把一些我們認為必須綁定的屬性強制填寫進去。通過定義一個特殊的__init__
方法,在創建實例的時候,就把name
,score
等屬性綁上去:
class Student(object):
def __init__(self, name, score):#構造函數:就是類在實例化的時候做的某些初始化操作
self.name = name
self.score = score
def __del__(self):#析構函數:析構函數就是這個實例在銷毀的時候做的一些操作。
self.cur.close()
self.coon.close()
註意到__init__
方法的第一個參數永遠是self
,表示創建的實例本身,因此,在__init__
方法內部,就可以把各種屬性綁定到self
,因為self
就指向創建的實例本身。
有了__init__
方法,在創建實例的時候,就不能傳入空的參數了,必須傳入與__init__
方法匹配的參數,但self
bart = Student(‘Bart Simpson‘, 59)
print(bart.name)
print(bart.score)
和普通的函數相比,在類中定義的函數只有一點不同,就是第一個參數永遠是實例變量self
,並且,調用時,不用傳遞該參數。
除此之外,類的方法和普通函數沒有什麽區別,所以,你仍然可以用默認參數、可變參數、關鍵字參數和命名關鍵字參數。
實例:
import pymysql
class OpMySql1: #經典類
pass
class OpMySql(object):#新式類
def __init__(self,host,user,password,db,port=3306,charset=‘utf8‘):#構造函數,就是類在實例化的時候做的某些初始化操作
schema = {
‘user‘:user,
‘host‘:host,
‘password‘:password,
‘db‘:db,
‘port‘:port,
‘charset‘:charset
}
try:
self.coon = pymysql.connect(**schema)
except Exception as e:
print(‘數據庫連接異常!%s‘%e)
quit(‘數據庫連接異常!%s‘%e)
else:#沒有出異常的情況下,建立遊標
self.cur = self.coon.cursor(cursor=pymysql.cursors.DictCursor)
def execute(self,sql):#類的方法
try:
self.cur.execute(sql)
except Exception as e:
print(‘sql有錯誤%s‘%e)
return e
if sql[:6].upper()==‘SELECT‘:
return self.cur.fetchall()
else:#其他sql語句的話
self.coon.commit()
return ‘ok‘
def __del__(self):#析構函數:析構函數就是這個實例在銷毀的時候做的一些操作。
self.cur.close()
self.coon.close()
res = OpMySql(‘211.149.218.16‘,‘jxz‘,‘123456‘,db=‘jxz‘) #實例化
print(res.execute(‘select * from stu;‘))
print(res.execute(‘select * from stu;‘))
print(res.execute(‘select * from stu;‘))
二,數據封裝
把一些功能的實現細節不對外暴露,類中對數據的賦值、內部調用對外部用戶是透明的,這使類變成了一個膠囊或容器,裏面包含著類的數據和方法。
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):#類的方法
print(‘%s: %s‘ % (self.name, self.score))
要定義一個方法,除了第一個參數是self
外,其他和普通函數一樣。要調用一個方法,只需要在實例變量上直接調用,除了self
不用傳遞,其他參數正常傳入:
print(bart.print_score())
這樣一來,我們從外部看Student
類,就只需要知道,創建實例需要給出name
和score
,而如何打印,都是在Student
類的內部定義的,這些數據和邏輯被“封裝”起來了,調用很容易,但卻不用知道內部實現的細節。
封裝的另一個好處是可以給Student
類增加新的方法,比如get_grade
:
class Student(object): ... def get_grade(self): if self.score >= 90: return ‘A‘ elif self.score >= 60: return ‘B‘ else: return ‘C‘
三,實例屬性與類屬性(實例變量與類變量)
由於Python是動態語言,根據類創建的實例可以任意綁定屬性。
給實例綁定屬性的方法是通過實例變量,或者通過self
變量:
class Student(object):
def __init__(self, name):
self.name = name#實例變量,必須實例化之後才能用,成員變量
s = Student(‘Bob‘) s.score = 90
但是,如果Student
類本身需要綁定一個屬性呢?可以直接在class中定義屬性,這種屬性是類屬性,歸Student
類所有:
class Student(object):
name = ‘Student‘#類變量
當我們定義了一個類屬性後,這個屬性雖然歸類所有,但類的所有實例都可以訪問到
class Student(object):
name = ‘Student‘
s = Student() #創建實例 s
print(s.name) #打印name屬性,因為實例並沒有name屬性,所有會繼續查找class的name屬性,輸出結果:Student
print(Student.name) #打印類的name 屬性,輸出結果:Student
s.name = ‘Michael‘ #給實例綁定name 屬性
print(s.name) #由於實例屬性優先級比類屬性高,因此它會屏蔽掉類屬性,輸出結果:Michael
print(Student.name) #但是類屬性並未消失,用Student.name仍然可以訪問,輸出結果:Student
del s.name #如果刪除實例的name屬性
print(s.name) #再次調用s.name 由於實例的name 屬性沒有找到,類的name屬性就顯示出來,輸出結果:Student
從上面的例子可以看出,在編寫程序的時候,千萬不要對實例屬性和類屬性使用相同的名字,因為相同名稱的實例屬性將屏蔽掉類屬性,但是當你刪除實例屬性後,再使用相同的名稱,訪問到的將是類屬性。
四,訪問限制(私有變量)
如果要讓內部屬性不被外部訪問,可以把屬性的名稱前加上兩個下劃線__
,在Python中,實例的變量名如果以__
開頭,就變成了一個私有變量(private),只有內部可以訪問,外部不能訪問
class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print(‘%s: %s‘ % (self.__name, self.__score))
改完後,對於外部代碼來說,沒什麽變動,但是已經無法從外部訪問實例變量.__name
和實例變量.__score
了
bart = Student(‘Bart Simpson‘, 59)
print(bart.__name)
輸出結果:
AttributeError: ‘Student‘ object has no attribute ‘__name‘
五,繼承和多態(python 不支持多態)
繼承可以把父類的所有功能都直接拿過來,這樣就不必重零做起,子類只需要新增自己特有的方法,也可以把父類不適合的方法覆蓋重寫
當我們定義一個class的時候,可以從某個現有的class繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Super class)
class Animal(object):
def run(self):
print(‘Animal is running...‘)
class Dog(Animal):#對於Dog來說,Animal就是它的父類,對於Animal來說,Dog就是它的子類
pass
class Cat(Animal):
pass
繼承有最大的好處是子類獲得了父類的全部功能。由於Animial
實現了run()
方法,因此,Dog
和Cat
作為它的子類,什麽事也沒幹,就自動擁有了run()
方法
dog = Dog() dog.run() cat = Cat() cat.run()
輸出結果:
Animal is running...
Animal is running...
class Animal(object):#父類
def run(self):
print(‘Animal is running...‘)
class Dog(Animal):#子類也可以將父類不適合的方法覆蓋重寫
def run(self):
print(‘Dog is running...‘)#修改
class Cat(Animal):
def run(self):
print(‘Cat is running...‘)
六,使用@property(屬性方法)
在綁定屬性時,如果我們直接把屬性暴露出去,雖然寫起來很簡單,但是,沒辦法檢查參數,導致可以把成績隨便改:
s = Student() s.score = 9999
這顯然不合邏輯。為了限制score的範圍,可以通過一個set_score()
方法來設置成績,再通過一個get_score()
來獲取成績,這樣,在set_score()
方法裏,就可以檢查參數:
class Student(object):
def get_score(self):
return self._score
def set_score(self, value):
if not isinstance(value, int):
raise ValueError(‘score must be an integer!‘)
if value < 0 or value > 100:
raise ValueError(‘score must between 0 ~ 100!‘)
self._score = value
s = Student()
s.set_score(60)
print(s.get_score())#輸出結果 60
s.set_score(9999)
print(s.get_score()) #輸出結果: ValueError: score must between 0 ~ 100!
但是,上面的調用方法又略顯復雜,沒有直接用屬性這麽直接簡單。
有沒有既能檢查參數,又可以用類似屬性這樣簡單的方式來訪問類的變量呢?
還記得裝飾器(decorator)可以給函數動態加上功能嗎?對於類的方法,裝飾器一樣起作用。Python內置的@property
裝飾器就是負責把一個方法變成屬性調用的:
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError(‘score must be an integer!‘)
if value < 0 or value > 100:
raise ValueError(‘score must between 0 ~ 100!‘)
self._score = value@property
的實現比較復雜,我們先考察如何使用。把一個getter方法變成屬性,只需要加上@property
就可以了,
此時,@property
本身又創建了另一個裝飾器@score.setter
,負責把一個setter方法變成屬性賦值,於是,我們就擁有一個可控的屬性操作:
s = Student()
s.score = 60
print(s.score) #輸出結果:60
s.score = 9999
print(s.score) #輸出結果:ValueError: score must between 0 ~ 100!
註意到這個神奇的@property
,我們在對實例屬性操作的時候,就知道該屬性很可能不是直接暴露的,而是通過getter和setter方法來實現的。
還可以定義只讀屬性,只定義getter方法,不定義setter方法就是一個只讀屬性:
class Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2015 - self._birth
上面的birth
是可讀寫屬性,而age
就是一個只讀屬性,因為age
可以根據birth
和當前時間計算出來
小結
@property
廣泛應用在類的定義中,可以讓調用者寫出簡短的代碼,同時保證對參數進行必要的檢查,這樣,程序運行時就減少了出錯的可能性。
七,靜態方法和類方法
@staticmethod #靜態方法
def other():
print(‘我是other‘)
@classmethod#類方法,也不需要實例化,直接就能用。它靜態方法高級一點
#它可以使用類變量和類方法。
def class_fun(cls):
print(cls.xiaohei)
cls.class_fun2()
@classmethod
def class_fun2(cls):
print(‘我是類方法2‘)
靜態方法
不需要實例化就能直接用的,其實和類沒有什麽關系,就是一個普通的函數
寫在了類裏面而已,也用不了self的那些東西,也調用不類裏面的其他函數。
Python 面向對象編程