Lesson 11

  • Python Begginer, Level 2, Lesson 10, v1.0.0, 2016.11 by David.Yi
  • v1.1. 2020.5.4 edit by David Yi

本次内容要点

  • 面向对象的编程之二
    • 创建和使用子类
    • 构造器方法和解构器方法
    • 调用父类的 super() 方法

In [6]:
# 我们建立一个有趣的简单的模仿游戏的类来说明面向对象编程的概念
# v1.0.0

# NPC 类
class NPC(object):
    
    # 初始化 NPC 的属性
    def __init__(self, name):
        self.name = name
        self.weapon = 'gun'
        self.blood = 1000
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        print('name:', self.name)
        print('weapon:', self.weapon)
        print('blood:', self.blood)
    
    # 定义方法 - 通用攻击
    def fight_common(self):
        print('Fight Common')
    
n1 = NPC('AA1')
n1.show_properties()
n1.fight_common()


name: AA1
weapon: gun
blood: 1000
Fight Common

这个 NPC 的类,在初始化这里定义了 NPC 拥有的三个属性,name、weapon、blood,其中 name 需要创建实例的时候设置。

NPC 类有两个方法,一个是 show_properties(),显示 NPC 的属性;另外一个是 fight_common(),我们称之为普通攻击。

创建和使用子类

通过 class B(A),即表示从 A 开始创建一个子类,我们称之为继承。

面向对象的编程方式的有点已经可以体现,我们可以通过继承来构造一个对象体系,比如这里现在 NPC 这个类中,定义了游戏中人物的最基本的一些属性和方法,可以理解不光 NPC 是战士,还是巫师,对会有这些属性和方法;然后我们把各自独特的属性和方法定义在从 NPC 中继承的各个子类中。我们先来定义一个战士 Soldier 类。


In [20]:
# 战士 Soldier 类,继承自 NPC class
class Soldier(NPC):
    # 暂时什么也不干
    pass
        
# 创建一个 Soldier 类, 作为 NPC 的子类
n1 = Soldier('AA2')

# 调用方法,因为 Soldier 中此刻并没有任何实际的方法等,所以实际上自动调用了父类的方法
n1.show_properties()
n1.fight_common()


name: AA2
weapon: gun
blood: 1000
Fight Common

在子类中,可以覆盖父类的方法。

比如 __init__() ,我们需要在 Soldier 类的初始化中加入一个只有 soldier 才有的级别 level 属性。 所以,我们先调用父类的__init__() 方法,再写 Soldier 类需要的代码。

show_properties()方法,我们先用简单的办法,完全覆盖,也就是使用这个新的show_properties()方法,来显示 NPC 标准的三个属性以及 Soldier 的一个独立属性。


In [7]:
# 战士 Soldier 类
class Soldier(NPC):
    
    # 建立 soldier 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.soldier_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        print('name:', self.name)
        print('weapon:', self.weapon)
        print('blood:', self.blood)
        print('soldier_level:', self.soldier_level)

# 创建一个 Soldier 类, 作为 NPC 的子类
n1 = Soldier('AA2')
n1.show_properties()
n1.fight_common()


name: AA2
weapon: gun
blood: 1000
soldier_level: 1
Fight Common

再来看看 show_properties() 这个方法,总感觉这样有点笨重,因为不管是否是面向对象的编程方式,都不应该有太多重复的代码,现在 NPC 类中也有 show_properties() 方法,用来显示标准的三个属性,而 Soldier 类中同样名字的方法显示四个属性,如果 NPC 类中增加了属性,那么两边类中的这个方法都要修改。

我们来看看有没有更好的办法,可以用 super(需要知道父类的类,self) 的方法来访问父类的方法,这样代码就简洁优雅了。


In [28]:
# 战士 Soldier 类
class Soldier(NPC):
    
    # 建立 soldier 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.soldier_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Soldier, self).show_properties()
        print('soldier_level', self.soldier_level)

# 创建一个 Soldier 类, 作为 NPC 的子类
n1 = Soldier('AA2')
n1.show_properties()
n1.fight_common()


name: AA2
weapon: gun
blood: 1000
soldier_level 1
Fight Common

init() 构造器方法和 del() 解构器方法

可以理解构造就是创建,解构就是销毁。每一个对象的实例都是有声明周期的,从构造到解构。

类别调用的时候,对象被创建的时候,python 先检查是否实现了 init() 方法,如果没有,则什么也不干。如果有 init()方法,则执行。

python 作为高级语言,具有垃圾对象回收机制,当 python 发现对于该实例对象的引用都被清除掉后,会执行 del() 方法。通常不需要自己去实现这个 del() 方法,python 会做好这一切。


In [1]:
# 先整理一下上面的代码
# v1.0.1

# NPC 类
class NPC(object):
    
    # 初始化 NPC 的属性
    def __init__(self, name):
        self.name = name
        self.weapon = 'gun'
        self.blood = 1000
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        print('name:', self.name)
        print('weapon:', self.weapon)
        print('blood:', self.blood)
    
    # 定义方法 - 通用攻击
    def fight_common(self):
        print('Fight Common')
        
# 战士 Soldier 类
class Soldier(NPC):
    
    # 建立 soldier 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.soldier_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Soldier, self).show_properties()
        print('soldier_level', self.soldier_level)

In [6]:
# 创建两个 soldier

n1 = Soldier('AA1')
n1.show_properties()
n1.fight_common()  

print()

n2 = Soldier('AA2')
n2.show_properties()
n2.fight_common()


name: AA1
weapon: gun
blood: 1000
soldier_level 1
Fight Common

name: AA2
weapon: gun
blood: 1000
soldier_level 1
Fight Common

In [9]:
# 连续创建多个 soldier 的实例
# 并存储在 list 中

s = []

for i in range(3):
    n = Soldier('AA' + str(i))
    n.show_properties()
    n.fight_common() 
    s.append(n)


name: AA0
weapon: gun
blood: 1000
soldier_level: 1
Fight Common
name: AA1
weapon: gun
blood: 1000
soldier_level: 1
Fight Common
name: AA2
weapon: gun
blood: 1000
soldier_level: 1
Fight Common

In [12]:
# 看一下存储了对象的列表

print(s)


[<__main__.Soldier object at 0x1063f2a58>, <__main__.Soldier object at 0x1063d7e48>, <__main__.Soldier object at 0x1063d7240>]

In [7]:
# 可以和一般访问列表一样访问列表中的对象

for i in s:
    print(i.name)
    
print(len(s))


AA0
AA1
AA2
3

In [8]:
# 可以删除一个实例
s.pop(1)

# 显示列表中的实例
for i in s:
    print(i.name)
    
print(len(s))


AA0
AA2
2

In [10]:
# 再增加一个巫师 Wizard 的类

# 巫师 Wizard 类
class Wizard(NPC):
    
    # 建立 Wizard 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.wizard_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Wizard, self).show_properties()
        print('wizard_level', self.wizard_level)
    
    # 定义一个巫师专用的战斗方法
    def wizard_fight_magic(self):
        print('Wizard Magic!')
        
# 创建一个 wizard

c1 = Wizard('CC1')
c1.show_properties()
c1.fight_common()  
c1.wizard_fight_magic()


name: CC1
weapon: gun
blood: 1000
wizard_level 1
Fight Common
Wizard Magic!

In [14]:
# 创建复杂的 NPC,3个 wizards,3个 soldiers

# 创建多个 soldier 的实例
s = []

for i in range(3):
    n = Soldier('AA' + str(i))
    n.show_properties()
    s.append(n)
    
for i in range(3):
    n = Wizard('CC' + str(i))
    n.show_properties()
    s.append(n)

for i in s:
    print(i.name)
    print('--')


name: AA0
weapon: gun
blood: 1000
soldier_level 1
name: AA1
weapon: gun
blood: 1000
soldier_level 1
name: AA2
weapon: gun
blood: 1000
soldier_level 1
name: CC0
weapon: gun
blood: 1000
wizard_level 1
name: CC1
weapon: gun
blood: 1000
wizard_level 1
name: CC2
weapon: gun
blood: 1000
wizard_level 1
AA0
--
AA1
--
AA2
--
CC0
--
CC1
--
CC2
--

In [12]:
# 显示类的方法

print(dir(Soldier))


['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'fight_common', 'show_properties']

In [13]:
# 显示类的方法

print(dir(Wizard))


['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'fight_common', 'show_properties', 'wizard_fight_magic']

In [10]:
# 当前版本的代码
# v1.0.2

# NPC 类
class NPC(object):
    
    # 初始化 NPC 的属性
    def __init__(self, name):
        self.name = name
        self.weapon = 'gun'
        self.blood = 1000
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        print('name:', self.name)
        print('weapon:', self.weapon)
        print('blood:', self.blood)
    
    # 定义方法 - 通用攻击
    def fight_common(self):
        print('Fight Common')
        
# 战士 Soldier 类
class Soldier(NPC):
    
    # 建立 soldier 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.soldier_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Soldier, self).show_properties()
        print('soldier_level', self.soldier_level)
        
# 巫师 Wizard 类
class Wizard(NPC):
    
    # 建立 Wizard 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.wizard_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Wizard, self).show_properties()
        print('wizard_level', self.wizard_level)
    
    # 定义一个巫师专用的战斗方法
    def fight_magic(self):
        print('Wizard Magic!')

In [18]:
# 在 NPC 的 __init__() 加入显示创建的是什么角色
# v1.0.3

# NPC 类
class NPC(object):
    
    # 初始化 NPC 的属性
    def __init__(self, name):
        self.name = name
        self.weapon = 'gun'
        self.blood = 1000
        # 先简单的显示
        print('')
        print('NPC created!')
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        print('name:', self.name)
        print('weapon:', self.weapon)
        print('blood:', self.blood)
    
    # 定义方法 - 通用攻击
    def fight_common(self):
        print('Fight Common')
        
# 战士 Soldier 类
class Soldier(NPC):
    
    # 建立 soldier 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.soldier_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Soldier, self).show_properties()
        print('soldier_level', self.soldier_level)
        
# 巫师 Wizard 类
class Wizard(NPC):
    
    # 建立 Wizard 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.wizard_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Wizard, self).show_properties()
        print('wizard_level', self.wizard_level)
    
    # 定义一个巫师专用的战斗方法
    def fight_magic(self):
        print('Wizard Magic!')

s = []

for i in range(2):
    n = Soldier('AA' + str(i))
    n.show_properties()
    s.append(n)
    
for i in range(2):
    n = Wizard('CC' + str(i))
    n.show_properties()
    s.append(n)


NPC created!
name: AA0
weapon: gun
blood: 1000
soldier_level 1

NPC created!
name: AA1
weapon: gun
blood: 1000
soldier_level 1

NPC created!
name: CC0
weapon: gun
blood: 1000
wizard_level 1

NPC created!
name: CC1
weapon: gun
blood: 1000
wizard_level 1

In [27]:
# 但是在 NPC 这个父类中没有显示出具体的子类名称
# 所以我们用下面的方法来显示子类的名称
# type(self).__name__ 来访问类的名称
# v1.0.4

# NPC 类
class NPC(object):
    
    # 初始化 NPC 的属性
    def __init__(self, name):
        self.name = name
        self.weapon = 'gun'
        self.blood = 1000
        print('')
        print(type(self).__name__, 'NPC created!')
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        print('name:', self.name)
        print('weapon:', self.weapon)
        print('blood:', self.blood)
    
    # 定义方法 - 通用攻击
    def fight_common(self):
        print('Fight Common')
        
# 战士 Soldier 类
class Soldier(NPC):
    
    # 建立 soldier 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.soldier_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Soldier, self).show_properties()
        print('soldier_level', self.soldier_level)
        
# 巫师 Wizard 类
class Wizard(NPC):
    
    # 建立 Wizard 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.wizard_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Wizard, self).show_properties()
        print('wizard_level', self.wizard_level)
    
    # 定义一个巫师专用的战斗方法
    def fight_magic(self):
        print('Wizard Magic!')

s = []

for i in range(2):
    n = Soldier('AA' + str(i))
    n.show_properties()
    s.append(n)
    
for i in range(2):
    n = Wizard('CC' + str(i))
    n.show_properties()
    s.append(n)


Soldier NPC created!
name: AA0
weapon: gun
blood: 1000
soldier_level 1

Soldier NPC created!
name: AA1
weapon: gun
blood: 1000
soldier_level 1

Wizard NPC created!
name: CC0
weapon: gun
blood: 1000
wizard_level 1

Wizard NPC created!
name: CC1
weapon: gun
blood: 1000
wizard_level 1

面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的程序编程范型,同时也是一种程序开发的方法。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。

面向对象程序设计可以看作一种在程序中包含各种独立而又互相调用的对象的思想,这与传统的思想刚好相反:传统的程序设计主张将程序看作一系列函数的集合,或者直接就是一系列对电脑下达的指令。面向对象程序设计中的每一个对象都应该能够接受数据、处理数据并将数据传达给其它对象,因此它们都可以被看作一个小型的“机器”,即对象。目前已经被证实的是,面向对象程序设计推广了程序的灵活性和可维护性,并且在大型项目设计中广为应用。此外,支持者声称面向对象程序设计要比以往的做法更加便于学习,因为它能够让人们更简单地设计并维护程序,使得程序更加便于分析、设计、理解。反对者在某些领域对此予以否认。


In [4]:
# 继续优化,根据 NPC 类型来设定 blood 和 weapon
# 将代码尽量集中,是有好处的
# v1.0.5

# NPC 类
class NPC(object):
    
    # 初始化 NPC 的属性
    def __init__(self, name):
        self.name = name
        
        self.npc_type = type(self).__name__
        
        print('')
        print(self.npc_type, 'NPC created!')
        
        if self.npc_type == 'Soldier':
            self.weapon = 'sword'
            self.blood = 1000
            
        if self.npc_type == 'Wizard':
            self.weapon = 'staff'
            self.blood = 2000
        
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        print('name:', self.name)
        print('weapon:', self.weapon)
        print('blood:', self.blood)
    
    # 定义方法 - 通用攻击
    def fight_common(self):
        print('Fight Common')
        
# 战士 Soldier 类
class Soldier(NPC):
    
    # 建立 soldier 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.soldier_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Soldier, self).show_properties()
        print('soldier_level', self.soldier_level)
        
# 巫师 Wizard 类
class Wizard(NPC):
    
    # 建立 Wizard 的初始化
    def __init__(self, name):
        # 调用 父类 NPC 的初始化方法
        NPC.__init__(self, name)
        
        # soldier 自己的初始化
        self.wizard_level = 1
        
    # 定义方法 - 显示 NPC 属性
    def show_properties(self):
        # 通过 super 来调用父类方法
        super(Wizard, self).show_properties()
        print('wizard_level', self.wizard_level)
    
    # 定义一个巫师专用的战斗方法
    def fight_magic(self):
        print('Wizard Magic!')

s = []

for i in range(2):
    n = Soldier('AA' + str(i))
    n.show_properties()
    s.append(n)
    
for i in range(2):
    n = Wizard('CC' + str(i))
    n.show_properties()
    s.append(n)


Soldier NPC created!
name: AA0
weapon: sword
blood: 1000
soldier_level 1

Soldier NPC created!
name: AA1
weapon: sword
blood: 1000
soldier_level 1

Wizard NPC created!
name: CC0
weapon: staff
blood: 2000
wizard_level 1

Wizard NPC created!
name: CC1
weapon: staff
blood: 2000
wizard_level 1