编写类时,并非总是要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。

1.子类的方法__init__()

创建子类的实例时,Python首先需要完成的任务是给父类的所有属性赋值。为此,子类的方法__init__()需要父类施以援手。
例如,下面来模拟电动汽车。电动汽车是一种特殊的汽车,因此我们可以在前面创建的Car类的基础上创建新类ElectricCar,这样我们就只需为电动汽车特有的属性和行为编写代码。下面来创建一个简单的ElectricCar类版本,它具备Car类的所有功能:

class car():
"""docstring for car"""
def __init__(self, make, modle, year):
self.make = make
self.modle = modle
self.year = year
self.odometer_reading = 0 def get_descriptive_name(self):
long_name = str(self.year) + " " +self.make + " " + self.modle
return long_name.title() def read_odometer(self):
"""打印一条指出汽车历程额消息"""
print("This car has " + str(self.odometer_reading) + "miles on it.") def update_odometer(self,mileage):
"""将里程表读数设置为指定的值"""
self.odometer_reading = mileage def reset_odometer_reading(self):
self.odometer_reading = 0 def add_odometer_reading(self):
'''里程加1'''
self.odometer_reading += 1 class ElectricCar(car):
"""docstring for ElectricCar"""
def __init__(self, make, modle, year):
'''初始化父类的属性'''
super().__init__(make, modle, year) my_tesla = ElectricCar("tesla", "modles", 2017)
print(my_tesla.get_descriptive_name())
my_tesla.read_odometer() >>>2017 Tesla Modles
This car has 0miles on it.

首先是Car类的代码。创建子类时,父类必须包含在当前文件中,且位于子类前面。我们定义了子类ElectricCar。定义子类时,必须在括号内指定父类的名称。方法__init__()接受创建Car实例所需的信息。

super()是一个特殊函数,帮助Python将父类和子类关联起来。这行代码让Python调用ElectricCar的父类的方法__init__(),让ElectricCar实例包含父类的所有属性。父类也称为超类(superclass),名称super因此而得名。
为测试继承是否能够正确地发挥作用,我们尝试创建一辆电动汽车,但提供的信息与创建普通汽车时相同。我们创建ElectricCar类的一个实例,并将其存储在变量my_tesla中。这行代码调用ElectricCar类中定义的方法__init__(),后者让Python调用父类Car中定义的方法__init__()。我们提供了实参'tesla'、'model s'和2017。

除方法__init__()外,电动汽车没有其他特有的属性和方法。

2.Python 2.7 中的继承

在Python 2.7中,继承语法稍有不同,ElectricCar类的定义类似于下面这样:

class Car(object):
  def __init__(self, make, model, year):
    --snip-- class ElectricCar(Car):
  def __init__(self, make, model, year):
    super(ElectricCar, self).__init__(make, model, year)
    --snip--

函数super()需要两个实参:子类名和对象self。为帮助Python将父类和子类关联起来,这些实参必不可少。另外,在Python 2.7中使用继承时,务必在定义父类时在括号内指定object。

3.给子类定义属性和方法

让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法。
下面来添加一个电动汽车特有的属性(电瓶),以及一个描述该属性的方法。我们将存储电瓶容量,并编写一个打印电瓶描述的方法:

class car():
--snip-- class ElectricCar(car):
"""docstring for ElectricCar"""
def __init__(self, make, modle, year):
'''初始化父类的属性'''
super().__init__(make, modle, year)
self.battery_size = 70 def describe_battery(self):
'''打印一条描述电瓶容量的消息'''
print("This car has a " + str(self.battery_size) + "-kwh battery.") my_tesla = ElectricCar("tesla", "modles", 2017)
print(my_tesla.get_descriptive_name())
my_tesla.read_odometer()
my_tesla.describe_battery() >>>2017 Tesla Modles
This car has 0miles on it.
This car has a 70-kwh battery.

我们添加了新属性self.battery_size,并设置其初始值(如70)。根据ElectricCar 类创建的所有实例都将包含这个属性,但所有Car实例都不包含它。我们还添加了一个名为describe_battery()的方法,它打印有关电瓶的信息。我们调用这个方法时,将看到一条电动汽车特有的描述。

对于ElectricCar类的特殊化程度没有任何限制。模拟电动汽车时,你可以根据所需的准确程度添加任意数量的属性和方法。如果一个属性或方法是任何汽车都有的,而不是电动汽车特有的,就应将其加入到Car类而不是ElectricCar类中。这样,使用Car类的人将获得相应的功能,而ElectricCar类只包含处理电动汽车特有属性和行为的代码。

4.重写父类的方法

对于父类的方法,只要它不符合子类模拟的实物的行为,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。
假设Car类有一个名为fill_gas_tank()的方法,它对全电动汽车来说毫无意义,因此你可能想重写它。下面演示了一种重写方式:

def ElectricCar(Car):
--snip--
def fill_gas_tank():
"""电动汽车没有油箱"""
print("This car doesn't need a gas tank!")

现在,如果有人对电动汽车调用方法fill_gas_tank(), Python将忽略Car类中的方法fill_gas_tank(),转而运行上述代码。使用继承时,可让子类保留从父类那里继承而来的精华,并剔除不需要的糟粕。

5.将实例用作属性

使用代码模拟实物时,你可能会发现自己给类添加的细节越来越多:属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分作为一个独立的类提取出来。你可以将大型类拆分成多个协同工作的小类。
例如,不断给ElectricCar类添加细节时,我们可能会发现其中包含很多专门针对汽车电瓶的属性和方法。在这种情况下,我们可将这些属性和方法提取出来,放到另一个名为Battery的类中,并将一个Battery实例用作ElectricCar类的一个属性:

class Car():                   #
--snip-- class Battery(): #
"""一次模拟电动汽车电瓶的简单尝试"""
def __init__(self, battery_size=70):
"""初始化电瓶的属性"""
self.battery_size = battery_size
def describe_battery(self): #
"""打印一条描述电瓶容量的消息"""
print("This car has a " + str(self.battery_size) + "-kWh battery.")
class ElectricCar(Car):
"""电动汽车的独特之处"""
def __init__(self, make, model, year):
"""
初始化父类的属性,再初始化电动汽车特有的属性
"""
super().__init__(make, model, year)
self.battery = Battery() # my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()

在1处,我们定义了一个名为Battery的新类,它没有继承任何类。2处的方法__init__()除self外,还有另一个形参battery_size。这个形参是可选的:如果没有给它提供值,电瓶容量将被设置为70。方法describe_battery()也移到了这个类中(见3)。
在ElectricCar类中,我们添加了一个名为self.battery的属性(见4)。这行代码让Python创建一个新的Battery实例(由于没有指定尺寸,因此为默认值70),并将该实例存储在属性self.battery中。每当方法__init__()被调用时,都将执行该操作;因此现在每个ElectricCar实例都包含一个自动创建的Battery实例。
我们创建一辆电动汽车,并将其存储在变量my_tesla中。要描述电瓶时,需要使用电动汽车的属性battery:

my_tesla.battery.describe_battery()

这行代码让Python在实例my_tesla中查找属性battery,并对存储在该属性中的Battery实例调用方法describe_battery().

这看似做了很多额外的工作,但现在我们想多详细地描述电瓶都可以,且不会导致ElectricCar类混乱不堪。下面再给Battery类添加一个方法,它根据电瓶容量报告汽车的续航里程:

class Car():
--snip--
class Battery():
--snip--
def get_range(self):
"""打印一条消息,指出电瓶的续航里程"""
if self.battery_size == 70:
range = 240
elif self.battery_size == 85:
range = 270
message = "This car can go approximately " + str(range)
message += " miles on a full charge."
print(message)
class ElectricCar(Car):
--snip-- my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range() >>>2016 Tesla Model S
This car has a 70-kWh battery.
This car can go approximately 240 miles on a full charge.

处新增的方法get_range()做了一些简单的分析:如果电瓶的容量为70kWh,它就将续航里程设置为240英里;如果容量为85kWh,就将续航里程设置为270英里,然后报告这个值。为使用
这个方法,我们也通过汽车的属性battery来调用它.

6.模拟实物

模拟较复杂的物件(如电动汽车)时,需要解决一些有趣的问题。续航里程是电瓶的属性还是汽车的属性呢?如果我们只需描述一辆汽车,那么将方法get_range()放在Battery类中也许是合适的;但如果要描述一家汽车制造商的整个产品线,也许应该将方法get_range()移到ElectricCar类中。在这种情况下,get_range()依然根据电瓶容量来确定续航里程,但报告的是一款汽车的续航里程。我们也可以这样做:将方法get_range()还留在Battery类中,但向它传递一个参数,如car_model;在这种情况下,方法get_range()将根据电瓶容量和汽车型号报告续航里程。
这让你进入了程序员的另一个境界:解决上述问题时,你从较高的逻辑层面(而不是语法层面)考虑;你考虑的不是Python,而是如何使用代码来表示实物。到达这种境界后,你经常会发现,现实世界的建模方法并没有对错之分。有些方法的效率更高,但要找出效率最高的表示法,需要经过一定的实践。只要代码像你希望的那样运行,就说明你做得很好!即便你发现自己不得不多次尝试使用不同的方法来重写类,也不必气馁;要编写出高效、准确的代码,都得经过这样的过程。

python基础===继承的更多相关文章

  1. python基础——继承和多态

    python基础——继承和多态 在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类.父类或超类 ...

  2. python基础——继承实现的原理

    python基础--继承实现的原理 1 继承顺序 class A(object): def test(self): print('from A') class B(A): def test(self) ...

  3. python基础——继承与派生、组合

    python基础--继承与派生 1 什么是继承: 继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类成为基类或超累,新建的类成为派生类或子类 1.1 继承分为:单 ...

  4. python基础----继承与派生、组合、接口与归一化设计、抽象类、子类中调用父类方法

    一.什么是继承                                                                          继承是一种创建新的类的方式,在pyth ...

  5. Python基础-继承与派生

    一.继承 继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类称为基类或超类,新建的类称为派生类或子类. python中类的继承分为:单继承和多继承 class P ...

  6. python基础===继承和多继承

    继承 class A: def spam(self): print("A.SPAM") def add(self, x,y): return x+y class B(A): def ...

  7. 二十二. Python基础(22)--继承

    二十二. Python基础(22)--继承 ● 知识框架   ● 继承关系中self的指向 当一个对象调用一个方法时,这个方法的self形参会指向这个对象 class A:     def get(s ...

  8. python基础之面向对象的多继承以及MRO算法

    内容梗概: 1. python多继承 2. python经典类的MRO 3. python新式类的MRO C3算法 1.python多继承 class Shen: def fly(self): pri ...

  9. Py修行路 python基础 (十五)面向对象编程 继承 组合 接口和抽象类

    一.前提回忆: 1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均 ...

随机推荐

  1. Java InputStream转File

    文件处于磁盘上或者流处于内存中 在输入流有已知的和预处理的数据时,如在硬盘上的文件或者在流处于内存中.这种情况下,不需要做边界校验,并且内存容量条件允许的话,可以简单的读取并一次写入. InputSt ...

  2. 【Python】Python简易爬虫爬取百度贴吧图片

    通过python 来实现这样一个简单的爬虫功能,把我们想要的图片爬取到本地.(Python版本为3.6.0) 一.获取整个页面数据 def getHtml(url): page=urllib.requ ...

  3. 【bzoj4491】我也不知道题目名字是什么 离线扫描线+线段树

    题目描述 给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串 输入 第一行n,表示A数组有多少元素接下来一行为n个整数A[i]接下来一个整数Q,表示询问数 ...

  4. Linux特殊字符含义

    文件名以 ' . ' 开头的都是隐藏文件或目录,只需要在文件或目录名前添加 ' . ' 就可以隐藏它 ~               表示主目录 .                当前目录 . .  ...

  5. Elasticsearch Query DSL备忘(1)(Constant score query和Bool Query)

    Query DSL (Domain Specific Language),基于json的查询方式 1.Constant score query,常量分值查询,目的就是返回指定的score,一般都结合f ...

  6. BZOJ3669:[NOI2014]魔法森林——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=3669 https://www.luogu.org/problemnew/show/P2387 为了得 ...

  7. HDU2222:Keywords Search——题解

    http://acm.hdu.edu.cn/showproblem.php?pid=2222 题目大意: 给定 n 个长度不超过 50 的由小写英文字母组成的单词,以及一篇长为 m 的文章,问有多少个 ...

  8. Static全局变量与普通的全局变量有什么区别?static函数与普通函数有什么区别?

    Static全局变量与普通的全局变量有什么区别? 答: 全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量.全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式. 这两者 ...

  9. CodeForces 185A. Plant (矩阵快速幂)

    CodeForces 185A. Plant (矩阵快速幂) 题意分析 求解N年后,向上的三角形和向下的三角形的个数分别是多少.如图所示: N=0时只有一个向上的三角形,N=1时有3个向上的三角形,1 ...

  10. poj:1850 Code(组合数学?数位dp!)

    题目大意:字符的字典序依次递增才是合法的字符串,将字符串依次标号如:a-1 b-2 ... z-26 ab-27 bc-52. 为什么题解都是组合数学的...我觉得数位dp很好写啊(逃 f[pos][ ...