3 面向对象程序设计¶
Abstract
面向对象编程(OOP)是一种组织程序的方法,它将本章介绍的许多思想结合在一起。与数据抽象中的函数一样,类创建了在使用和实现数据之间的抽象屏障。与调度字典(dispatch dictionaries)一样,对象响应行为请求。与可变数据结构一样,对象具有无法从全局环境直接访问的本地状态。Python 对象系统提供了方便的语法来促进使用这些技术来组织程序。这种语法的大部分在其他面向对象的编程语言之间共享。
3.1 Definition(类的定义)¶
class Account:
def __init__(self, account_holder):
self.balance = 0
self.holder = account_holder
def deposit(self, amount):
self.balance = self.balance + amount
return self.balance
def withdraw(self, amount):
if amount > self.balance:
return 'Insufficient funds'
self.balance = self.balance - amount
return self.balance
这里建了一个Account
类
3.2 Inheritance(继承)¶
Abstract
在面向对象编程范式中,我们经常会发现不同类型之间存在关联,尤其是在类的专业化程度上。即使两个类具有相似的属性,它们的特殊性也可能不同。
class CheckingAccount(Account):
"""从账号取钱会扣出手续费的账号"""
withdraw_charge = 1
interest = 0.01
def withdraw(self, amount):
return Account.withdraw(self, amount + self.withdraw_charge)
Note
Python 支持子类从多个基类继承属性的概念,这种语言功能称为多重继承(multiple inheritance)。
>>> class AsSeenOnTVAccount(CheckingAccount, SavingsAccount):
def __init__(self, account_holder):
self.holder = account_holder
self.balance = 1 # 赠送的 1 $!
- 如果没有非歧义引用,就按预期正确解析
- 但是,当引用不明确时,例如对
Account
和CheckingAccount
中定义的withdraw
方法的引用。Python使用称为C3方法解析排序的递归算法解析此名称。可以在所有类上使用mro方法查询任何类的方法解析顺序。
>>> [c.__name__ for c in AsSeenOnTVAccount.mro()]
['AsSeenOnTVAccount', 'CheckingAccount', 'SavingsAccount', 'Account', 'object']
3.3 实例¶
Abstract
我们看到类和对象本身可以使用函数和字典来表示。通过以这种方式实现对象系统的目的是为了说明使用对象的隐喻并不需要特定的编程语言。即使在没有内置对象系统的编程语言中,程序也可以是面向对象的。为了实现对象,我们将放弃点表示法(它需要内置语言支持),而是创建行为方式与内置对象系统的元素非常相似的调度字典。我们已经看到了如何通过调度字典实现消息传递的行为。为了完全实现对象系统,我们在实例、类和基类之间发送消息,它们都是包含属性的字典。
在开始我们的实现之前,假定我们有一个类的实现,它可以查出任何不属于实例的名称。我们将一个类作为参数传递给 make_instance 的形参 cls
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
是一个能够响应get
和set
消息的调度字典。set
消息和Python
对象系统中的属性赋值操作相对应:所有已经赋值的属性直接存储在对象的本地属性字典中。在get
消息中,如果name
没有出现在本地attributes
字典中,则会在类中查找。如果从类中查找返回的值是一个函数,则这个函数必须被绑定到实例。
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
我们基于字典构建的对象系统在实现上与Python
中的内置对象系统非常相似。在 Python 中,任何用户定义的类的实例都有一个特殊属性__dict__
,它将该对象的本地实例属性存储在一个字典中,就像我们的attribute
字典一样。但Python
与我们的系统不同之处在于,它区分了一些特殊方法,这些方法与内置函数交互,以确保这些函数对许多不同类型的参数都能正确运行。
3.4 泛型编程¶
Abstract
对象抽象的一个核心概念就是泛型函数,这种函数能够接受多种不同类型的值。我们将思考三种不同的用于实现泛型函数的技术:共享接口,类型派发和类型强制转换。在建立这些概念的过程中,我们也会发现一些特性,这些特性支持Python
对象系统创泛型函数
对比函数重载(同一作用域内函数名相同,参数列表不同的函数),函数模板只需要一个函数就实现了函数重载的部分功能(参数个数相同类型不同,函数重载需要定义多个同名参数列表不同的函数)
函数模板具体化,函数模板具体化就是将某一(某几)个要处理的类型单独处理,需要单独写一个实现,形式是template<> void fun(type& t)
;,函数模板的具体化和普通函数可以同时存在,调用顺序是普通函数 > 函数模板具体化 > 模板函数
以CS61A中的Number类为例理解