了解Python中的抽象性,第二部分
像C++、Java、Smalltalk和其他许多语言一样,Python是一种面向对象的语言。然而,Python是多范式的,这意味着你可以选择最适合任务的范式,但其核心范式是面向对象编程。
术语 "对象"指的是一个数据(属性)的集合,有一组访问和修改这些数据的方法。我们在第1部分讨论了全局变量和函数,但在这里我们描述与对象有关的抽象概念。
对象和类
对象由属性和方法组成。属性是作为对象的一部分的变量,而方法则有点像存储在属性中的函数。
当考虑到类和对象之间可能存在的关系以及相关的功能时,面向对象的设计是很有用的。它适用于实现导致软件重用的模式的情况。
为了规划出要使用的类和方法,思考问题的描述并将名词与可能的类、动词与方法、以及形容词与属性联系起来可能会有帮助。
类是一组对象。类似于我们在第一部分中涉及的命名空间定义,类的命名空间指的是class 语句中的所有代码将执行的特殊命名空间。
继承(Inheritance
继承包括从一般的对象中创建专门的类。一个类可以是一个甚至多个类的子类****(多重继承)。通过使用一个以上的超类(一个可以创建子类的类),你可以创建独立且不同的正交功能。
例子
为了识别一个超类,我们把它写在类名后面的括号里。
class Censor:
def init(self):
self.erased = []
def bleep(self, sequence):
return [x for x in sequence if x not in self.erased]
class Bleep(Censor): # Subclass of Censor
def init(self): # Overrides init method from Filter superclass
self.erased = ['CENSORED']
Censor 它本身是一个普通的类,实际上并不审查任何东西。然而,当我们可以把它作为一个基类,一旦 方法被继承和 覆盖,就可以从序列中剔除'CENSORED'字样时,它就变得有用了。bleep init
抽象基类,也被称为Python中的abc 模块,在确定一个类应该能够提供的功能方面非常有用,而不需要实际实现。
例子
from abc import ABC, abstractmethod
class Print(ABC):
@abstractmethod
def echo(self):
pass
@abstractmethod 是一个装饰器,它将该方法标记为抽象的,因此是必须在子类中实现的。我们可以对其进行子类化,并通过覆盖 方法将其实例化。echo
class Typewriter(Print):
def echo(self):
print("Overwriting the base class!")
在实例化Typewriter 对象t 之后,我们可以访问其echo 方法。
>>> t = Typewriter()
>>> isinstance(t, Typewriter)
True
>>> t.echo()
Overwriting the base class!
然而,如果我们创建另一个不是Print 的子类的类,我们仍然可以通过类型检查。
class Scribe:
def echo(self):
print("Sample Text")
它作为一个Typewriter 对象通过,但并不是一个对象。
>>> s = Scribe()
>>> isinstance(s, Print)
False
有时不可能进行子类化,例如从别人的模块中导入Scribe 。在这种情况下,我们可以将Scribe 注册为Typewriter ,如下所示。
>>> Typewriter.register(Scribe)
<class '__main__.Scribe'>
>>> isinstance(s, Typewriter)
True
>>> issubclass(Scribe, Typewriter)
True
有一点需要注意的是,虽然我们注册的抽象类的任何子类的实例都会返回True ,但它不会有其属性echo ,这也符合鸭子类型(对象的类型或类别不如它定义的方法重要)。

多态性
多态性来自希腊语,意思是 "有多种形式"。它是一种以类似方式对待不同类型和类别的对象的能力。具体来说,这意味着你可以在不同类的对象上使用相同的操作,而不需要知道其类实现的具体细节。
这样做的好处是松散耦合,更灵活,更容易重构。然而,当我们需要找出一个对象有哪些方法或属性时,我们可以使用接口和某些函数。
例子
repr() 函数返回给定对象的一个可打印的表示性字符串。它与str() 不同,因为它主要用于调试(计算精确的值,并打印出与表示值完全相同的字符串);而str() 是用于输出目的。
def text_length(x):
print("The length of", repr(x), "is", len(x))
repr() 是一个多态性的例子,因为它的工作与输入类型无关。
>>> text_length('Test String')
The length of 'Test String' is 11
>>> text_length([90, 32, 12, 09])
The length of [90, 32, 12, 09] is 4
封装
封装是对外界隐藏对象如何工作的不重要的细节。对象可以隐藏其内部状态(属性)。这使得属性只能通过方法获得。然而在 Python 中,所有的属性都是公开的,这意味着程序员可以意外地使状态不一致。
例子
这里我们创建了一个对象,并将变量c 绑定到它身上。假设我们写一个HiddenObject 类,用一个属性而不是全局变量来封装对象中的一个名字。
>>> pcbh = HiddenObject()
>>> pcbh.set_name('Princess Consuela Banana')
>>> pcbh.get_name()
'Princess Consuela Banana'
在创建另一个对象后,我们看到原来的对象h ,保留了它的名字,因为它有自己的状态,可以用类的方法改变。
>>> cb = HiddenObject()
>>> cb.set_name('Quack')
>>> cb.get_name()
'Quack'
>>> pcbh.get_name()
'Princess Consuela Banana'