面向对象 楔子

写在前面
  此篇文章并没有用到面向对象的语法(也就是class),而是以面向对象的思想,通过之前学过的函数知识来实现某种需求,进而初步展示一下面向对象的思路思想以及特点与优点

1. 需求描述

  假如,现在要完成一个简单的《人狗大战》的小游戏,那么我们需要考虑如下:
    1)要有人和狗两种不同角色;
    2)人和狗分别有不同的属性;
    3)人可能有姓名、职业、攻击力、血量等等属性;
    4)狗可能有姓名、品种、攻击力、血量等属性

2. 面向对象思想实现人狗大战

2.1 初步实现

1)我们先通过现有知识,通过函数先来尝试完成上述两个角色的建立

# 定义人的模版
def person(name,job,hp,ad,level=1):
    person_dic = {'name':name,'job':job,'hp':hp,'ad':ad,'level':level}	# 属性分别为:姓名、角色、血量、攻击力、等级
    return person_dic

# 定义狗的模版
def dog(name,kind,hp,ad):
    dog_dic = {'name':name,'kind':kind,'hp':hp,'ad':ad}	# 属性分别为:姓名、品种、血量、攻击力
    return dog_dic

如上代码,即定义好了“人”与“狗”两种角色的属性模版

2)根据模版生成具体的人和狗

# 生成具体的人
tom = person('tom','法师',1000,50)

# 生成具体的狗
maomao = dog('maomao','泰迪',500,10)

如上代码,生成了具体的人狗对象,他们分别具有不同的属性及属性值之后即可考虑人狗之前互相攻击的动作

3)定义人狗互殴的动作/函数
定义人攻击狗的动作/函数

# 人攻击狗
def attack(person,dog):
    dog['hp'] -= person['ad']
    print('%s攻击了%s,%s掉了%s点血' % (person['name'],dog['name'],dog['name'],person['ad']))

定义狗咬人的动作/函数

# 狗咬人
def bite(dog,person):
    person['hp'] -= dog['ad']
    print('%s咬了%s,%s掉了%s点血' % (dog['name'],person['name'],person['name'],dog['ad']))

已定义好人攻击狗,狗咬人的函数

4)调用上述攻击与咬函数

# 调用人攻击狗函数,tom攻击maomao
attack(tom,maomao)	# 输出:tom攻击了maomao,maomao掉了50点血

# 调用狗咬人函数,maomao咬tom
bite(maomao,tom)	# 输出:maomao咬了tom,tom掉了10点血

如上至此,人狗大战的需求已完成,可创建角色,也可实现人狗互殴

然而,在调试过程中,不小心犯了如下错误:

# bite函数传参错误
bite(tom,maomao)	# 输出:tom咬了maomao,maomao掉了50点血

bite函数的意义本是狗咬人,结果由于在调用bite函数的时候参数的位置传入有误,而bite函数又没有任何约束,谁都可以进行调用,才导致出现了狗急跳墙而人急咬狗的情况…

那如何对attack和bite函数进行约束与限制呢?(下面以attack函数为例进行讲解)

2.2 通过面向对象思想优化代码

1)第一步:将attack内置到“人”的模版中

#定义人的模版
def person(name,job,hp,ad,level=1):
    person_dic = {'name':name,'job':job,'hp':hp,'ad':ad,'level':level}
    # 人攻击狗
    def attack(person,dog):
        dog['hp'] -= person['ad']
        print('%s攻击了%s,%s掉了%s点血' % (person['name'],dog['name'],dog['name'],person['ad']))
    return person_dic

此时,attack函数内置到了person模版中,但是attack无法从外部直接调用,该如何解决呢?

2)第二步:将attack函数对内存地址赋予到person角色到一个属性中(即person_dic的一个key)

#定义人的模版
def person(name,job,hp,ad,level=1):
    person_dic = {'name':name,'job':job,'hp':hp,'ad':ad,'level':level}
    # 人攻击狗
    def attack(person,dog):
        dog['hp'] -= person['ad']
        print('%s攻击了%s,%s掉了%s点血' % (person['name'],dog['name'],dog['name'],person['ad']))
    person_dic['attack'] = attack   # 将attack函数的内存地址赋予person_dic['attack'],后续直接加括号即可调用
    return person_dic

tom = person('tom','法师',1000,50)
print(tom)	# 输出:{'attack': .attack at 0x103613950>, 'ad': 50, 'hp': 1000, 'job': '法师', 'level': 1, 'name': 'tom'}

如上代码,执行后发现:
  tom的属性字典里有一个'attack': .attack at 0x103613950>,那么这个攻击的方法就只属于tom了

3)第三步:传参优化及函数调用

#定义人的模版
def person(name,job,hp,ad,level=1):
    person_dic = {'name':name,'job':job,'hp':hp,'ad':ad,'level':level}
    # 人攻击狗
    def attack(dog):
        dog['hp'] -= person_dic['ad']
        print('%s攻击了%s,%s掉了%s点血' % (person_dic['name'],dog['name'],dog['name'],person_dic['ad']))
    person_dic['attack'] = attack   # 将attack函数的内存地址赋予person_dic['attack'],后续直接加括号即可调用
    return person_dic

tom = person('tom','法师',1000,50)
print(tom)  # 查看tom的属性信息
# tom['attack'](dog_object) # 直接通过此种方法,传入具体出来的dog对象,就可调用该函数

  attack函数目前是内部函数,就可以直接引用外部函数的变量了,就没必要传person这个参数了,直接调用person_dic这个外部字典的值即可
  这样的话,attack函数只需要传入dog参数即可,直接通过tom['attack'](dog_object)即可调用函数,也就无需考虑参数顺序传入错误的问题了

4)第四步:以同样的方法修改dog函数,整体代码整理后如下

#定义人的模版
def person(name,job,hp,ad,level=1):
    person_dic = {'name':name,'job':job,'hp':hp,'ad':ad,'level':level}
    # 人攻击狗
    def attack(dog):
        dog['hp'] -= person_dic['ad']
        print('%s攻击了%s,%s掉了%s点血' % (person_dic['name'],dog['name'],dog['name'],person_dic['ad']))
    person_dic['attack'] = attack   # 将attack函数的内存地址赋予person_dic['attack'],后续直接加括号即可调用
    return person_dic

# 定义狗的模版
def dog(name,kind,hp,ad):
    dog_dic = {'name':name,'kind':kind,'hp':hp,'ad':ad}
    # 狗咬人
    def bite(person):
        person['hp'] -= dog_dic['ad']
        print('%s咬了%s,%s掉了%s点血' % (dog_dic['name'],person['name'],person['name'],dog_dic['ad']))
    dog_dic['bite'] = bite
    return dog_dic

# 具体创建人和狗的角色
tom = person('tom','法师',1000,50)
maomao = dog('maomao','泰迪',500,10)

# 执行攻击与咬的动作/函数
tom['attack'](maomao)	# tom 攻击 maomao
maomao['bite'](tom)	# maomao 咬 tom

'''
输出结果:
tom攻击了maomao,maomao掉了50点血
maomao咬了tom,tom掉了10点血
'''

此时,攻击和咬两个动作就分别约束在了person和dog当两个模版中了,而当person调用bite函数时,就会由于无此函数而报错(由代码知,person函数下只有person_dic和attack()),狗同理

# 执行tom咬maomao
tom['bite'](maomao)
'''
报错信息
Traceback (most recent call last):
  File "/Users/mlg/Knowledge/Python/Pycharm_Project/Py27/classes/day07/practice/面向对象博客/blog01.py", line 61, in 
    tom['bite'](maomao)
KeyError: 'bite'
'''

3. 总结

结合实现《人狗大战》的过程来简单总结一下面向对象思想的关键点:

1)不关心程序执行的具体结果
2)关心程序中有多少个角色
3)关心每个角色有什么属性、技能/方法
4)所有角色都是又一个模版创建
5)所有的属性和技能都在自己的角色模版中
6)可直接看出程序的结构(即 可读性好)
7)可以很方便的为角色添加属性和技能(即 可扩展性高)

你可能感兴趣的:(python)