大爽Python入门教程 7-4 面向对象编程 封装、继承、多态
大爽Python入门公开课教案 点击查看教程总目录
第二节部分的例子,给人最直观的感受,
就是类能够通过self
来实现跨函数(方法)传参。
在参数比较多的情景中,这算是一种比较省事的手段。
实际上,类的真正优点不在于此。
而在于其三大特性:封装、继承、多态。
1 三大特性
封装
封装(encapsulation)
简单的来讲,就是隐藏对象的属性和实现细节,仅对外公开接口。
就像使用手机,我们并不需要知道手机的底层原理,
也不需要知道运行的app的源码,就可以直接使用手机和APP。
这样在开发过程中,
能更轻松地理解类的方法,调用(使用)类的方法,。
继承
继承(inheritance)
如果一个类别B“继承自”另一个类别A,就把这个B称为“A的子类”,
而把A称为“B的父类别”也可以称“A是B的超类”。
继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码。
在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。
另外,为子类追加新的属性和方法也是常见的做法。
比如有一个父类
class Parent:
pass
写一个继承它的子类,写法如下
class Child(Person):
pass
也就是在类名后加括号,括号里面加父类。
python支持同时继承多个类,
继承多个时,用逗号隔开。
class GrandChild(Person, Child):
pass
多态
多态(polymorphism)
指为不同数据类型的实体提供统一的接口。
一个父类可以有多个子类,不同的子类对同一方法,有不同的实现。
这就实现了使用一个接口(方法),实现不同的效果(对于不同的子类)。
多态性一般依赖于继承和重写(见下)。
2 常用补充
重写(Override)
子类重写父类的函数,功能上覆盖掉父类。
class Parent:
def __init__(self, name):
self.name = name
def greet(self):
print("This is %s" % self.name)
class Child(Parent):
def greet(self):
print("Hi, I'm %s" % self.name)
p = Parent("Zhang san")
c = Child("Li si")
p.greet()
c.greet()
其输出为
This is Zhang san
Hi, I'm Li si
其中Child
类重写了父类的greet
方法。
Child
类创建的实例,其调用greet
方法时,
调用的是Child
的greet
方法,
父类的方法被覆盖掉了,不再执行。
super
子类可以重写父类的方法,也可以直接沿用父类的方法(什么都不写)。
在重写时,常有的情形是,沿用父类的代码,
然后做一点修改(比如增加一些属性之类的)。
在这个时候,我们无法直接使用父类的方法,因为这个方法名已经被子类用了。
这个时候我们尝试用super
方法来调用父类的方法,执行父类的代码。
比如现有类Animal
如下
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
我们想要继承这个类,实现一个宠物类Pet
,
这个类要添加一个新的属性owener
,其代码如下
class Pet(Animal):
def __init__(self, name, age, owner):
super().__init__(name, age)
self.owner = owner
其中
super().__init__(name, age)
就是调用了父类Animal
的__init__
方法。
这个也有其他写法(但不常用)
super(Pet, self).__init__(name, age) # 主要是python2用,python3也可以,但不常用了
3 实例理解
Animal
我们这里以动物为例,
先实现一个一个基础的动物类,作为所有的动物的父类。
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
self.sound = ""
def get_name(self):
return self.name
def show_info(self):
print("%s is %s years old" % (self.get_name(), self.age))
def say(self):
print("%s say: %s" % (self.get_name(), self.sound))
Dog
, Cat
, Sheep
接下来实现其子类Dog
, Cat
, Sheep
, 代码如下
class Dog(Animal):
def __init__(self, name, age):
super().__init__(name, age)
self.kind = "Dog"
self.sound = "Wang wang"
def get_name(self):
return "%s %s" % (self.kind, self.name)
class Cat(Animal):
def __init__(self, name, age):
super().__init__(name, age)
self.kind = "Cat"
self.sound = "miao~"
def get_name(self):
return "%s %s" % (self.kind, self.name)
class Sheep(Animal):
def __init__(self, name, age):
super().__init__(name, age)
self.kind = "Sheep"
self.sound = "Mie~"
def get_name(self):
return "%s %s" % (self.kind, self.name)
效果
然后执行以下代码
animals = [
Dog("Duoduo", 10),
Cat("Bubu", 12),
Sheep("Lili", 15),
]
for a in animals:
a.show_info()
a.say()
输出如下
Dog DuoDuo is 10 years old
Dog DuoDuo say: Wang wang
Cat Bubu is 12 years old
Cat Bubu say: miao~
Sheep Lili is 15 years old
Sheep Lili say: Mie~
分析
上面的例子展示了面向对象写法的一些特点,或者说优点。
- 父类
Animal
的很多代码可以直接沿用。 - 添加功能时,只用作一点针对性的修改。
- 不同的子类有相同的方法名,不用单独区分再处理,直接一起调用,方便。
同一方法,不同子类可以有不同实现,最后效果也不同。
4 isinstance 和 type 解析
之前讲过,判断对象类比,一般推荐使用isinstance
方法。
这里深入讲一下isinstance
和type
的区别。
isinstance
先看一下isinstance
的文档说明。
isinstance(object, classinfo)
Return True if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof.
If object is not an object of the given type, the function always returns False. >
如果参数 object 是参数 classinfo 的实例或者是其 (直接、间接或 虚拟) 子类的实例,则返回 True。
如果 object 不是给定类型的对象,函数将总是返回 False。
简单来讲,isinstance
函数判断object
是否是classinfo
类或其子类的实例。
比如下面的代码
class A:
pass
class B(A):
pass
a = A()
b = B()
r1 = isinstance(a, A)
r2 = isinstance(a, B)
r3 = isinstance(b, A)
r4 = isinstance(b, B)
print(r1)
print(r2)
print(r3)
print(r4)
输出为
True
False
True
True
type
type
实际不是一个函数,而是一个类。
其文档说明为
class type(object)
With one argument, return the type of an object.
The return value is a type object and generally the same object as returned by object.class.
传入一个参数时,返回 object 的类型。
返回值是一个 type 对象,通常与 object.class 所返回的对象相同。
简单来讲,type
只能用来获取object
的类型,无法知道其父类子类之类的关系。
比如下面的代码
class A:
pass
class B(A):
pass
a = A()
b = B()
ta = type(a)
tb = type(b)
print(ta)
print(tb)
print(ta==tb)
print(ta==A)
print(ta==B)
print(tb==A)
print(tb==B)
输出为
<class '__main__.A'>
<class '__main__.B'>
False
True
False
False
True