文章目录
七、再谈抽象
多态:可对不同类型的对象执行相同的操作,而这些操作就像“被施了魔法”一样能够
正常运行。
封装:对外部隐藏有关对象工作原理的细节。
继承:可基于通用类创建出专用类。
7.1 对象魔法 ⭐🌙🔺
7.1.1 多态
“有多种形态”。这大致意味着即便你不知道变量指向的是哪种对象,也能够对其执行操作,且操作的行为将随对象所属的类型(类)而异。
>>> object.get_price()
2.5
-
- 案例展示
标准库模块random包含一个名为
choice
的函数,它从序列中随机选择一个元素。
# 案例一 count()方法
>>> from random import choice
>>> x = choice(['Hello, world!', [1, 2, 'e', 'e', 4]])
>>> x.count('e')
2
# 案例二 加法运算夫
>>> 1 + 2
3
>>> 'Fish' + 'license'
'Fishlicense'
# 案例三 repr
def length_message(x):
print("The length of", repr(x), "is", len(x))
>>> length_message('Fnord')
The length of 'Fnord' is 5
>>> length_message([1, 2, 3])
The length of [1, 2, 3] is 3
7.1.2 封装
多态让你无需知道对象所属的类(对象的类型)就能调用其方法
封装让你无需知道对象的构造就能使用它。
- 问题引入
已知一个名为OpenObject的类
>>> o = OpenObject() # 对象就是这样创建的
>>> o.set_name('Sir Lancelot')
>>> o.get_name()
'Sir Lancelot'
当创建多个对象时,出现问题,因为他们公用同一个变量
>>> o1 = OpenObject()
>>> o2 = OpenObject()
>>> o1.set_name('Robin Hood')
>>> o2.get_name()
'Robin Hood'
如上代码所见,设置一个对象的名称时,将自动设置另一个对象的名称。
解决办法
:将其作为一个属性即可,属性是归属于对象的变量,就像方法一样。
对象有自己的状态。对象的状态由其属性(如名称)描述。对象的方法可能修改这些属性,因此对象将一系列函数(方法)组合起来,并赋予它们访问一些变量(属性)的权限,而属性可用于在两次函数调用之间存储值。
使用属性而非全局变量重新编写前面的类,并将其重命名为ClosedObject,就可像下面这样使用它:
>>> c = ClosedObject()
>>> c.set_name('Sir Lancelot')
>>> c.get_name()
'Sir Lancelot'
>>> r = ClosedObject()
>>> r.set_name('Sir Robin')
r.get_name()
'Sir Robin'
>>> c.get_name()
'Sir Lancelot'
7.1.3 继承
见7.2.5章节、7.2.6章节
7.2 类
7.2.1 类的创建
类:类型,一种对象。每个对象都属于特定的类,并被称为该类的实例。
例如:鸟就是鸟类的实例。
鸟类是一个非常通用(抽象)的类,它有多个子类:你看到的那只鸟可能属于子类“云雀”。你可将“鸟类”视为由所有鸟组成的集合,而“云雀”是其一个子集。一个类的对象为另一个类的对象的子集时,前者就是后者的子类。因此“云雀”为“鸟类”的子类,而“鸟类”为“云雀”的超类。
类的所有实例都有该类的所有方法,因此子类的所有实例都有超类的所有方法
- 定义一个类:Person
class Person:
def set_name(self, name):
self.name = name
def get_name(self):
return self.name
def greet(self):
print("Hello, world! I'm {}.".format(self.name))
class语句创建独立的命名空间,用于在其中定义函数
>>> foo = Person()
>>> bar = Person()
>>> foo.set_name('Luke Skywalker')
>>> bar.set_name('Anakin Skywalker')
>>> foo.greet()
Hello, world! I'm Luke Skywalker.
>>> bar.greet()
Hello, world! I'm Anakin Skywalker.
对foo调用set_name和greet时,foo都会作为
第一个参数
自动传递给它们。我将这个参数命名为self,这非常贴切。实际上,可以随便给这个参数命名,但鉴于它总是指向对象本身,因此习惯上将其命名为self。
self很有用,甚至必不可少。如果没有它,所有的方法都无法访问对象本身——要操作的属性所属的对象
# 外部访问属性
>>> foo.name
'Luke Skywalker'
>>> bar.name = 'Yoda'
>>> bar.greet()
Hello, world! I'm Yoda.
7.2.2 属性、函数、方法
- 实例展示
# 案例一
>>> class Class:
... def method(self):
... print('I have a self!')
...
>>> def function():
... print("I don't...")
...
>>> instance = Class()
>>> instance.method()
I have a self!
>>> instance.method = function
>>> instance.method()
I don't...
# 案例二
>>> class Bird:
... song = 'Squaawk!'
... def sing(self):
... print(self.song)
...
>>> bird = Bird()
>>> bird.sing()
Squaawk!
>>> birdsong = bird.sing
>>> birdsong()
Squaawk!
7.2.3 隐藏–不能从外部访问⭐
解释何为:不能从外部访问?
>>> c.name
'Sir Lancelot'
>>> c.name = 'Sir Gumby'
>>> c.get_name()
'Sir Gumby'
毕竟,如果能直接访问ClosedObject(对象c所属的类)的属性name,就不需要创建方法setName和getName了。
例如,ClosedObject可能在对象修改其名称时向管理员发送电子邮件。这种功能可能包含在方法set_name中。但如果直接设置c.name,结果将如何呢?什么都不会发生——根本不会发送电子邮件。为避免这类问题,可将属性定义为私有
。
私有属性不能从对象外部访问
,而只能通过存取器方法
(如get_name和set_name)来访问。
class Secretive:
def __inaccessible(self):
print("Bet you can't see me ...")
def accessible(self):
print("The secret message is:")
# 现在从外部不能访问__inaccessible,但在类中(如accessible中)依然可以使用它。
self.__inaccessible()
>>> s = Secretive()
>>> s.__inaccessible()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Secretive instance has no attribute '__inaccessible'
>>> s.accessible()
The secret message is:
Bet you can't see me ...
7.2.4 类的命名空间
在class语句中定义的代码都是在一个特殊的命名空间(
类的命名空间
)内执行的,而类的所有成员都可访问这个命名空间。类定义其实就是要执行的代码段。
class MemberCounter:
members = 0
def init(self):
MemberCounter.members += 1
>>> m1 = MemberCounter()
>>> m1.init()
>>> MemberCounter.members
1
>>