面向对象

Posted sky77

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象相关的知识,希望对你有一定的参考价值。

第一次参加工作,进入了一家游戏公司,公司需要开发一款游戏《人狗大战》
一款游戏,首先得把角色和属性定下来。

角色有2个,分别是人和狗
属性如下:
人 :昵称、性别、血、攻击力
狗 :名字、品种、血、攻击力

 

 1 #
 2 person = {name: xiao_Ming, sex:M, hp: 1, ad: 5}
 3 #
 4 dog = {name: 旺财, sex:M, hp: 100, ad: 100}
 5 
 6 def attack(person,dog):
 7     #人攻击狗
 8     print({}攻击{}.format(person[name], dog[name]))
 9     #狗掉血,狗的血量-人的攻击力
10     dog[hp] -= person[ad]
11 
12 attack(person,dog)
13 #查看狗的血量
14 print(dog[hp])

执行输出:

xiao_Ming攻击旺财
95

人攻击了狗,狗要反击了!,再定义一个函数

 1 def bite(dog,person): #狗咬人
 2     print({}咬了{}.format(dog[name], person[name]))
 3     # 人掉血,人的血量-狗的攻击力
 4     person[hp] -= dog[ad]
 5     #判断人的血量是否小于等于0
 6     if person[hp] <= 0:
 7         print(game over,{} win.format(dog[name]))
 8 
 9 bite(dog,person)
10 #查看人的血量
11 print(person[hp])

执行输出:

旺财咬了xiao_Ming
game over,旺财 win
-99

 

现在还只有一个玩家,有多个玩家怎么办,再加一个?
每添加一个人,就得创建一个字典
但是创造一个人物角色,没有血条,游戏就会有bug

所以,为了解决这个问题,需要定义一个模板,那么人的属性就固定下来了

定义2个函数,人和狗的模板 注意: self它不是关键字,只是一个变量而已。varieties表示品种

 1 def Person(name, sex, hp, ad):
 2     # 人模子
 3     self = {name: name, sex: sex, hp: hp, ad: ad}
 4     return self
 5 
 6 
 7 def Dog(name, varieties, hp, ad):
 8     # 狗模子
 9     self = {name: name, varieties: varieties, hp: hp, ad: ad}
10     return self
11 
12 def bite(dog,person): #狗咬人
13     print({}咬了{}.format(dog[name], person[name]))
14     # 人掉血,人的血量-狗的攻击力
15     person[hp] -= dog[ad]
16     #判断人的血量是否小于等于0
17     if person[hp] <= 0:
18         print(game over,{} win.format(dog[name]))
19 
20 person1 = Person(xiao_Ming,M,1,5)
21 dog1 = Dog(旺财,teddy,100,100)
22 
23 bite(dog1,person1)
24 print(person1[hp])

可以发现,这里就规范了角色的属性个数,简化了创建角色的代码

执行输出:

旺财咬了xiao_Ming
game over,旺财 win
-99

如果参数传的顺序乱了,游戏就会有bug

为了解决这个问题,需要把攻击函数放在人模子里面,咬人放在狗模板里面。

外部无法直接调用。

 

 1 def Person(name,sex,hp,ad):
 2     # 人模子
 3     self = {name:name, sex:sex, hp:hp, ad: ad}
 4  
 5     def attack(dog):  # 人攻击狗
 6         #参数已经被self接收了,所以attack函数不需要接收2个参数,1个参数就够了
 7         print({}攻击{}.format(self[name], dog[name]))
 8         # 狗掉血,狗的血量-人的攻击力
 9         dog[hp] -= self[ad]
10  
11     self[attack] = attack #增加一个字典key
12     print(self) #查看字典的值
13     return self
14  
15 def Dog(name,varieties,hp,ad):
16     # 狗模子
17     self = {name: name, varieties: varieties, hp: hp, ad: ad}
18  
19     def bite(person):  # 狗咬人
20         # 参数已经被self接收了,所以bite函数不需要接收2个参数,1个参数就够了
21         print({}咬了{}.format(self[name], person[name]))
22         # 人掉血,人的血量-狗的攻击力
23         person[hp] -= self[ad]
24         # 判断人的血量是否小于等于0
25         if person[hp] <= 0:
26             print(game over,{} win.format(self[name]))
27  
28     self[bite] = bite
29     return self
30  
31 #创建2个角色
32 person1 = Person(xiao_Ming,M,1,5)
33 dog1 = Dog(旺财,teddy,100,100)
34 #执行人攻击狗函数,它是通过字典取值调用的。
35 person1[attack](dog1)
36 print(dog1[hp])

执行输出:

{‘name‘: ‘xiao_Ming‘, ‘sex‘: ‘M‘, ‘hp‘: 1, ‘attack‘: <function Person.<locals>.attack at 0x000001F56180AAE8>, ‘ad‘: 5}
xiao_Ming攻击旺财
95

 

 

面向对象编程

类的概念 : 具有相同属性和技能的一类事物
人类就是抽象一个概念
对象 : 就是对一个类的具体的描述
具体的人 ,她有什么特征呢?比如,眉毛弯弯的,眼睛大大的,穿着一件粉色的裙子...

 

比如说桌子,猫,这些是类的概念,为什么呢?因为它是抽象的。

再比如购物

商品 的大概属性: 名字,类别,价格,产地,保质期,编号...
比如 苹果 生鲜类 5块钱 --- 它是一个对象,因为对它做了具体描述

 

使用面向对象的好处:
  1.使得代码之间的角色关系更加明确
  2.增强了代码的可扩展性
  3.规范了对象的属性和技能


面向对象的特点:结局的不确定性

 

新建一个类,类名的首字母最好是大写的,规范一点,否则Pycharm有波浪号

1 class Person:
2     静态变量 = 123
3  
4 print(Person.__dict__) #内置的双下划线方法

执行输出:

{‘__doc__‘: None, ‘静态变量‘: 123, ‘__module__‘: ‘__main__‘, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Person‘ objects>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Person‘ objects>}

从结果中,可以找到 ‘静态变量‘: 123

访问静态变量,第一种方式

1 print(Person.__dict__[静态变量])

访问静态变量,第二种方式

1 print(Person.静态变量)

执行输出:123

测试外部是否可以修改静态变量

1 Person.静态变量 = 456
2 print(Person.静态变量)

执行输出:456

删除静态变量

1 del Person.静态变量
2 print(Person.__dict__)

执行输出:456

{‘__module__‘: ‘__main__‘, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Person‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Person‘ objects>, ‘__doc__‘: None}

发现找不到 ‘静态变量‘: 123 了

总结:

引用静态变量
  1.类名.__dict__[‘静态变量名‘] 可以查看,但是不能删改
  2.类名.静态变量名 直接就可以访问,可以删改
  删除一个静态变量 del 类名.静态变量名

动态变量,指的是函数。为什么呢?因为函数内部要执行一段代码,参数不同,结果是不确定的。

1 class Person:
2     静态变量 = 123 #静态属性,静态变量
3     role = person
4     def f1(self): #默认带一个参数self,方法,动态属性
5         print(1234567)
6 <br>#引用动态变量
7 Person.f1()

执行报错:

TypeError: f1() missing 1 required positional argument: ‘self‘

提示缺少一个参数self

随便传一个参数,再次执行

1 Person.f1(1)

执行输出:

1234567

因为self变量必须要传,可不可以不传呢?

可以把self删掉,但是不符合规范

只要是类的方法,必须要传self
self的名字,是约定俗成

总结:

引用动态变量
  1.类名.方法名 查看这个方法的内存地址
  2.类名.方法名(实参) 调用了这个方法,必须传一个实参,这个实参传给了self

类和对象,是相对的概念

类是已经创造的模子
对象是用模子填充

调用类名加括号,创造一个对象
创造一个命名空间,唯一属于对象

1 alex = Person()   # 创造一个对象
2 alex 是对象、实例
3 Person是类
4 对象 = 类名()

类变成对象的过程,是实例化的 过程

技术分享图片

 

 

实例化,是产生实例的过程

总结:

创造一个对象 - 实例化
  产生一个实例(对象)的过程
  对象 = 类名()

 

计算机只认识二进制
写的代码,是自己能看懂的。但是执行的过程中,并不是这样种的。

 

实例化的过程:
 1.创造一个实例,将会作为一个实际参数 # python
 2.自动触发一个__init__的方法,并且把实例以参数的形式传递给__init__方法中的self形参
 3.执行完__init__方法之后,会将self自动返回给alex
  __init__方法 :初始化方法,给一个对象添加一些基础属性的方法,一般情况下是针对self的赋值

 

1 class Person:
2     role = person #静态属性
3     def __init__(self):
4         print(self) #查看变量
5  
6 alex = Person()
7 print(alex) #查看变量

执行输出:

<__main__.Person object at 0x0000025F23D8BC18>
<__main__.Person object at 0x0000025F23D8BC18>

 

可以看到2次查看变量的内存地址是一样的。

也就是说self表示实例本身。

如果实例化时,传一个参数

 

1 alex = Person(sb)

执行报错:

TypeError: __init__() takes 1 positional argument but 2 were given

因为传的参数是多余的,为什么呢?在没传参之前,执行正常,传参之后,就报错了。

因为实例化时,它把实例本身传给类,self接收了参数,也就是实例本身。再多传一个参数,就报错了。

类里面再多写一个参数,实例化时,传一个参数

 

1 class Person:
2     role = person #静态属性
3     def __init__(self,name):
4         print(self,name) #查看变量
5  
6 alex = Person(sb)
7 print(alex) #查看变量

执行输出:

<__main__.Person object at 0x00000243EC48B908> sb
<__main__.Person object at 0x00000243EC48B908>

类和外部唯一的联系,就是self

技术分享图片

执行输出:

{‘name‘: ‘sb‘}

 让alex拥有自己的字典

 1 class Person:
 2     role = person #静态属性
 3     def __init__(self,name,sex,hp,ad):
 4         self.__dict__[name] = name
 5         self.__dict__[sex] = sex
 6         self.__dict__[hp] = hp
 7         self.__dict__[ad] = ad
 8  
 9 alex = Person(sb,M,1,5)
10 print(alex.__dict__)

执行输出:

{‘name‘: ‘sb‘, ‘ad‘: 5, ‘sex‘: ‘M‘, ‘hp‘: 1}

每次调用Person()都会产生一个新的内存空间,它会返回给调用者
但是上面的写法,不规范

第二种写法

 1 class Person:
 2     role = person #静态属性
 3     def __init__(self,name,sex,hp,ad):
 4         self.name = name
 5         self.sex = sex
 6         self.hp = hp
 7         self.ad = ad
 8  
 9 alex = Person(sb,M,1,5)
10 print(alex.__dict__)

执行输出,效果同上。

推荐使用第二种方法,从此以后,就不要使用__dict__的方法修改属性

直接使用对象名.属性名 修改

 1 class Person:
 2     role = person #静态属性
 3     def __init__(self,name,sex,hp,ad):
 4         self.name = name
 5         self.sex = sex
 6         self.hp = hp
 7         self.ad = ad
 8  
 9 alex = Person(sb,M,1,5)
10 alex.name = a_sb
11 print(alex.name)

执行输出:

a_sb

增加一个类方法

1 class Person:
2     role = person #静态属性
3     def __init__(self,name,sex,hp,ad):
4         self.name = name
5         self.sex = sex
6         self.hp = hp
7         self.ad = ad
8     def attack(self):
9         print({}发起了一次攻击.format(self.name))

执行类方法

1 alex = Person(sb,M,1,5)
2 Person.attack(alex)

执行输出:

sb发起了一次攻击

执行类方法可以简写

1 alex = Person(sb,M,1,5)
2 alex.attack()

方法的调用 :
  1.类名.方法名(对象名) # 那么方法中的self参数就指向这个对象
  2.对象名.方法名() # 这样写 相当于 方法中的self参数直接指向这个对象,推荐使用

 

attack是和Person关联起来的
所以外部可以直接调用attack方法

 

小明,10岁,男,上山去砍柴
小明,10岁,男,开车去东北
小明,10岁,男,最爱大保健
老李,90岁,男,上山去砍柴
老李,90岁,男,开车去东北
老李,90岁,男,最爱大保健
老张…

 1 class Person(object):
 2     def __init__(self, name, age, sex=, hobby=(上山去砍柴, 开车去东北, 最爱大保健)):
 3         self.name = name
 4         self.age = age
 5         self.sex = sex
 6         self.hobby = hobby
 7  
 8     def info(self):
 9         for i in self.hobby:
10             print({},{}岁,{},{}.format(self.name, self.age, self.sex, i))
11  
12  
13 ming = Person(小明, 10)
14 li = Person(老李, 90)
15 ming.info()
16 li.info()

 

使用面向对象的方式编码三级菜单

将之前的代码复制粘贴过来,切割成面向对象方式

 1 # -*- coding: utf-8 -*-
 2 class AreaMenu(object):
 3     def __init__(self):
 4         self.zone = {
 5             山东: {
 6                 青岛: [四方, 黄岛, 崂山, 李沧, 城阳],
 7                 济南: [历城, 槐荫, 高新, 长青, 章丘],
 8                 烟台: [龙口, 莱山, 牟平, 蓬莱, 招远]
 9             },
10             江苏: {
11                 苏州: [沧浪, 相城, 平江, 吴中, 昆山],
12                 南京: [白下, 秦淮, 浦口, 栖霞, 江宁],
13                 无锡: [崇安, 南长, 北塘, 锡山, 江阴]
14             },
15             浙江: {
16                 杭州: [西湖, 江干, 下城, 上城, 滨江],
17                 宁波: [海曙, 江东, 江北, 镇海, 余姚],
18                 温州: [鹿城, 龙湾, 乐清, 瑞安, 永嘉]
19             }
20         }
21         self.province = list(self.zone.keys())
22         self.run()
23  
24     def run(self):  # 省列表
25         while True:
26             print(.center(20, *))
27             # 打印省列表
28             for i in self.province:
29                 print({}	{}.format(self.province.index(i) + 1, i))
30             province_input = input(请输入省编号,或输入q/Q退出:).strip()
31             if province_input.isdigit():
32                 province_input = int(province_input)
33                 if 0 < province_input <= len(self.province):
34                     # 省编号,由于显示加1,获取的时候,需要减1
35                     province_id = province_input - 1
36                     # 城市列表
37                     city = list(self.zone[self.province[province_id]].keys())
38                     # 进入市区列表
39                     self.city(province_id, city)
40                 else:
41                     print("33[41;1m省编号 {} 不存在!33[0m".format(province_input))
42             elif province_input.upper() == Q:
43                 break
44             else:
45                 print("33[41;1m输入省编号非法!33[0m")
46  
47     def city(self, province_id, city):  # 市区列表
48         if province_id == ‘‘ or city == ‘‘:
49             return province_id 和 city 参数不能为空
50         while True:
51             print(.center(20, *))
52             for j in city:
53                 print({}	{}.format(city.index(j) + 1, j))
54             city_input = input("请输入市编号,或输入b(back)返回上级菜单,或输入q(quit)退出:").strip()
55             if city_input.isdigit():
56                 city_input = int(city_input)
57                 if 0 < city_input <= len(city):
58                     # 市编号,由于显示加1,获取的时候,需要减1
59                     city_id = city_input - 1
60                     # 县列表
61                     county = self.zone[self.province[province_id]][city[city_id]]
62                     # 进入县列表
63                     self.county(county)
64                 else:
65                     print("33[41;1m市编号 {} 不存在!33[0m".format(city_input))
66             elif city_input.upper() == B:
67                 break
68             elif city_input.upper() == Q:
69                 # 由于在多层while循环里面,直接exit退出即可
70                 exit()
71             else:
72                 print("33[41;1m输入市编号非法!33[0m")
73  
74     def county(self, county):  # 县列表
75         if county == ‘‘:
76             return county 参数不能为空
77         while True:
78             print(.center(20, *))
79             for k in county:
80                 print({}	{}.format(county.index(k) + 1, k))
81             # 到县这一级,不能输入编号了,直接提示返回菜单或者退出
82             county_input = input("输入b(back)返回上级菜单,或输入q(quit)退出:").strip()
83             if county_input == b:
84                 # 终止此层while循环,跳转到上一层While
85                 break
86             elif county_input == q:
87                 # 结束程序
88                 exit()
89             else:
90                 print("33[41;1m已经到底线了,请返回或者退出!33[0m")
91  
92  
93 if __name__ == __main__:
94     AreaMenu()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

# -*- coding: utf-8 -*-
class AreaMenu(object):
    def __init__(self):
        self.zone = {
            ‘山东‘: {
                ‘青岛‘: [‘四方‘‘黄岛‘‘崂山‘‘李沧‘‘城阳‘],
                ‘济南‘: [‘历城‘‘槐荫‘‘高新‘‘长青‘‘章丘‘],
                ‘烟台‘: [‘龙口‘‘莱山‘‘牟平‘‘蓬莱‘‘招远‘]
            },
            ‘江苏‘: {
                ‘苏州‘: [‘沧浪‘‘相城‘‘平江‘‘吴中‘‘昆山‘],
                ‘南京‘: [‘白下‘‘秦淮‘‘浦口‘‘栖霞‘‘江宁‘],
                ‘无锡‘: [‘崇安‘‘南长‘‘北塘‘‘锡山‘‘江阴‘]
            },
            ‘浙江‘: {
                ‘杭州‘: [‘西湖‘‘江干‘‘下城‘‘上城‘‘滨江‘],
                ‘宁波‘: [‘海曙‘‘江东‘‘江北‘‘镇海‘‘余姚‘],
                ‘温州‘: [‘鹿城‘‘龙湾‘‘乐清‘‘瑞安‘‘永嘉‘]
            }
        }
        self.province = list(self.zone.keys())
        self.run()
 
    def run(self):  # 省列表
        while True:
            print(‘省‘.center(20‘*‘))
            # 打印省列表
            for in self.province:
                print(‘{} {}‘.format(self.province.index(i) + 1, i))
            province_input = input(‘请输入省编号,或输入q/Q退出:‘).strip()
            if province_input.isdigit():
                province_input = int(province_input)
                if 0 < province_input <= len(self.province):
                    # 省编号,由于显示加1,获取的时候,需要减1
                    province_id = province_input - 1
                    # 城市列表
                    city = list(self.zone[self.province[province_id]].keys())
                    # 进入市区列表
                    self.city(province_id, city)
                else:
                    print("33[41;1m省编号 {} 不存在!33[0m".format(province_input))
            elif province_input.upper() == ‘Q‘:
                break
            else:
                print("33[41;1m输入省编号非法!33[0m")
 
    def city(self, province_id, city):  # 市区列表
        if province_id == ‘ or city == ‘‘:
            return ‘province_id 和 city 参数不能为空‘
        while True:
            print(‘市‘.center(20‘*‘))
            for in city:
                print(‘{} {}‘.format(city.index(j) + 1, j))
            city_input = input("请输入市编号,或输入b(back)返回上级菜单,或输入q(quit)退出:").strip()
            if city_input.isdigit():
                city_input = int(city_input)
                if 0 < city_input <= len(city):
                    # 市编号,由于显示加1,获取的时候,需要减1
                    city_id = city_input - 1
                    # 县列表
                    county = self.zone[self.province[province_id]][city[city_id]]
                    # 进入县列表
                    self.county(county)
                else:
                    print("33[41;1m市编号 {} 不存在!33[0m".format(city_input))
            elif city_input.upper() == ‘B‘:
                break
            elif city_input.upper() == ‘Q‘:
                # 由于在多层while循环里面,直接exit退出即可
                exit()
            else:
                print("33[41;1m输入市编号非法!33[0m")
 
    def county(self, county):  # 县列表
        if county == ‘‘:
            return ‘county 参数不能为空‘
        while True:
            print(‘县‘.center(20‘*‘))
            for in county:
                print(‘{} {}‘.format(county.index(k) + 1, k))
            # 到县这一级,不能输入编号了,直接提示返回菜单或者退出
            county_input = input("输入b(back)返回上级菜单,或输入q(quit)退出:").strip()
            if county_input == ‘b‘:
                # 终止此层while循环,跳转到上一层While
                break
            elif county_input == ‘q‘:
                # 结束程序
                exit()
            else:
                print("33[41;1m已经到底线了,请返回或者退出!33[0m")
 
 
if __name__ == ‘__main__‘:
    AreaMenu()
















































以上是关于面向对象的主要内容,如果未能解决你的问题,请参考以下文章

面向面试编程代码片段之GC

PHP面向对象之选择工厂和更新工厂

Java中面向对象的三大特性之封装

python之路之前没搞明白4面向对象(封装)

Scala的面向对象与函数编程

Python面向对象学习之八,装饰器