<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Python - 面向对象编程 01
Python Senior, Lesson 10, v1.0.0, 2016.11 by David.Yi
v1.1, 2020.5.4, 6.13 edit by David Yi
本次内容要点
类与实例
类的属性
类的方法
创建和使用子类
构造器方法和解构器方法
调用父类的 super() 方法
面向对象编程
类与实例
类，是一个定义；实例是真正的对象的实现，创建一个实例的过程称作实例化。
所有的类都继承自一个叫做 object 的类。继承的定义之后再说。
类的一些操作方式和函数很像，在没有面向对象编程方式的时候，就是面向过程（函数）的开发方式。
类可以很复杂，也可以很简单，取决于应用的需要。面向对象的编程方式，是现代流行的开发方式，博大精深，需要慢慢理解体会。一开始有些不太清楚，也没有关系。
类的属性
类可以理解为一种称之为命名空间（ namespace），在这个类之下，数据都是属于这个类的实例的，我们称为属性，用实例名字+点+属性名字。这样的类比较简单，在 c 语言中称为结构体，在 pascal 中为记录类型，python 的数据结构比较简单。
Step1:  类的方法
光是把类作为命名空间太简单了，可以给类添加功能，称为方法。
方法定义在类中，使用在实例中。可以理解为实例是真正做事情的代码，所以在实例中调用方法完成某个功能是合理的。
Step2:  这个 NPC 的类，在初始化这里定义了 NPC 拥有的三个属性，name、weapon、blood，其中 name 需要创建实例的时候设置。
NPC 类有两个方法，一个是 show_properties()，显示 NPC 的属性；另外一个是 fight_common()，我们称之为普通攻击。
创建和使用子类
通过 class B(A)，即表示从 A 开始创建一个子类，我们称之为继承。
面向对象的编程方式的有点已经可以体现，我们可以通过继承来构造一个对象体系，比如这里现在 NPC 这个类中，定义了游戏中人物的最基本的一些属性和方法，可以理解不光 NPC 是战士，还是巫师，对会有这些属性和方法；然后我们把各自独特的属性和方法定义在从 NPC 中继承的各个子类中。我们先来定义一个战士 Soldier 类。
Step3:  在子类中，可以覆盖父类的方法。
比如 __init__() ，我们需要在 Soldier 类的初始化中加入一个只有 soldier 才有的级别 level 属性。
所以，我们先调用父类的__init__() 方法，再写 Soldier 类需要的代码。
而 show_properties()方法，我们先用简单的办法，完全覆盖，也就是使用这个新的show_properties()方法，来显示 NPC 标准的三个属性以及 Soldier 的一个独立属性。
Step4:  再来看看 show_properties() 这个方法，总感觉这样有点笨重，因为不管是否是面向对象的编程方式，都不应该有太多重复的代码，现在 NPC 类中也有 show_properties() 方法，用来显示标准的三个属性，而 Soldier 类中同样名字的方法显示四个属性，如果 NPC 类中增加了属性，那么两边类中的这个方法都要修改。
我们来看看有没有更好的办法，可以用 super(需要知道父类的类，self) 的方法来访问父类的方法，这样代码就简洁优雅了。
Step5:  init() 构造器方法和 del() 解构器方法
可以理解构造就是创建，解构就是销毁。每一个对象的实例都是有声明周期的，从构造到解构。
类别调用的时候，对象被创建的时候，python 先检查是否实现了 init() 方法，如果没有，则什么也不干。如果有 init()方法，则执行。
python 作为高级语言，具有垃圾对象回收机制，当 python 发现对于该实例对象的引用都被清除掉后，会执行 del() 方法。通常不需要自己去实现这个 del() 方法，python 会做好这一切。
Step6:  面向对象程序设计（英语：Object-oriented programming，缩写：OOP）是种具有对象概念的程序编程范型，同时也是一种程序开发的方法。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元，将程序和数据封装其中，以提高软件的重用性、灵活性和扩展性，对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里，计算机程序会被设计成彼此相关的对象。
面向对象程序设计可以看作一种在程序中包含各种独立而又互相调用的对象的思想，这与传统的思想刚好相反：传统的程序设计主张将程序看作一系列函数的集合，或者直接就是一系列对电脑下达的指令。面向对象程序设计中的每一个对象都应该能够接受数据、处理数据并将数据传达给其它对象，因此它们都可以被看作一个小型的“机器”，即对象。目前已经被证实的是，面向对象程序设计推广了程序的灵活性和可维护性，并且在大型项目设计中广为应用。此外，支持者声称面向对象程序设计要比以往的做法更加便于学习，因为它能够让人们更简单地设计并维护程序，使得程序更加便于分析、设计、理解。反对者在某些领域对此予以否认。
<ASSISTANT_TASK:>
Python Code:
# 申明一个 class MyData
class MyData(object):
    pass
# 实例化  MyData, 实例的名字叫做 obj_math
obj_math = MyData()
obj_math.x = 4
print(obj_math.x)
Explanation: Python - 面向对象编程 01
Python Senior, Lesson 10, v1.0.0, 2016.11 by David.Yi
v1.1, 2020.5.4, 6.13 edit by David Yi
本次内容要点
类与实例
类的属性
类的方法
创建和使用子类
构造器方法和解构器方法
调用父类的 super() 方法
面向对象编程
类与实例
类，是一个定义；实例是真正的对象的实现，创建一个实例的过程称作实例化。
所有的类都继承自一个叫做 object 的类。继承的定义之后再说。
类的一些操作方式和函数很像，在没有面向对象编程方式的时候，就是面向过程（函数）的开发方式。
类可以很复杂，也可以很简单，取决于应用的需要。面向对象的编程方式，是现代流行的开发方式，博大精深，需要慢慢理解体会。一开始有些不太清楚，也没有关系。
类的属性
类可以理解为一种称之为命名空间（ namespace），在这个类之下，数据都是属于这个类的实例的，我们称为属性，用实例名字+点+属性名字。这样的类比较简单，在 c 语言中称为结构体，在 pascal 中为记录类型，python 的数据结构比较简单。
End of explanation
class MyData(object):
    
    # 定义一个 SayHello 的方法，self 可以理解为必须传递的参数
    def SayHello(self):
        print('Hello!')
        
# 实例化
obj_math = MyData()
# 调用方法
obj_math.SayHello()
# 类的方法的直接调用，其实还是实例化了
MyData().SayHello()
# 在上面基础上，复杂一点的例子
class MyData(object):
    
    # 初始化方法，双下划线前后
    # 实例化的时候，需要传递 self 之后的参数
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    # 定义一个 SayHello 的方法，self 可以理解为必须传递的参数
    def SayHello(self):
        print('Hello!')
        
    def Add(self):
        print(self.x + self.y)
        
# 实例化
obj_math = MyData(3,4)
# 调用方法
obj_math.SayHello()
obj_math.x = 5
obj_math.Add()
o1 = MyData(1,3)
o1.Add()
# 再复杂一点，创建多个实例
class MyData(object):
    
    # 初始化方法，双下划线前后
    # 实例化的时候，需要传递 self 之后的参数
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    # 定义一个 SayHello 的方法，self 可以理解为必须传递的参数
    def SayHello(self):
        print('Hello!')
        
    def Add(self):
        print(self.x + self.y)
        
# 实例化
obj_math = MyData(3,4)
# 调用方法
obj_math.SayHello()
obj_math.Add()
# 再创建一个实例
obj_math2 = MyData(5,6)
obj_math2.Add()
# 我们建立一个有趣的简单的模仿游戏的类来说明面向对象编程的概念
# 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()
Explanation: 类的方法
光是把类作为命名空间太简单了，可以给类添加功能，称为方法。
方法定义在类中，使用在实例中。可以理解为实例是真正做事情的代码，所以在实例中调用方法完成某个功能是合理的。
End of explanation
# 战士 Soldier 类，继承自 NPC class
class Soldier(NPC):
    # 暂时什么也不干
    pass
        
# 创建一个 Soldier 类， 作为 NPC 的子类
n1 = Soldier('AA2')
# 调用方法，因为 Soldier 中此刻并没有任何实际的方法等，所以实际上自动调用了父类的方法
n1.show_properties()
n1.fight_common()
Explanation: 这个 NPC 的类，在初始化这里定义了 NPC 拥有的三个属性，name、weapon、blood，其中 name 需要创建实例的时候设置。
NPC 类有两个方法，一个是 show_properties()，显示 NPC 的属性；另外一个是 fight_common()，我们称之为普通攻击。
创建和使用子类
通过 class B(A)，即表示从 A 开始创建一个子类，我们称之为继承。
面向对象的编程方式的有点已经可以体现，我们可以通过继承来构造一个对象体系，比如这里现在 NPC 这个类中，定义了游戏中人物的最基本的一些属性和方法，可以理解不光 NPC 是战士，还是巫师，对会有这些属性和方法；然后我们把各自独特的属性和方法定义在从 NPC 中继承的各个子类中。我们先来定义一个战士 Soldier 类。
End of explanation
# 战士 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()       
Explanation: 在子类中，可以覆盖父类的方法。
比如 __init__() ，我们需要在 Soldier 类的初始化中加入一个只有 soldier 才有的级别 level 属性。
所以，我们先调用父类的__init__() 方法，再写 Soldier 类需要的代码。
而 show_properties()方法，我们先用简单的办法，完全覆盖，也就是使用这个新的show_properties()方法，来显示 NPC 标准的三个属性以及 Soldier 的一个独立属性。
End of explanation
# 战士 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()   
Explanation: 再来看看 show_properties() 这个方法，总感觉这样有点笨重，因为不管是否是面向对象的编程方式，都不应该有太多重复的代码，现在 NPC 类中也有 show_properties() 方法，用来显示标准的三个属性，而 Soldier 类中同样名字的方法显示四个属性，如果 NPC 类中增加了属性，那么两边类中的这个方法都要修改。
我们来看看有没有更好的办法，可以用 super(需要知道父类的类，self) 的方法来访问父类的方法，这样代码就简洁优雅了。
End of explanation
# 先整理一下上面的代码
# 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)
# 创建两个 soldier
n1 = Soldier('AA1')
n1.show_properties()
n1.fight_common()  
print()
n2 = Soldier('AA2')
n2.show_properties()
n2.fight_common()   
# 连续创建多个 soldier 的实例
# 并存储在 list 中
s = []
for i in range(3):
    n = Soldier('AA' + str(i))
    n.show_properties()
    n.fight_common() 
    s.append(n)
# 看一下存储了对象的列表
print(s)
# 可以和一般访问列表一样访问列表中的对象
for i in s:
    print(i.name)
    
print(len(s))
# 可以删除一个实例
s.pop(1)
# 显示列表中的实例
for i in s:
    print(i.name)
    
print(len(s))
# 再增加一个巫师 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()
# 创建复杂的 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('--')
# 显示类的方法
print(dir(Soldier))
# 显示类的方法
print(dir(Wizard))
# 当前版本的代码
# 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!')
# 在 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 这个父类中没有显示出具体的子类名称
# 所以我们用下面的方法来显示子类的名称
# 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)
Explanation: init() 构造器方法和 del() 解构器方法
可以理解构造就是创建，解构就是销毁。每一个对象的实例都是有声明周期的，从构造到解构。
类别调用的时候，对象被创建的时候，python 先检查是否实现了 init() 方法，如果没有，则什么也不干。如果有 init()方法，则执行。
python 作为高级语言，具有垃圾对象回收机制，当 python 发现对于该实例对象的引用都被清除掉后，会执行 del() 方法。通常不需要自己去实现这个 del() 方法，python 会做好这一切。
End of explanation
# 继续优化，根据 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)
Explanation: 面向对象程序设计（英语：Object-oriented programming，缩写：OOP）是种具有对象概念的程序编程范型，同时也是一种程序开发的方法。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元，将程序和数据封装其中，以提高软件的重用性、灵活性和扩展性，对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里，计算机程序会被设计成彼此相关的对象。
面向对象程序设计可以看作一种在程序中包含各种独立而又互相调用的对象的思想，这与传统的思想刚好相反：传统的程序设计主张将程序看作一系列函数的集合，或者直接就是一系列对电脑下达的指令。面向对象程序设计中的每一个对象都应该能够接受数据、处理数据并将数据传达给其它对象，因此它们都可以被看作一个小型的“机器”，即对象。目前已经被证实的是，面向对象程序设计推广了程序的灵活性和可维护性，并且在大型项目设计中广为应用。此外，支持者声称面向对象程序设计要比以往的做法更加便于学习，因为它能够让人们更简单地设计并维护程序，使得程序更加便于分析、设计、理解。反对者在某些领域对此予以否认。
End of explanation
<END_TASK>