Python——面向對象

1. Propertiespython

  處於安全考慮,一般在對實例屬性進行讀寫時要進行必定的預判和處理,好比參數的判斷、淨化等,這些方法一般對應於getXXX、setXXX這樣的函數,Python提供了一種 「property」 機制,便於實現這樣的操做。編程

  首先,咱們來看一個只讀的 property:安全

>>> class Rectangle(object):
...     def __init__(self, width, height):
...         self.width = width
...         self.height = height
...     @property
...     def area(self):
...         return self.width * self.height
...

  property 經過裝飾器 @property 聲明,@property 所修飾的函數將成爲實例的一個屬性:閉包

>>> r = Rectangle(10, 10)
>>> print r.area
100

  上面的例子中,只讀性質 area 是動態生成的,可是該例並無徹底展現 property 的優點。對於那些可寫的屬性,每每須要通過額外的驗證,才容許對屬性的值進行某些修改,這裏咱們不妨假定,咱們只容許長方形實例的寬和高都是整數:函數式編程

class Rectangle(object):
    __slots__ =  '_width', '_height'

    @property
    def width(self):
        return self._width

    @width.setter
    def width(self, value): # 矩形的寬必須是整數
        if not isinstance(value, int):
            raise ValueError, 'Width must be an integer.'
        self._width = value

    @property
    def height(self):
        return self._height

    @height.setter
    def height(self, value): # 矩形的高必須是整數
        if not isinstance(value, int):
            raise ValueError, 'Height must be an integer.'
        self._height = value
         
    @property
    def area(self): # 矩形的面積
        return self._height * self._width
        
if __name__ == "__main__":
    r = Rectangle()
    r.width = 100
    r.height = 100.1 # 程序運行到這會終止,將拋出異常
    print r.areaself._width

  這個例子比較全面地展現了Python中 property 機制,@property 裝飾的方法在使用時徹底和屬性相同,可是property將公共的安全接口暴露給用戶,避免了直接對屬性進行操做時帶來的安全隱患。property 機制能夠在讀寫實例的屬性時進行額外操做。函數

  對於可讀寫的 property,首先使用 @property 定義只讀的部分 x,此時Python會自動生成一個 @x.setter 用於建立 x 的可寫部分。好比該例子中的性質 width,當設置了 width 的只讀部分後,自動生成一個 @width.setter裝飾器,用來修飾 width 的可寫部分。spa

  這個例子使用 Python 的 property 機制建立了一個寬高只能是整數的長方形類 Rectangle,規定 Rectangle 的實例具備整數類型的可讀寫屬性 width 和 height,同時還有隻讀性質 area。code

 

  上面的例子中,class body 中的 __slots__ 屬性又有什麼做用和含義呢? 對象

2. __slots__blog

  Python支持動態地爲實例、類型綁定屬性,這樣綁定的屬性都會存放在對象的私有屬性 __dict__ 中,例如:

>>> class E(object):
...     pass
...
>>> e = E()
>>> e.__dict__
{}

  能夠看到此時 e  尚未動態綁定的屬性,接下來:

>>> e.name = 'slots_example'
>>> e.__dict__
{'name': 'slots_example'}

  經過爲 e 動態綁定屬性 name,能夠看到已經將該屬性對應的鍵值對添加到 e.__dict__ 屬性中。

  類屬性 __slots__ 就是用來限制咱們可以給實例動態綁定的屬性數量和名稱的,以上面的類型 E 和 實例 e 爲例,若是 E 中定義了類屬性 __slots__,那麼 e 將再也不具備 __dict__ 屬性。同時,只能給 e 動態綁定那些名稱出如今 __slots__ 中的屬性。

  __slots__必定得是類屬性,類型上一般是一個元組,裏面的每一個元素都是字符串。

例:

>>> class Rectangle(object):
...     __slots__ = 'width', 'height' # 將 __slots__ 設置爲類屬性
...
>>> r = Rectangle()
>>> r.name = 'test'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Rectangle' object has no attribute 'name'
>>> r.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Rectangle' object has no attribute '__dict__'

  可見此時咱們 1. 沒法爲實例 r 動態綁定除了__slots__中列出的屬性,2.實例 r 沒有 __dict__屬性,這是 __slots__ 屬性最主要的功能。當某個類型可能會被實例化不少不少次時,若是每一個實例都有一些動態綁定的屬性,那麼存儲上的額外開銷是巨大的,此時能夠經過 __slots__ 屬性,限制這些實例中可以動態添加的額外屬性。

 

3. 閉包(closure)

  事實上,閉包並不能算做 Python 中的面向對象機制,應當看作是一種面向對象以外的手段。

  Python 支持函數式編程,能夠在一個函數中定義新的函數,例如裝飾器就是這樣定義的。此時內部額外定義的函數被稱爲嵌套函數(nested function),外部的函數被稱爲外函數(outer function),嵌套函數能夠訪問外函數的局部變量和參數,這種來自外函數的變量和參數稱爲自由變量(free variable)。

  閉包是指嵌套函數和它能夠訪問的自由變量。

例如:

>>> def f(a,b):
...     def g():
...         return a + b
...     return g
...
>>> f(2,3)
<function g at 0x0000000002072358>
>>> f(2,3)()
5

  嵌套函數 g() 能夠訪問 外函數 f() 的局部變量和參數,g() 和 f() 提供的自由變量共同構成了一個閉包。

  閉包在 Python 中是一個包含自由變量的函數對象,自由變量信息被保存在這個函數對象的 __closure__ 屬性中。__closure__屬性是一個元組,其中的每個元素都是一個Python cell對象

  仍是上面的例子,假設咱們將 f(2, 3) 返回的函數對象綁定到一個引用:

>>> g1 = f(2, 3)
>>> g1
<function g at 0x00000000020727B8>
>>> g1.__closure__
(<cell at 0x000000000219BD98: int object at 0x0000000001F56290>, <cell at 0x000000000219BCA8: int object at 0x0000000001F56278>)

  如何從這樣一個 cell 對象中查看具體的自由變量的信息呢?訪問cell對象的cell_contents屬性便可。

>>> g1.__closure__[0].cell_contents
2

  閉包能夠建立一些構造器,進一步建立一些須要在初始化階段就已經動態肯定某些環境信息的可調用對象,這與面向對編程中的類-->實例的模式是不一樣的。

相關文章
相關標籤/搜索