Python class decorators for classes

decorator是一個滿常使用的方式,只要透過裝飾,就能很容易新增一個功能,然而被裝飾者,完全不必知道裝飾者的存在,減少耦合性。

例如今天有一個Canvas,要將文字畫在canvas上,並且加上邊框,文字並不需要知道邊框存不存在,這時就可以使用decorator附加上去。

Example:

首先簡單定義一個canvas,draw會先將內容寫在變數裡,然後在由show將結果顯示在terminal上面。

class Canvas( object ):
    """
    Define a sample canvas for terminal.
    """

    def __init__( self, width=15, height=5 ):
        """
        Init a params.
        """

        self.__data = []
        self.__width = width
        self.__height = height
        self.__setup( width, height )

    def __setup( self, width, height ):
        """
        Create a data block.
        """

        for hi in range( height ):

            self.__data.append( [] )

            for wi in range( width ):

                self.__data[ hi ].append( " " )

    def draw( self, content, x, y ):
        """
        Draw content to data block.
        """

        width = self.__width

        for char in content:

            if x >= width:

                break

            self.__data[y][x] = char

            x = x + 1

    def show( self ):
        """
        Print to terminal.
        """

        for hi in range( self.__height ):

            print "".join( self.__data[ hi ] )

接著View定義了基本的位置,和width計算,在由drawTo繪製到canvas。

class View( object ):
    """
    Define a sample view.
    """

    def setY( self, y ):
        """
        Set a position of y.
        """

        self._y = y

    def getY( self ):
        """
        Get a position of y.
        """

        return self._y

    def setX( self, x ):
        """
        Set a position of x.
        """

        self._x = x

    def getX( self ):
        """
        Get a position of x.
        """

        return self._x

    def getWidth( self ):
        """
        Get the view width.
        """

        return 0

    def drawTo( self, canvas, parent = None ):
        """
        Draw something to canvas.
        """

        pass

定義一個decorator,讓border裝飾。

class ViewDecorator( object ):
    """
    Decorator of View.
    """

    def __init__( self, *views ):

        self.__views = views

    def __call__( self, drawTo ):
        views  = self.__views;

        def draw_chain( base_view, canvas ):

            drawTo( base_view, canvas )

            for view in views:

                view.drawTo( canvas, base_view )

        return draw_chain

Text和border分別繼承於View,text會將文字畫到canvas,而border則是畫框([]),此時可以看到text已經裝飾了border "@ViewDecorator``( Border() )"

class Border( View ):
    """
    Extend a View.
    """

    def drawTo( self, canvas, parent = None ):
        """
        Draw a border to canvas by parent position.
        """

        if parent:

            width = parent.getWidth()
            x = parent.getX()
            y = parent.getY()
            canvas.draw( "[", x - 1 , y )
            canvas.draw( "]", width + x , y )

class Text( View ):
    """
    Extend a view.
    """

    def __init__( self, content ):
        """
        Set a text.
        """

        self.__content = content

    def getWidth( self ):
        """
        Get a width of text.
        """

        return len( self.__content )

    @ViewDecorator( Border() )
    def drawTo( self, canvas, parent = None ):
        """
        Draw a text to canvas.
        """
        canvas.draw( self.__content, self.getX(), self.getY() )

執行python decorator.py

canvas = Canvas()
text = Text( "Sparrow" )
text.setX( 3 )
text.setY( 2 )
text.drawTo( canvas )
canvas.show()

最後就會顯示下列資訊在螢幕上。

[Sparrow]

source code