Day-9: 面對對象高級編程

  數據封裝、繼承和多態只是面向對象編程中最基礎的3個概念。html

  下面整理面向對象高級編程的更爲強大的技巧。python

  使用__slots__:Python屬於動態語言,能夠容許已建立好的類動態地綁定任何屬性和方法。可是,給實例綁定後,由該類建立的其餘其餘實例是沒有綁定的;不過,能夠給類綁定,那麼有該類建立的實例均會擁有該屬性和方法。編程

>>> class Student(object):
...     pass
...
>>> s = Student()
>>> s.name = 'Michael' # 動態給實例綁定一個屬性
>>> print s.name
Michael
>>> def set_age(self, age): # 定義一個函數做爲實例方法
...     self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s, Student) # 給實例綁定一個方法
>>> s.set_age(25) # 調用實例方法
>>> s.age # 測試結果
25
>>> def set_score(self, score):
...     self.score = score
...
>>> Student.set_score = MethodType(set_score, None, Student)

  同時,正由於動態語言的這種特性,爲了不過於屬性綁定,class類中使用__slots__來限制容許的屬性。app

>>> class Student(object):
...     __slots__ = ('name', 'age') # 用tuple定義容許綁定的屬性名稱
...
>>> s = Student() # 建立新的實例
>>> s.name = 'Michael' # 綁定屬性'name'
>>> s.age = 25 # 綁定屬性'age'
>>> s.score = 99 # 綁定屬性'score'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

__slots__屬性只對父類起做用,但繼承的子類是不起做用的,除非子類中也定義__slots__,這樣,子類容許定義的屬性就是自身的__slots__加上父類的__slots__。函數

  使用@property裝飾器:學習

  在實際使用中,爲了檢查修改參數的合法性,會定義一個get和set函數,對set函數進行一系列的嚴格驗證。測試

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) # ok!
>>> s.get_score()
60
>>> s.set_score(9999)
Traceback (most recent call last):
  ...
ValueError: score must between 0 ~ 100!

  爲了簡化上述調用和設置的過程,將兩個方法直接變成調用屬性同樣簡單,使用@property裝飾器。spa

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
>>> s = Student()
>>> s.score = 60 # OK,實際轉化爲s.set_score(60)
>>> s.score # OK,實際轉化爲s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
  ...
ValueError: score must between 0 ~ 100!

就能像調用普通屬性同樣方便了!設計

  也能夠只定義只讀屬性:3d

class Student(object):

    @property
    def birth(self):
        return self._birth

    @birth.setter
    def birth(self, value):
        self._birth = value

    @property
    def age(self):
        return 2014 - self._birth
  •  多重繼承

  在繼承時,因爲對象會有多個屬性,通常按照繼承關係來講,選取其中一個大的類別做爲主線,即主線是單一繼承下來的。除主線外,其餘的屬性能夠做爲功能,多重繼承下來。這種設計稱爲Mixin。

  例如,下面中,Mammal和Bird做爲主線,Runnable和Flyable做爲Mixin功能增長進去,構成多重繼承。

class Animal(object):
    pass

# 大類:
class Mammal(Animal):
    pass

class Bird(Animal):
    pass
class Runnable(object):
    def run(self):
        print('Running...')

class Flyable(object):
    def fly(self):
        print('Flying...')
class Dog(Mammal, Runnable):
    pass
class Bat(Mammal, Flyable):
    pass
  • 定製類

  以前提到過的以雙下劃線開頭和結尾的屬性,是屬於特殊屬性,它們是用來定製類的。

  __str__():顯示print該類的實例的名稱

>>> class Student(object):
...     def __init__(self, name):
...         self.name = name
...     def __str__(self):
...         return 'Student object (name: %s)' % self.name
...
>>> print Student('Michael')
Student object (name: Michael)

  __repr__():直接顯示變量

class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__

>>>s = Student('Michael')
>>>s
Student object (name: Michael)

  __iter__():服務於for...in的迭代循環,該方法返回一個迭代對象,而後,Python的for循環會一直調用next()方法獲得循環的下一個值,直到遇到StopIteration錯誤後退出循環。

lass Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化兩個計數器a,b

    def __iter__(self):
        return self # 實例自己就是迭代對象,故返回本身

    def next(self):
        self.a, self.b = self.b, self.a + self.b # 計算下一個值
        if self.a > 100000: # 退出循環的條件
            raise StopIteration();
        return self.a # 返回下一個值
>>> for n in Fib():
...     print n
...
1
1
2
3
5
...
46368
75025

  __getitem__():容許像list同樣按索引取值和切片。

class Fib(object): def __getitem__(self, n): if isinstance(n, int): a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): start = n.start stop = n.stop a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L
>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101
>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

  __getattr__():當調用類的屬性或者方法不存在時,運行__getattr__()方法。

class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99
>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99

  __call__():對實例自己進行調用時,啓用該方法。

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

    def __call__(self):
        print('My name is %s.' % self.name)
>>> s = Student('Michael')
>>> s()
My name is Michael.

經過callable()函數,能夠判斷一個對象是不是「可調用」對象。

  更多定製類的方法,參考Python文檔

 注:本文爲學習廖雪峯Python入門整理後的筆記

相關文章
相關標籤/搜索