Python面向对象编程之继承与多态详解
创新互联公司是一家集网站建设,碑林企业网站建设,碑林品牌网站建设,网站定制,碑林网站建设报价,网络营销,网络优化,碑林网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。
本文实例讲述了Python面向对象编程之继承与多态。分享给大家供大家参考,具体如下:
Python 类的继承
在OOP(Object Oriented Programming)程序设计中,当我们定义一个class的时候,可以从某个现有的class 继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。
我们先来定义一个class Person,表示人,定义属性变量 name 及 sex (姓名和性别);
定义一个方法print_title():当sex是male时,print man;当sex 是female时,print woman。参考如下代码:
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person): # Child 继承 Person
pass
May = Child("May","female")
Peter = Person("Peter","male")
print(May.name,May.sex,Peter.name,Peter.sex) # 子类继承父类方法及属性
May.print_title()
Peter.print_title()
而我们编写 Child 类,完全可以继承 Person 类(Child 就是 Person);使用 class subclass_name(baseclass_name) 来表示继承;
继承有什么好处?最大的好处是子类获得了父类的全部属性及功能。如下 Child 类就可以直接使用父类的 print_title() 方法
实例化Child的时候,子类继承了父类的构造函数,就需要提供父类Person要求的两个属性变量 name 及 sex:
在继承关系中,如果一个实例的数据类型是某个子类,那它也可以被看做是父类(May 既是 Child 又是 Person)。但是,反过来就不行(Peter 仅是 Person,而不是Child)。
继承还可以一级一级地继承下来,就好比从爷爷到爸爸、再到儿子这样的关系。而任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。比如如下的继承树:
isinstance() 及 issubclass()
Python 与其他语言不同点在于,当我们定义一个 class 的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和Python自带的数据类型,比如str、list、dict没什么两样。
Python 有两个判断继承的函数:isinstance() 用于检查实例类型;issubclass() 用于检查类继承。参见下方示例:
class Person(object):
pass
class Child(Person): # Child 继承 Person
pass
May = Child()
Peter = Person()
print(isinstance(May,Child)) # True
print(isinstance(May,Person)) # True
print(isinstance(Peter,Child)) # False
print(isinstance(Peter,Person)) # True
print(issubclass(Child,Person)) # True
Python 类的多态
在说明多态是什么之前,我们在 Child 类中重写 print_title() 方法:若为male,print boy;若为female,print girl
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person): # Child 继承 Person
def print_title(self):
if self.sex == "male":
print("boy")
elif self.sex == "female":
print("girl")
May = Child("May","female")
Peter = Person("Peter","male")
print(May.name,May.sex,Peter.name,Peter.sex)
May.print_title()
Peter.print_title()
当子类和父类都存在相同的 print_title()方法时,子类的 print_title() 覆盖了父类的 print_title(),在代码运行时,会调用子类的 print_title()
这样,我们就获得了继承的另一个好处:多态。
多态的好处就是,当我们需要传入更多的子类,例如新增 Teenagers、Grownups 等时,我们只需要继承 Person 类型就可以了,而print_title()方法既可以直不重写(即使用Person的),也可以重写一个特有的。这就是多态的意思。调用方只管调用,不管细节,而当我们新增一种Person的子类时,只要确保新方法编写正确,而不用管原来的代码。这就是著名的“开闭”原则:
对扩展开放(Open for extension):允许子类重写方法函数
对修改封闭(Closed for modification):不重写,直接继承父类方法函数
子类重写构造函数
子类可以没有构造函数,表示同父类构造一致;子类也可重写构造函数;现在,我们需要在子类 Child 中新增两个属性变量:mother 和 father,我们可以构造如下(建议子类调用父类的构造方法,参见后续代码):
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
class Child(Person): # Child 继承 Person
def __init__(self,name,sex,mother,father):
self.name = name
self.sex = sex
self.mother = mother
self.father = father
May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)
若父类构造函数包含很多属性,子类仅需新增1、2个,会有不少冗余的代码,这边,子类可对父类的构造方法进行调用,参考如下:
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
class Child(Person): # Child 继承 Person
def __init__(self,name,sex,mother,father):
Person.__init__(self,name,sex) # 子类对父类的构造方法的调用
self.mother = mother
self.father = father
May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)
多重继承
多重继承的概念应该比较好理解,比如现在需要新建一个类 baby 继承 Child , 可继承父类及父类上层类的属性及方法,优先使用层类近的方法,代码参考如下:
class Person(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def print_title(self):
if self.sex == "male":
print("man")
elif self.sex == "female":
print("woman")
class Child(Person):
pass
class Baby(Child):
pass
May = Baby("May","female") # 继承上上层父类的属性
print(May.name,May.sex)
May.print_title() # 可使用上上层父类的方法
class Child(Person):
def print_title(self):
if self.sex == "male":
print("boy")
elif self.sex == "female":
print("girl")
class Baby(Child):
pass
May = Baby("May","female")
May.print_title() # 优先使用上层类的方法
python的继承实际上是一个指针或者是引用的链表。它是动态语言。
面向对象有多态、继承、封装几个主要概念。要形成面向对象还需要分层,抽象,动静态建模等技能。
继承通常是指类的继承,派生类从基类里继承它的属性与方法。当然也可以重载基类的方法,或者是重写方法。python里虽然可以重写方法,不过仍然可以通过super等函数修饰获得基类方法。
基类又称父类。派生类也称为子类。
属性就是对象中的变量。方法就是对象中的函数。
类是一种定义。如果类有了实例,就称为对象。类是对象的静态结构的描述,是声明。
继承是子类复用父类的属性和方法的机制,类的继承是以生活中继承为灵感设计的。
生活中继承的例子有很多,例如,汽车的发展历程就体现了继承。最早汽车的轮胎都是实心的,功能少,性能差,而如今汽车的功能越来越多,性能越来越好,这是经过一代一代的发展而来的,每一代比上一代扩充了一些功能,改进了一些性能。纵观整个汽车的发展史,每一代汽车都是在上一代汽车的特征和行为的基础之上,进行了设计和改良。这其中有些功能没有改变,有些功能是新增的,也有些功能经过了改良。例如,从第一代的汽车到现在的汽车,做交通工具这一特点始终都在沿用。
生活中汽车的例子,与计算机中的继承是非常相似的。这么一代又一代的更新是有好处的。
首先,节省了设计流程,不用闭门造车。
其次,在前一代汽车基础之上设计,这样原来重复的生产技术还可以复用,再设计一些新增的功能,这样就能大大地提高生产效率。
下面介绍一个实际案例,来说明继承的用处。
假设设计一个师生管理系统,具有学生和教师两种用户,需要设计两个类,一个学生类(Student),一个教师类(Teacher)。Student类有学号、姓名、性别、年龄、用户名、密码等属性,Teacher类有姓名、性别、年龄、用户名、密码、学历等属性,这两个类中都有属性的getter和setter方法。由此可知,这两个类中有许多相同的属性和方法,也就是说代码有冗余。为了避免这样的情况,就可以使用继承来优化设计。将Student类和Teacher类中相同的属性和方法抽取出来,单独作为一个父类,这个父类称为用户类(User),而Student类和Teacher类作为子类继承父类User。Student类和Teacher类中就只放自己特有的属性和方法即可。由于这两个类都继承User类,因此User类中的属性和方法,它们可以直接使用。需要指出的是,在继承中私有属性和私有方法是不能被继承的。
继承提高了代码的重用性,减少了代码和数据的冗余度。另外,如果要修改用户名属性,不采用继承的情况下,两个类的属性都需要修改,而使用继承后,只需要修改父类的用户名属性即可。由此可知,继承使代码的修改更加方便。
关于Python的基础问题可以看下这个网页的视频教程,网页链接,希望我的回答能帮到你。
9.5. 继承
当然,如果一种语言不支持继承就,“类”就没有什么意义。派生类的定义如下所示:
class DerivedClassName(BaseClassName):
命名 BaseClassName (示例中的基类名)必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模块中时这一点非常有用:
class DerivedClassName(modname.BaseClassName):
派生类定义的执行过程和基类是一样的。构造派生类对象时,就记住了基类。这在解析属性引用的时候尤其有用:如果在类中找不到请求调用的属性,就搜索基类。如果基类是由别的类派生而来,这个规则会递归的应用上去。
派生类的实例化没有什么特殊之处: DerivedClassName() (示列中的派生类)创建一个新的类实例。方法引用按如下规则解析:搜索对应的类属性,必要时沿基类链逐级搜索,如果找到了函数对象这个方法引用就是合法的。
派生类可能会覆盖其基类的方法。因为方法调用同一个对象中的其它方法时没有特权,基类的方法调用同一个基类的方法时,可能实际上最终调用了派生类中的覆盖方法。(对于 C++ 程序员来说,Python 中的所有方法本质上都是 虚 方法。)
派生类中的覆盖方法可能是想要扩充而不是简单的替代基类中的重名方法。有一个简单的方法可以直接调用基类方法,只要调用: BaseClassName.methodname(self, arguments)。有时这对于客户也很有用。(要注意只有 BaseClassName 在同一全局作用域定义或导入时才能这样用。)
Python 有两个用于继承的函数:
函数 isinstance() 用于检查实例类型: isinstance(obj, int) 只有在 obj.__class__ 是 int 或其它从 int 继承的类型
函数 issubclass() 用于检查类继承: issubclass(bool, int) 为 True,因为 bool 是 int 的子类。
然而, issubclass(float, int) 为 False,因为 float 不是 int 的子类。
类(Class): 用来描述具有相同的属性和方法的对象的集合。
类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
类有一个名为 __init__() 的特殊方法(构造方法),该方法在类实例化时会自动调用
self:self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。
类调用 Car.weight
实例化 car01=Car(5)
实例对象调用 car01.weght
我们在构造类时,Python3默认我们继承了object这个基类,我个人理解object就是个空的类,可以不用管为何要在括号中写上object,这是Python3的特性,在python2中如果你没有写object的话不会默认继承了object这个基类。
同样的我们自己希望继承的父类只需要把objetc改为我们自己定义的类名即可。子类中可以拥有父类中所有的公有属性和方法,但是可以通过在变量名前加下划线使其变为私有,这样子类就不可以访问父类中的成员了。
以下三个公交车类的父类均为客车类,我们可以写一个funcs方法使得每次调用funcs方法时,传入不同的对象以执行不同的func方法,具体实现如下:
主函数 :
可以看到,我将小 汽车 实例化为带有重量为5t的一个具体对象,将客车实例化为带有重量为20t的一个具体对象,将三个公交车实例化为带有重量为15t的一个具体对象.
如上图所示,我每次在调用funcs方法时都传入了一个实例化对象,funcs根据不同的对象执行相应的内部方法。
如果要编写的类是另一个类的特殊版本时,那么就可以使用继承 。原有的类称为父类 , 新类称为子类 。 子类继承了父类的所有属性和方法, 同时子类还可以自定义自己的属性和方法。
定义子类的实例时, 可以通过 子类的 __init__() 方法,给父类的所有属性赋值。
假设有这样的一个 User 类:
接着,我们定义一个 Admin 类,让它继承 User 类:
运行结果:
super() 是一个特殊函数, 它会把父类和子类关联起来。因为父类也称为超类 ( superclass),所以这个函数叫做 super。接着调用父类的 __init__() 方法, 让子类包含父类的所有属性。
子类除了拥有继承父类而来的属性和方法之外,还可以自定义子类自己的属性和方法。
一般情况下,管理员账号比普通账号,拥有更高级别的权限。因此,我们为 Admin 定义一个有别于 User 的 privileges 属性,并定义一个 “打印拥有的权限” 的新方法:
运行结果:
对于继承而来的父类方法, 如果它不符合子类所期望的行为,那么就可以对其重写。 只要在子类中定义一个与父类同名的方法,即可实现重写。
User 本身定义了一个 “是否验证通过” 的方法,Admin 是管理员,所以需要在打印日志中特别标注出来,这时就需要重写父类定义的方法:
运行结果:
通过重写父类方法, 我们就可以让子类即可以保留或改写从父类取其精华,弃其“糟粕”啦。
当实体越来越复杂,对应的类变得越来越大时, 我们可以将这个大型类拆分成多个可协同工作的小类。
比如,账号权限,其实即使是普通账号也是有某些权限的。所以我们把权限定义为一个类,然后在 Admin 中使用它: