1. 程式人生 > >Python 面向對象編程

Python 面向對象編程

默認 urn 關鍵字 千萬 範圍 class a 靜態 dog 打印

一,類和實例

1,類是創建實例的模板,而實例則是一個一個具體的對象,各個實例擁有的數據都互相獨立,互不影響;

class Student(object):
    pass

定義類:定義類是通過class關鍵字,class後面緊接著是類名,即Student,類名通常是大寫開頭的單詞,緊接著是(object)

表示該類是從哪個類繼承下來的,通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類。

定義好了Student類,就可以根據Student類創建出Student的實例,創建實例是通過類名+()實現的:
bart = Student()

2,方法就是與實例綁定的函數,和普通函數不同,方法可以直接訪問實例的數據;

方法就是類的功能,也就是定義在類裏面的函數,它實現了某個功能

通過在實例上調用方法,我們就直接操作了對象內部的數據,但無需知道方法內部的實現細節。

和靜態語言不同,Python允許對實例變量綁定任何數據,也就是說,對於兩個實例變量,雖然它們都是同一個類的不同實例,但擁有的變量名稱都可能不同


由於類可以起到模板的作用,因此,可以在創建實例的時候,把一些我們認為必須綁定的屬性強制填寫進去。通過定義一個特殊的__init__方法,在創建實例的時候,就把namescore等屬性綁上去:
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

不需要傳,Python解釋器自己會把實例變量傳進去:

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類,就只需要知道,創建實例需要給出namescore,而如何打印,都是在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()方法,因此,DogCat作為它的子類,什麽事也沒幹,就自動擁有了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 面向對象編程