1. 程式人生 > >No.8 Python面向物件程式設計

No.8 Python面向物件程式設計

面向物件三大關鍵:封裝、繼承、多型。我們從這三個方面介紹Python的面向物件程式設計,同時會提到Python中類的魔術方法:

1. 封裝成類:

在Python中,我們使用以下方式進行類的宣告:

class Person (object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def sayHello(self):
        print 'Hi~ I am %s' % (self.name)

p = Person('xiaoming', 'male')
p.sayHello()

這裡需要講解的是以下幾點:

1. __init__(self,...):類似於python的建構函式,不過同時python的成員變數也需要生命在__init__(self,...)中。類內部的所有成員函式均需要一個預設的self引數,並且需要放在第一位,作用類似於我們在java中的this一樣

2. 在python中,公有,私有通過下劃線來表示 。__xx或者__func(self):表示該變數或者函式是私有的,在外部無法獲取。公有的屬性(成員變數或者函式)使用xxx 或者__xxx__來表示。我們一般直接使用變數名來表示公有的成員變數,使用__xx__來表示特殊的公有屬性

3. 由於python是一個動態的語言,所以我們可以給例項化的物件繫結新的變數。這僅對於我們自己宣告的類物件,內建的類是不可以的


我們可以使用dir(p)來檢視p物件的屬性:


python使用@classmethod 和 直接在類中建立屬性來獲得類方法和類變數(類似於java中的static方法和變數)

class Person(object):

    count = 0

    @classmethod
    def objNum(cls):
        return cls.count
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
        Person.count = Person.count + 1

    def sayHello(self):
        print 'Hi~ I am %s' % (self.name)

print Person.count  # 列印 0
p = Person('xiaoming', 'male') # 執行一次__init__(self)函式, count= 1
print p.count  # 列印1
print Person.objNum() #列印1
在java中,我們會宣告屬性,設定get和set方法,在python中同樣有類似的用法:
# _*_ coding = utf-8 _*_
class Person(object):

    count = 0

    @classmethod
    def objNum(cls):
        return cls.count

    def __init__(self, name, gender):
        self.name = name
        self.__gender = gender
        Person.count = Person.count + 1

    @property
    def gender(self):
        return self.__gender

    @gender.setter
    def gender(self, gender):
        if gender == 'male' or gender == 'female':
            self.__gender = gender
        else:
            raise ValueError("Wrong Gender")

    def sayHello(self):
        print 'Hi~ I am %s' % (self.name)

p = Person('xiaoming','male')
p.gender = 'xmale'
執行程式,會丟擲ValueError

二、繼承:

在Person類下面,我們宣告一個Boy類,來繼承Person

# _*_ coding = utf-8 _*_
class Person(object):

    count = 0

    @classmethod
    def objNum(cls):
        return cls.count

    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
        Person.count = Person.count + 1

    def sayHello(self):
        print 'Hi~ I am %s' % (self.name)


class Boy(Person):
    def __init__(self,name,gender,hobby):
        super(Boy, self).__init__(name, gender)
        self.hobby = hobby

b = Boy('xiaoming','male','coding')
print b.name,b.gender,b.hobby
同時,python也可以很好的實現多根繼承:
# _*_ coding = utf-8 _*_
class Person(object):

    count = 0

    @classmethod
    def objNum(cls):
        return cls.count

    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
        Person.count = Person.count + 1

    def sayHello(self):
        print 'Hi~ I am %s' % (self.name)

class Player(object):

    def __init__(self,game):
        self.game = game

    def playgame(self):
        print 'Play game %s' % (self.game)

class Boy(Person,Player):
    def __init__(self,name,gender,game,hobby):
        Person.__init__(self,name, gender)
        Player.__init__(self,game)
        self.hobby = hobby

b = Boy('xiaoming','male','LOL','coding')
print b.name,b.gender,b.game,b.hobby
這裡跟單根整合不同的地方是,我們在子類的__init__(self,...)函式中,採用了不同的方法呼叫父類__init__(),總結一下,有以下兩種方法:

1. super(Derived,self).__init__(arg1,arg2)

2. Base.__init__(self,args1..)

第一種方法僅適用於單根,第二種單根多根均可以,推薦使用第二種

三、多型:

python的多型沒有什麼特殊性,也很簡單。給上述Person 和Boy新增sayHello函式

# _*_ coding = utf-8 _*_
class Person(object):

    count = 0

    @classmethod
    def objNum(cls):
        return cls.count

    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
        Person.count = Person.count + 1

    def sayHello(self):
        print 'Hi~ I am Person %s' % (self.name)

class Player(object):

    def __init__(self,game):
        self.game = game

    def playgame(self):
        print 'Play game %s' % (self.game)




class Boy(Person,Player):
    def __init__(self,name,gender,game,hobby):
        Person.__init__(self,name, gender)
        Player.__init__(self,game)
        self.hobby = hobby

    def sayHello(self):
        print 'Hi~ I am Boy %s' % (self.name)

def sayHello(x):
    x.sayHello()

b = Boy('xiaoming','male','LOL','coding')
p = Person('xiaoming','male')
sayHello(b)
sayHello(p)
4. 魔術方法:

python的class中有很多魔術方法,這些魔術方法是我們不需要直接呼叫但是python的某些函式或者操作會呼叫的特殊方法。這類方法均為用雙下劃線包圍的函式,例如__str__。在我們對一個物件使用print 函式的時候,就會呼叫到物件的__str__()函式。如果我們對類中的一些魔術方法進行覆寫,就會得到我們想要的結果

例如,我們給Person新增一個__str__()方法:

# _*_ coding = utf-8 _*_
class Person(object):

    count = 0

    @classmethod
    def objNum(cls):
        return cls.count

    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
        Person.count = Person.count + 1
    def __str__(self):
        return "I'm Person %s" % (self.name)

    def sayHello(self):
        print 'Hi~ I am Person %s' % (self.name)
        
p = Person('xiaoming','male')
print p
就會列印我們的想要的 I'm Person xiaoming

在前面我們講到了,可以給具體的例項物件繫結屬性。如果我們不希望一個例項可以隨意的新增屬性呢?

我們可以使用__slots__,這會宣告類允許的屬性列表:

# _*_ coding = utf-8 _*_
class Person(object):

    count = 0
    __slots__ = ('name','gender')
    @classmethod
    def objNum(cls):
        return cls.count

    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
        Person.count = Person.count + 1
    def __str__(self):
        return "I'm Person %s" % (self.name)

    def sayHello(self):
        print 'Hi~ I am Person %s' % (self.name)

p = Person('xiaoming','male')
p.score = 123
print p
就會報錯,提示該物件沒有屬性score。

python中還有很多我們常用到的魔術方法,比如表示比較的__cmp__,表示長度的__len__,和將物件變為可呼叫物件,使得物件可以直接變成函式來返回結果的__call__等,這裡不再贅述