再谈抽象
这一章学习Python面向对象编程,学习对象、类、多态、封装、继承、接口和内省。
对象魔法
- 多态: 可对不同类型的对象执行相同的操作。
- 封装: 对外部隐藏有关对象工作原理的细节。
- 继承: 可基于通用类创建出专用类
多态
Python中多态用的地方超级多,也叫鸭子模型。我们一直用的‘+’就应用了多态。它的功能是将俩个对象相加,至于对象是什么类型。它并不在意,无论是数字还是字符串类型。它都可以使用。这就是多态。
>>> 1 + 1
2
>>> 'one' + 'one'
'oneone'
封装
封装就像用电视机,你按电源,电视打开。但是你不知道具体是怎么实现的。对于外部它是保密的,是密封的。python中的封装是需要将属性变成私有的。
继承
继承就像是子承父业,孩子不需要做什么就可以拥有和父亲一样多的东西。孩子还可以在父亲的基础上做出自己的东西。也可以把父亲的企业按照自己的意愿再修改。
类
类是一种抽象。一种概括,人细分的话有很多很多种人,但是特别的分的话只有男人和女人两种人。类就是这样,概括最重要的部分。而具体到每个个体,这个个体就是一个对象,是男人类或者女人类的一个实例。如果稍微细分一点,分为男青年类,男青年类就是男人类的一个子类。而男人类就是男青年类的一个超类。
创建自定义类
python中使用class语句定义类。
SyntaxError: invalid syntax
>>> 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))
类中的函数叫做方法。方法和函数区别在于方法的第一个参数总是self。那么self是什么呢?self指向对象本身。
私有属性
默认情况下,可从外部访问对象的属性。所以python中没有私有属性,但是要让方法或者属性变成私有的(不能从外部访问),只需让其名称以两个下划线打头即可。
>>> class Secretive:
def __inaccessible(self): # 私有方法
print('Bet you can\'t see me ')
def accessible(self):
print('The secret message is: ')
self.__inaccessible()
pass
>>> s = Secretive()
>>> s.__inaccessible() # 私有方法,无法在类外部访问
Traceback (most recent call last):
File "<pyshell#63>", line 1, in <module>
s.__inaccessible()
AttributeError: 'Secretive' object has no attribute '__inaccessible'
>>> s.accessible()
The secret message is:
Bet you can't see me
可以在使用私有方法时在开头加上一个下划线和类名,来在类外访问私有方法。
>>> s._Secretive__inaccessible()
Bet you can't see me
>>> # No I can see you
类的命名空间。
定义类时,在class语句中定义的代码都是在一个特殊的命名空间(类的命名空间)里执行的。另外类定义其实就是要执行的代码段
>>> class C:
print('Class C being defined')
Class C being defined
看下面代码的示范,其中定义了一个属性member。再次体会一下类的命名空间。
>>> class MemberCounter:
members = 0
def init(self):
MemberCounter.members += 1
pass
>>> m1 = MemberCounter()
>>> m1.init()
>>> MemberCounter.members
1
>>> m2 = MemberCounter()
>>> m2.init()
>>> MemberCounter.members
2
>>> m1.members
2
>>> m2.members
2
每个实例都可访问这个类作用域内的变量。但是,在实例中给属性赋值时会遮盖。相当于局部变量和全局变量的关系。
>>> m1.members = 'Two'
>>> m2.members
2
>>> m1.members
'Two'
>>> MemberCounter.members
2
>>> m2.members is MemberCounter.members
True
>>> m1.members is m2.members
False
指定超类
>>> class Filter:
def init(self):
self.blocked = []
pass
def filter(self, sequence):
return [x for x in sequence if x not in self.blocked]
在class语句中的超类名,并将其用圆括号括起。
>>> class SPAMFilter(Filter): # SPAMFilter是Filter的子类
def init(self): # 重写超类Filter的方法init
self.blocked = ['SPAM']
pass
pass
>>> f = Filter()
>>> f.init()
>>> f.filter([1, 2, 3])
[1, 2, 3]
>>> s = SPAMFilter()
>>> s.init()
>>> s.filter(['SPAM', 'SPAM', 'SPAM', 'SPAM', 'eggs', 'bacon', 'SPAM'])
['eggs', 'bacon']
深入探讨继承
使用内置方法issubclass判断一个类是否是另一个类的子类】
>>> issubclass(SPAMFilter, Filter)
True
>>> issubclass(Filter, SPAMFilter)
False
使用__bases__ 寻找一个类的基类
>>> SPAMFilter.__bases__
(<class '__main__.Filter'>,)
>>> Filter.__bases__
(<class 'object'>,)
使用isinstance确定对象是否是特定类的实例
>>> s = SPAMFilter()
>>> isinstance(s, SPAMFilter)
True
>>> isinstance(s, Filter)
True
>>> isinstance(s, Class)
False
如果你要获悉对象属于哪个类,可使用属性__class__
>>> s.__class__
<class '__main__.SPAMFilter'>
多个超类
Python支持多重继承
>>> class Calculator:
def calculate(self, expression):
self.value = eval(expression)
>>> class Talker:
def talk(self):
print('Hi, my value is ', self.value)
>>> class TalkingCalculator(Calculator, Talker):
pass
>>> tc = TalkingCalculator()
>>> tc.calculate('1 + 2 +3')
>>> tc.talk()
Hi, my value is 6
接口
接口类似于多态,介绍几个方法。
使用hasattr(),检查方法是否存在
>>> hasattr(tc, 'talk')
True
>>> callable(getattr(tc, 'talk', None))
True
使用setattr(),设置对象的属性
>>> setattr(tc, 'name', 'Mr.Gumby')
>>> tc.name
'Mr.Gumby'
使用__dict__检查对象中存储的所有值。
>>> tc.__dict__
{'value': 6, 'name': 'Mr.Gumby'}
抽象基类
介绍一下显示接口,即抽象类。python之前的不支持的,之后通过引入模块abc提高了官方解决方案。
模块abc,显式接口
使用@abstactmethod来将方法标记为抽象的
>>> from abc import ABC, abstractmethod
>>> class Talker(ABC):
@abstractmethod
def talk(self):
pass
抽象类无法实例化,抽象类需要继承
>>> Talker()
Traceback (most recent call last):
File "<pyshell#197>", line 1, in <module>
Talker()
TypeError: Can't instantiate abstract class Talker with abstract methods talk
继承的子类若没有重写抽象方法,还是抽象类。直到抽象方法被重写。
>>> class Knigger(Talker):
def talk(self):
print("NI!")
>>> k = Knigger()
小结
- 对象: 对象由属性和方法组成。属性不过是属于对象的变量,而方法是存储在属性的函数。相比于其他函数,(关联的)方法有一个不同之处,那就是它总是将其所属的对象作为第一个参数,而这个参数通常被命名为self。
- 类: 类表示一组(或一类)对象,而每个对象都属特定的类。类的主要任务是定义其实例将包含的方法。
- 多态: 多态指的是能够同样地对待不同类型和类的对象,即无需知道对象属于哪个类就可以调用其方法。
- 封装:对象可能隐藏(封装)其内部状态。在有些语言中,这意味着对象的状态(属性)只能通过其方法来访问。在Python中,所有的属性都是公有的,但直接访问对象的状态时应谨慎行事,因为这可能在不经意间导致状态不一致。
- 继承:一个类可以是一个或多个类的子类,在这种情况下,子类将继承超类的所有方法。你可以指定多个超类,通过这样做可组合正交(独立且不相关)的功能。为此,一种常见的做法是使用一个核心超类以及一个或多个混合超类。
- 接口和内省: 一般而言,你无须过于深入地研究对象,而只依赖于多态来调用所需的方法。然而,如果要确定对象包含哪些方法或属性,有一些函数可供你用来完成这种工作。
- 抽象基类: 使用模块abc可创建抽象基类。抽象基类用于指定子类必须提供哪些功能,却不实现这些功能。
学习的新函数
函数 | 描述 |
---|---|
callable(object) | 判断对象是否是可调用的(如是否是函数或方法) |
getattr(object, name[,default]) | 获取属性的值,还可提供默认值 |
hasattr(object, name) | 确定对象是否有指定的属性 |
isinstance(object, class) | 确定对象是否是指定类的实例 |
issubclasss(A, B) | 确定A是否是B的子类 |
random.choice(sequence) | 从一个非空序列中随机地选择一个元素 |
setattr(object, name, value) | 将对象的指定属性设置为指定的值 |
type(object) | 返回对象的类型 |
注:小结摘抄于Python基础教程(第三版)