編寫符合Python風格的對象

導語:本文章記錄了本人在學習Python基礎之面向對象篇的重點知識及我的心得,打算入門Python的朋友們能夠來一塊兒學習並交流。

本文重點:python

一、掌握編寫Pythonic code背後經常使用的特殊方法;
二、掌握可擴展的格式化輸出方法;
三、瞭解可散列對象的設置以及節省內存的__slots__對象。

1、自定義具備Python風格的類

自定義的向量類須要支持基本的輸出,迭代,求模。程序員

一、自定義向量類型

從自定義向量類型入手寫出符合Python風格的對象,這離不開特殊方法的支持。
咱們指望的自定義向量類型應支持的基本功能:數組

  • 構造,__init__
  • 輸出,__repr__和__str__
  • 迭代,__iter__
  • 求模,__abs__
  • 轉化爲字節序列,__bytes__

代碼實現以下:函數

import math
from array import array
class Vector2d:
    typecode='d'
    def __init__(self,x,y):
        self.x=float(x)
        self.y=float(y)

    def __str__(self):
        return str(tuple(self))

    def __iter__(self):
        return (i for i in (self.x,self.y))

    def __repr__(self):
        classname=type(self).__name__
        s="{}({},{})".format(classname,*self)
        return s

    def __abs__(self):
        return math.hypot(self.x,self.y)

    def __bytes__(self):
        return (bytes(self.typecode,encoding='utf-8')+
                bytes(array(self.typecode,self)))

二、使用一個類方法實現備選構造方法

咱們能將實例轉化爲字節序列,那麼也應構造一個將實例轉化爲字節序列的方法。學習

@classmethod
    def frombytes(cls,seqs):
        typecode=chr(seqs[0])
        memv=memoryview(seqs[1:]).cast(typecode)
        return cls(*memv)

memoryview是泛化和去數學化的數組。code

三、classmethod和staticmethod兩個裝飾器

classmethod:定義操做類而不是操做實例的方法,類方法的第一個參數是類自己而不是實例。最多見的用途是定義備選構造方法(返回cls(*))component

staticmethod:是普通的函數,只是碰巧在類的定義體中,而不是在模塊層定義。orm

2、格式化顯示

一、擴展內置的format函數

經過改寫format背後的__format__能夠寫出可擴展的格式。
實例1:實現format對向量類的處理對象

def __format__(self,fmt_spec=''):
        components=(format(v,fmt_spec)for v in self)
        return "({},{})".format(*components)

實例2:經過尾部自定義格式代碼p實現將直角座標向量轉化爲極座標向量。繼承

def __format__(self,fmt_spec=''):
        if fmt_spec[-1]=="p":
            coord=(abs(self),self.angle())
            spec=fmt_spec[:-1]
            components=(format(v,spec)for v in coord)
            outer="<{},{}>"
        else:
            coord=self
            components = (format(v, fmt_spec) for v in self)
            outer = "({},{})"
        return outer.format(*components)

本段代碼的重點在於判斷格式中是否存在自定義格式符p,並進行對應的格式處理。

3、將對象變爲可散列的

目前的向量是不可散列的,而可散列對象須要知足:

(1)支持hash()函數,而且經過hash()獲得的散列值是不變的;
(2)支持經過__eq__()方法來檢測相等性;
(3)若a==b爲真,則hash(a)=hash(b)也爲真。

因此咱們須要把對象定爲不可變,而後自定義__hash__。

一、將對象定爲不可變的

經過使用兩個前導下劃線。將屬性標記爲私有的。

@property
    def x(self):
        return self.__x
    @property
    def y(self):
        return self.__y

二、自定義__hash__()

使用異或運算符實現。

def __hash__(self):
        return hash(self.x)^hash(self.y)

4、其它

一、只讀屬性的設置

  • 私有屬性的設置只是避免修改方法意外訪問不該更改的值,而沒法防止有意的改動。
  • 經過__dict__屬性能夠查詢Python如何存儲向量的屬性名,而後只要編寫a._Vector2d__x=5這樣的代碼就會惡意賦值。
  • Python程序員約定使用一個下劃線前綴編寫「受保護」的屬性即self._x,他們認爲應該使用命名約定來避免意外覆蓋屬性。

二、利用__slots__節省內存

默認狀況下,Python在各個實例中名爲__dict__的字典中儲存實例屬性,相應地會消耗大量內存。
經過__slots__類屬性,並讓解釋器把實例屬性存儲在元組中,能夠節省大量內存。

class Vector2d:
    __slots__ = ('__x','__y')
    typecode='d'
#其餘方法實現省略

使用__slots__應注意的問題:

  • __slots__沒法從超類繼承而來,每一個子類都須要定義__slots__屬性;
  • 實例只能擁有__slots__中列出的屬性,除非把'__dict__'加入到__slots__中(這樣作就失去了節省內存的初衷)
  • 若是不把'weakref__'加入__slots__,實例就不能做爲弱引用的目標。

當處理的實例規模較小時,禁止建立動態屬性或不支持弱引用是比較好的選擇。

三、覆蓋類屬性

經過建立子類能夠把繼承自父類的實例屬性覆蓋掉。

class Shortvector2d(Vector2d):
    typecode = 'f'
#其它方法實現省略
相關文章
相關標籤/搜索