SICP Python 描述 2.6 實現類和對象

2.6 實現類和對象

來源:2.6 Implementing Classes and Objects

譯者:飛龍

協議:CC BY-NC-SA 4.0

在使用面向對象編程范式時,我們使用對象隱喻來指導程序的組織。數據表示和操作的大部分邏輯都表達在類的定義中。在這一節中,我們會看到,類和對象本身可以使用函數和字典來表示。以這種方式實現對象系統的目的是展示使用對象隱喻并不需要特殊的編程語言。即使編程語言沒有面向對象系統,程序照樣可以面向對象。

為了實現對象,我們需要拋棄點運算符(它需要語言的內建支持),并創建分發字典,它的行為和內建對象系統的元素差不多。我們已經看到如何通過分發字典實現消息傳遞行為。為了完整實現對象系統,我們需要在實例、類和基類之間發送消息,它們全部都是含有屬性的字典。

我們不會實現整個 Python 對象系統,它包含這篇文章沒有涉及到的特性(比如元類和靜態方法)。我們會專注于用戶定義的類,不帶有多重繼承和內省行為(比如返回實例的類)。我們的實現并不遵循 Python 類型系統的明確規定。反之,它為實現對象隱喻的核心功能而設計。

2.6.1 實例

我們從實例開始。實例擁有具名屬性,例如賬戶余額,它可以被設置或獲取。我們使用分發字典來實現實例,它會響應“get”和“set”屬性值消息。屬性本身保存在叫做attributes的局部字典中。

就像我們在這一章的前面看到的那樣,字典本身是抽象數據類型。我們使用列表來實現字典,我們使用偶對來實現列表,并且我們使用函數來實現偶對。就像我們以字典實現對象系統那樣,要注意我們能夠僅僅使用函數來實現對象。

為了開始我們的實現,我們假設我們擁有一個類實現,它可以查找任何不是實例部分的名稱。我們將類作為參數cls傳遞給make_instance

>>> def make_instance(cls):
        """Return a new object instance, which is a dispatch dictionary."""
        def get_value(name):
            if name in attributes:
                return attributes[name]
            else:
                value = cls['get'](name)
                return bind_method(value, instance)
        def set_value(name, value):
            attributes[name] = value
        attributes = {}
        instance = {'get': get_value, 'set': set_value}
        return instance

instance是分發字典,它響應消息getsetset消息對應 Python 對象系統的屬性賦值:所有賦值的屬性都直接儲存在對象的局部屬性字典中。在get中,如果name在局部attributes字典中不存在,那么它會在類中尋找。如果cls返回的value為函數,它必須綁定到實例上。

綁定方法值。make_instance中的get_value使用get尋找類中的具名屬性,之后調用bind_method。方法的綁定只在函數值上調用,并且它會通過將實例插入為第一個參數,從函數值創建綁定方法的值。

>>> def bind_method(value, instance):
        """Return a bound method if value is callable, or value otherwise."""
        if callable(value):
            def method(*args):
                return value(instance, *args)
            return method
        else:
            return value

當方法被調用時,第一個參數self通過這個定義綁定到了instance的值上。

2.6.2 類

類也是對象,在 Python 對象系統和我們這里實現的系統中都是如此。為了簡化,我們假設類自己并沒有類(在 Python 中,類本身也有類,幾乎所有類都共享相同的類,叫做type)。類可以接受getset消息,以及new消息。

>>> def make_class(attributes, base_class=None):
        """Return a new class, which is a dispatch dictionary."""
        def get_value(name):
            if name in attributes:
                return attributes[name]
            elif base_class is not None:
                return base_class['get'](name)
        def set_value(name, value):
            attributes[name] = value
        def new(*args):
            return init_instance(cls, *args)
        cls = {'get': get_value, 'set': set_value, 'new': new}
        return cls

不像實例那樣,類的get函數在屬性未找到的時候并不查詢它的類,而是查詢它的base_class。類并不需要方法綁定。

實例化。make_class中的new函數調用了init_instance,它首先創建新的實例,之后調用叫做__init__的方法。

>>> def init_instance(cls, *args):
        """Return a new object with type cls, initialized with args."""
        instance = make_instance(cls)
        init = cls['get']('__init__')
        if init:
            init(instance, *args)
        return instance

最后這個函數完成了我們的對象系統。我們現在擁有了實例,它的set是局部的,但是get會回溯到它們的類中。實例在它的類中查找名稱之后,它會將自己綁定到函數值上來創建方法。最后類可以創建新的(new)實例,并且在實例創建之后立即調用它們的__init__構造器。

在對象系統中,用戶僅僅可以調用create_class,所有其他功能通過消息傳遞來使用。與之相似,Python 的對象系統由class語句來調用,它的所有其他功能都通過點表達式和對類的調用來使用。

2.6.3 使用所實現的對象

我們現在回到上一節銀行賬戶的例子。使用我們實現的對象系統,我們就可以創建Account類,CheckingAccount子類和它們的實例。

Account類通過create_account_class函數創建,它擁有類似于 Python class語句的結構,但是以make_class的調用結尾。

>>> def make_account_class():
        """Return the Account class, which has deposit and withdraw methods."""
        def __init__(self, account_holder):
            self['set']('holder', account_holder)
            self['set']('balance', 0)
        def deposit(self, amount):
            """Increase the account balance by amount and return the new balance."""
            new_balance = self['get']('balance') + amount
            self['set']('balance', new_balance)
            return self['get']('balance')
        def withdraw(self, amount):
            """Decrease the account balance by amount and return the new balance."""
            balance = self['get']('balance')
            if amount > balance:
                return 'Insufficient funds'
            self['set']('balance', balance - amount)
            return self['get']('balance')
        return make_class({'__init__': __init__,
                           'deposit':  deposit,
                           'withdraw': withdraw,
                           'interest': 0.02})

在這個函數中,屬性名稱在最后設置。不像 Python 的class語句,它強制內部函數和屬性名稱之間的一致性。這里我們必須手動指定屬性名稱和值的對應關系。

Account類最終由賦值來實例化。

>>> Account = make_account_class()

之后,賬戶實例通過new消息來創建,它需要名稱來處理新創建的賬戶。

>>> jim_acct = Account['new']('Jim')

之后,get消息傳遞給jim_acct,來獲取屬性和方法。方法可以調用來更新賬戶余額。

>>> jim_acct['get']('holder')
'Jim'
>>> jim_acct['get']('interest')
0.02
>>> jim_acct['get']('deposit')(20)
20
>>> jim_acct['get']('withdraw')(5)
15

就像使用 Python 對象系統那樣,設置實例的屬性并不會修改類的對應屬性:

>>> jim_acct['set']('interest', 0.04)
>>> Account['get']('interest')
0.02

繼承。我們可以創建CheckingAccount子類,通過覆蓋類屬性的子集。在這里,我們修改withdraw方法來收取費用,并且降低了利率。

>>> def make_checking_account_class():
        """Return the CheckingAccount class, which imposes a $1 withdrawal fee."""
        def withdraw(self, amount):
            return Account['get']('withdraw')(self, amount + 1)
        return make_class({'withdraw': withdraw, 'interest': 0.01}, Account)

在這個實現中,我們在子類的withdraw中調用了基類Accountwithdraw函數,就像在 Python 內建對象系統那樣。我們可以創建子類本身和它的實例,就像之前那樣:

>>> CheckingAccount = make_checking_account_class()
>>> jack_acct = CheckingAccount['new']('Jack')

它們的行為相似,構造函數也一樣。每筆取款都會在特殊的withdraw函數中收費 $1,并且interest也擁有新的較低值。

>>> jack_acct['get']('interest')
0.01
>>> jack_acct['get']('deposit')(20)
20
>>> jack_acct['get']('withdraw')(5)
14

我們的構建在字典上的對象系統十分類似于 Python 內建對象系統的實現。Python 中,任何用戶定義類的實例,都有個特殊的__dict__屬性,將對象的局部實例屬性儲存在字典中,就像我們的attributes字典那樣。Python 的區別在于,它區分特定的特殊方法,這些方法和內建函數交互來確保那些函數能正常處理許多不同類型的參數。操作不同類型參數的函數是下一節的主題。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 要點: 函數式編程:注意不是“函數編程”,多了一個“式” 模塊:如何使用模塊 面向對象編程:面向對象的概念、屬性、...
    victorsungo閱讀 1,562評論 0 6
  • 2.5 面向對象編程 來源:2.5 Object-Oriented Programming 譯者:飛龍 協議:...
    布客飛龍閱讀 784評論 0 34
  • 定義類并創建實例 在Python中,類通過 class 關鍵字定義。以 Person 為例,定義一個Person類...
    績重KF閱讀 3,971評論 0 13
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,937評論 18 139
  • 本文為《爬著學Python》系列第十四篇文章。 中間因為工作原因隔了好久沒有更新,現在穩定很多,會陸續開始更新。不...
    SyPy閱讀 12,134評論 1 10