• python全栈学习--Day17(初识面向对象)


    一、引子

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

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

    定义2个字典

    1
    2
    3
    4
    #人
    person = {'name''xiao_Ming''sex':'M''hp'1'ad'5}
    #狗
    dog = {'name''旺财''sex':'M''hp'100'ad'100}

    首先是人攻击狗,定义个函数

    1
    2
    3
    4
    5
    def attack(person,dog):
        #人攻击狗
        print('{}攻击{}'.format(person['name'], dog['name']))
        #狗掉血,狗的血量-人的攻击力
        dog['hp'-= person['ad']

    执行函数

    1
    2
    3
    attack(person,dog)
    #查看狗的血量
    print(dog['hp'])

    执行输出:

    xiao_Ming攻击旺财
    95

    人攻击了狗,狗得反击吧,再定义一个函数

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

    执行函数

    1
    2
    3
    bite(dog,person)
    #查看人的血量
    print(person['hp'])

    执行输出:

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

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

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

    定义2个函数,人和狗的模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def Person(name,sex,hp,ad):
        # 人模子
        self = {'name':name, 'sex':sex, 'hp':hp, 'ad': ad}
        return self
     
    def Dog(name,varieties,hp,ad):
        # 狗模子
        self = {'name': name, 'varieties': varieties, 'hp': hp, 'ad': ad}
        return self

    注意: self它不是关键字,只是一个变量而已。varieties表示品种

    创建2个角色

    1
    2
    person1 = Person('xiao_Ming','M',1,5)
    dog1 = Dog('旺财','teddy',100,100)

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

    执行狗咬人函数

    1
    2
    bite(dog1,person1)
    print(person1['hp'])

    执行输出:

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

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

    1
    2
    attack(dog1,person1)
    print(person1['hp'])

    执行输出:

    旺财攻击xiao_Ming
    -199

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

    外部无法直接调用。

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

    执行输出:

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

    字典里面的attack对应的值,是一个函数,也就是一个内存地址

    把值取出来,传参就可以执行了

    person1['attack'](dog1)

    如果定义多个角色呢?

    1
    2
    3
    person1 = Person('xiao_Ming','M',1,5)
    person2 = Person('Zhang_san','M',1,5)
    person3 = Person('Li_si','M',1,5)

    执行输出:

    {'name': 'xiao_Ming', 'hp': 1, 'ad': 5, 'sex': 'M', 'attack': <function Person.<locals>.attack at 0x000001EC7C3AAAE8>}
    {'name': 'Zhang_san', 'hp': 1, 'ad': 5, 'sex': 'M', 'attack': <function Person.<locals>.attack at 0x000001EC7C3AAB70>}
    {'name': 'Li_si', 'hp': 1, 'ad': 5, 'sex': 'M', 'attack': <function Person.<locals>.attack at 0x000001EC7C3AABF8>}

    注意观察attack的值,它们对应的是不同的内存地址。

    函数每执行一次,就会开辟一个新的命名空间,相互之间不受影响。

    在命名空间内部,self是字典,接收参数,attack是函数,用来调用的。

    上面的2个函数Person和Dog用了闭包

    这就是用函数的方式完成了面向对象的功能。

    下面开始正式介绍面向对象

    二、面向对象编程

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

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

    再比如购物

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

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


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

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

    1
    2
    3
    4
    class Person:
        静态变量 = 123
     
    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__['静态变量'])

    执行输出:123

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

    1
    2
    Person.__dict__['静态变量'= 456
    print(Person.__dict__['静态变量'])

    执行报错:

    TypeError: 'mappingproxy' object does not support item assignment

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

    1
    print(Person.静态变量)

    执行输出:123

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

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

    执行输出:456

    删除静态变量

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

    执行报错:

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

    提示缺少一个参数self

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

    1
    Person.f1(1)

    执行输出:

    1234567

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

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

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

    总结:

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

    类和对象,是相对的概念

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

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

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

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


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

    总结:

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

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

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

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

    执行输出:

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

    name和self是没有关系的

    查看self的值

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

    执行输出:

    {}

    self默认有一个空字典

    可以给self加参数

    1
    2
    3
    4
    5
    6
    7
    class Person:
        role = 'person' #静态属性
        def __init__(self,name):
            self.__dict__['name'= name
     
    alex = Person('sb')
    print(alex.__dict__)

    执行输出:

    {'name': 'sb'}

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

    让alex拥有自己的字典

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

    执行输出:

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

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

    第二种写法

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

    执行输出,效果同上。

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

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

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

    执行输出:

    a_sb

    属性的调用:
      1.对象名.属性名 第一种调用方法,推荐使用
      2.对象名.__dict__['属性名'] 第二种调用方

    广义上的属性,是指对象的属性

    类里面的方法,没有顺序之分
    一般把init放到第一个
    在类里面的def 一般叫方法

    增加一个类方法

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

    执行类方法

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

    执行输出:

    sb发起了一次攻击

    执行类方法可以简写

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

      

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

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

    今日内容总结:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    # 查看静态变量的第一种方式
    # print(Person.__dict__)   # 内置的双下方法
    # print(Person.__dict__['静态变量'])
     
    # 查看静态变量的第二种方式
    # print(Person.静态变量)   # 123 值
    # print(Person.role)
    # Person.静态变量 = 456
    # print(Person.静态变量)
    # del Person.静态变量
    # print(Person.__dict__)
     
    类名
        # 引用静态变量
            # 1.类名.__dict__['静态变量名'] 可以查看,但是不能删改
            # 2.类名.静态变量名 直接就可以访问,可以删改
                    # 删除一个静态变量 del 类名.静态变量名
        # 引用动态变量
            # 1.类名.方法名  查看这个方法的内存地址
            # 1.类名.方法名(实参)  调用了这个方法,必须传一个实参,这个实参传给了self
        # 创造一个对象 - 实例化
            # 产生一个实例(对象)的过程
            # 对象 = 类名()
     
    # 实例化的过程:
        # 1.创造一个实例,将会作为一个实际参数  # python
        # 2.自动触发一个__init__的方法,并且把实例以参数的形式传递给__init__方法中的self形参
        # 3.执行完__init__方法之后,会将self自动返回给alex
    # __init__方法 :初始化方法,给一个对象添加一些基础属性的方法,一般情况下是针对self的赋值<br>
    # 对象
        # 在类的内部 self是本类的一个对象
        # 在类的外部,每一个对象都对应着一个名字,这个对象指向一个对象的内存空间
        # 属性的调用:
            # 对象名.属性名               第一种调用方法
            # 对象名.__dict__['属性名']   第二种调用方法
        # 方法的调用 :
            # 类名.方法名(对象名)  # 那么方法中的self参数就指向这个对象
            # 对象名.方法名()      # 这样写 相当于  方法中的self参数直接指向这个对象

    明天默写:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Person:
        role = 'person'   # 静态属性
        def __init__(self,name,sex,hp,ad):
            self.name = name     # 对象属性 属性
            self.sex = sex
            self.hp = hp
            self.ad = ad
        def attack(self):
            print('%s发起了一次攻击'%self.name)
     
    alex = Person('a_sb','不详',1,5)
    boss_jin = Person('金老板','女',20,50)
     
    alex.attack()      # 相当于执行Person.attack(alex)
    boss_jin.attack()  # 相当于执行Person.attack(boss_jin)

      

    练习一:在终端输出如下信息

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

     答案:

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

    执行输出:

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

  • 相关阅读:
    【Docker】(6)---Dockerfile文件
    【Docker】(5)---springCloud注册中心打包Docker镜像
    【Docker】(4)搭建私有镜像仓库
    【Docker】(3)---linux部署Docker及Docker常用命令
    【Docker】(2)---仓库、镜像、容器
    【Docker】(1)---Docker入门篇
    【Maven】---Nexus私服配置Setting和Pom
    【Maven】---Linux搭建Nexus3.X私服
    【Maven】---坐标与依赖
    go语言path包和filepath包的学习与使用
  • 原文地址:https://www.cnblogs.com/haowen980/p/8797560.html
Copyright © 2020-2023  润新知