zoukankan      html  css  js  c++  java
  • python 全栈开发,Day19(组合,组合实例,初识面向对象小结,初识继承)

    一、组合

    表示的一种什么有什么的关系

    先来说一下,__init__的作用

    class Dog:
        def __init__(self, name, kind, hp, ad):
            self.name = name  # 对象属性 属性
            self.kind = kind
            self.hp = hp
            self.ad = ad
     
        def bite(self, p):
            p.hp -= self.ad  # 人掉血
            print('%s咬了%s一口,%s掉了%s点血' % (self.name, p.name, p.name, self.ad))

    实例化A和B

    A = Dog('笨笨', 'teddy', 50, 10)
    B = Dog('花花')

    A职员是老员工,他知道这个游戏,狗有什么属性。

    B是新来的,假如没有__init__方法,B就随便传参数了,但是类方法执行时,会报错。

    为了避免这个问题,在__init__方法里面,约束某些属性,必须要传,否则方法执行出错。

    人狗大战游戏,现在需要增加武器

    武器是人的一个属性,比如攻击力,磨损度,价格,名字,品级,技能

    增加一个类

    class Weapon:
        def __init__(self, name, price, level, ad):
            self.name = name
            self.price = price
            self.level = level
            self.ad = ad * self.level  # 升级之后,攻击就翻倍了
            self.wear = 20  # 默认的耐久度,实例化时,可以不用传
     
        def skill(self, dog):  # 技能
            dog.hp -= self.ad
            print('%s受到了%s点的伤害,%s掉了%s点血' % (dog.name, self.name, dog.name, self.ad))

    实例化一个武器

    axe = Weapon('斧头', 1000, 100, 1)  # 斧头
    

    二、组合实例

    1.人狗大战

    武器给谁装备呢?武器需要花钱买吧,那么就需要玩家充钱,现在加一个充钱功能

    完整代码如下:

    class Person:
        def __init__(self, name, sex, hp, ad):
            self.name = name  # 对象属性 属性
            self.sex = sex
            self.hp = hp  # 血量
            self.ad = ad  # 攻击力
            self.money = 0  # 金额
            self.arms = None  # 默认武器为None<br>
        def attack(self, d):
            d.hp -= self.ad
            print('%s攻击了%s,%s掉了%s点血' % (self.name, d.name, d.name, self.ad))
     
        def pay(self):  # 充值
            money = int(input('请输入您要充值的金额:'))
            self.money += money
            print('您的余额是:%s' % self.money)
     
        def wear(self, weapon):  # 装备武器
            if self.money >= weapon.price:
                self.arms = weapon  # 组合 给人装备了武器
                self.money -= weapon.price
                print('购买成功,您已经顺利装备了%s' % weapon.name)
            else:
                print('余额不足,请充值!')
     
        def attack_with_weapon(self, dog):  # 拿武器攻击狗
            if 'arms' in self.__dict__ and self.arms != None:  # 如果武器属性在实例属性字典里,并且属性不为空
                self.arms.skill(dog)  # 使用武器攻击狗
            else:
                print('请先装备武器')
     
     
    class Dog:
        def __init__(self, name, kind, hp, ad):
            self.name = name  # 对象属性 属性
            self.kind = kind
            self.hp = hp
            self.ad = ad
     
        def bite(self, p):
            p.hp -= self.ad  # 人掉血
            print('%s咬了%s一口,%s掉了%s点血' % (self.name, p.name, p.name, self.ad))
     
     
    class Weapon:  # 武器
        def __init__(self, name, price, level, ad):
            self.name = name  # 武器名
            self.price = price  # 价格
            self.level = level  # 等级
            self.ad = ad * self.level  # 升级之后,攻击就翻倍了
            self.wear = 20  # 默认的耐久度,实例化时,可以不用传
     
        def skill(self, dog):  # 技能,攻击狗
            dog.hp -= self.ad  # 狗掉血
            print('%s受到了%s点的伤害,%s掉了%s点血' % (dog.name, self.name, dog.name, self.ad))

    实例化武器,玩家购买武器,攻击狗

    alex = Person('a_sb', '不详', 1, 5)
    boss_jin = Person('金老板', '女', 20, 50)
    teddy = Dog('笨笨', 'teddy', 50, 10)
    axe = Weapon('斧头', 1000, 100, 1)  # 斧头
     
    alex.pay()  # 充值
    alex.wear(axe)  # 装备武器斧头
    alex.arms.skill(teddy)  # 使用斧头攻击狗

    执行输出:

    注意:

    不能加类静态变量meny = 0
    否则玩家充钱了,别的玩家就可以使用了

    int之后,不需要strip()一下,int会自动去除空格

    这样写是一次性的,写一个while循环,显示菜单执行

    实例化部分,改成如下:

    alex = Person('a_sb', '不详', 1, 5)
    boss_jin = Person('金老板', '女', 20, 50)
    teddy = Dog('笨笨', 'teddy', 50, 10)
    axe = Weapon('斧头', 1000, 100, 1)  # 斧头
     
     
    lst = ['攻击', '充值', '装备武器', '使用武器攻击']
    while True:
        for index, value in enumerate(lst, 1):
            print(index, value)
        num = int(input('请选择操作序号 >>>'))
        if num == 1:
            alex.attack(teddy)
        elif num == 2:
            alex.pay()
        elif num == 3:
            print('装备前余额 %s' % alex.money)
            alex.wear(axe)
            print('装备后余额 %s' % alex.money)
        elif num == 4:
            alex.attack_with_weapon(teddy)
        else:
            print('无效的序号')

    执行输出:

    修改实例化部分,代码如下:

    alex.pay()  # 充值
    alex.wear(axe)  # 装备武器斧头
    print(alex.__dict__)  # 查看alex的属性
    print(axe)  # 查看aex

    执行输出:

    请输入您要充值的金额:2000
    您的余额是:2000
    购买成功,您已经顺利装备了斧头
    {'sex': '不详', 'hp': 1, 'name': 'a_sb', 'ad': 5, 'arms': <__main__.Weapon object at 0x00000250D273C6A0>, 'money': 1000}
    <__main__.Weapon object at 0x00000250D273C6A0>

    可以发现alex的arms属性和axe的内存地址,是一摸一样的。

    skill方法,只有武器才有,人是不能直接调用的。但是,人一旦装备上了武器,就可以执行skill方法。

    关键点,就在于以下一段代码

    self.arms = weapon  # 组合 给人装备了武器

    直接给人加了一个属性arms,注意,等式右边的weapon是一个武器对象

    所以就可以使用武器攻击狗

    self.arms.skill(dog)

    self也就是实例对象,比如alex

    当一个类的对象作为另一个类的属性,说明这2个类组合在一起了。

    那么类就可以使用另外一个类的属性和方法了。

    一般说组合 ,是指2个类的组合

    修改实例化部分

    alex = Person('a_sb', '不详', 1, 5)
    boss_jin = Person('金老板', '女', 20, 50)
    teddy = Dog('笨笨', 'teddy', 50, 10)
    axe = Weapon('斧头', 1000, 100, 1)  # 斧头
     
    alex.pay()  # 充值
    alex.wear(axe)  # 装备武器斧头
    print(alex.arms.__dict__)  # 查看axe实例,也就是Weapon类的所有属性
    alex.arms.skill(teddy)  # 执行

    执行输出:

    请输入您要充值的金额:2000
    您的余额是:2000
    购买成功,您已经顺利装备了斧头
    {'price': 1000, 'ad': 100, 'level': 100, 'wear': 20, 'name': '斧头'}
    笨笨受到了斧头点的伤害,笨笨掉了100点血

    为什么会用组合 :独立的对象不能发挥他的作用,必须依赖一个对象

    比如上面的例子,斧头不能够攻击狗,它依赖人来执行。

    修改实例化部分

    alex = Person('a_sb', '不详', 1, 5)
    boss_jin = Person('金老板', '女', 20, 50)
    teddy = Dog('笨笨', 'teddy', 50, 10)
    axe = Weapon('斧头', 1000, 100, 1)  # 斧头
     
    axe.skill(teddy)  # 直接用斧头攻击狗

    执行输出:

    笨笨受到了斧头点的伤害,笨笨掉了100点血

    这样就不对了

    人还可以装备2件武器

    alex = Person('a_sb', '不详', 1, 5)
    boss_jin = Person('金老板', '女', 20, 50)
    teddy = Dog('笨笨', 'teddy', 50, 10)
    axe = Weapon('斧头', 1000, 100, 1)  # 斧头
    knife = Weapon('刀', 1000, 100, 1)  # 刀
     
    alex.pay()  # 充值
    alex.wear(axe)  # 装备武器斧头
    alex.wear(knife)  # 装备武器刀
    alex.arms.skill(teddy)  # 执行攻击

    执行输出:

    请输入您要充值的金额:2000
    您的余额是:2000
    购买成功,您已经顺利装备了斧头
    购买成功,您已经顺利装备了刀
    笨笨受到了刀点的伤害,笨笨掉了100点血

    2.圆环

    圆形类
    写一个圆环类 组合 圆形类 去完成 计算圆环的面积和周长
    一个类的对象作为另一个类对象的属性
    圆环中有圆

    圆形类 : 计算圆形面积 和 周长
    圆环类 :
    圆环的周长 : 大圆周长加小圆周长
    圆环的面积 : 大圆的面积 - 小圆的面积

    看昨天写的圆环

    from math import pi
     
     
    class Ring1:
        def __init__(self, out_r, in_r):
            self.out_r = out_r
            self.in_r = in_r
     
        def cal_area(self):
            return abs(pi * self.out_r ** 2 - pi * self.in_r ** 2)
     
        def cal_perimeter(self):
            return pi * self.out_r * 2 + pi * self.in_r * 2
     
     
    r1 = Ring1(10, 5)
    print(r1.cal_area())
    print(r1.cal_perimeter())

    执行输出:

    235.61944901923448
    94.24777960769379

    改成组合方式

    from math import pi
     
     
    class Circle:  # 圆形的面积公式不会变
        def __init__(self, r):
            self.r = r
     
        def cal_area(self):  # 面积
            return pi * self.r ** 2
     
        def cal_perimeter(self):  # 周长
            return pi * self.r * 2
     
     
    class Ring2:  # 圆环
        def __init__(self, out_r, in_r):
            self.out_circle = Circle(out_r)  # 实例化圆作为圆环的大圆
            self.in_circle = Circle(in_r)  # 实例化圆,作为圆环的小圆
     
        def area(self):  # 圆环面积<br>        #用绝对值,保证结果不为负数
            return abs(self.out_circle.cal_area() - self.in_circle.cal_area())  # 大圆面积 - 小圆面积
     
        def cal_perimeter(self):
            return self.out_circle.cal_perimeter() + self.in_circle.cal_perimeter()  # 大圆周长 + 小圆周长
     
     
    r1 = Ring2(10, 5)  # 实例化圆环,传入大圆和小圆的半径
    print(r1.area())
    print(r1.cal_perimeter())

    执行输出:

    235.61944901923448
    94.24777960769379

    在这个例子中,圆环包含了圆,而圆的面积和周长公式,和圆环有点类似。

    所以,可以把圆,这个对象,作为圆环类对象的属性,这就是组合。

    3.老师和班级

    老师
    属性:姓名 年龄 性别 班级 : s11

    班级
    属性:班级名 班级人数 科目 性质

    class Clas:
        def __init__(self, name, num, course, type):
            self.name = name
            self.num = num
            self.course = course
            self.type = type
     
     
    class Teacher:
        def __init__(self, name, sex, age):
            self.name = name
            self.sex = sex
            self.age = age
     
     
    py11 = Clas('超级无敌s11', 89, 'python', '脱产全栈')  # 实例化一个班级
    print(py11.course)  # 查看课程
     
    boss_jin = Teacher('太白', '?', 40)  # 实例化一个老师

    执行输出:

    python

    那么老师怎么和班级关联呢?

    直接给Teacher类加一个属性cls,表示班级

    class Teacher:
        def __init__(self, name, sex, age, cls):
            self.name = name
            self.sex = sex
            self.age = age
            self.cls = cls  # 班级

    实例化时,最后一个参数,直接传对象py11

    py11 = Clas('超级无敌s11', 89, 'python', '脱产全栈')  # 实例化一个班级
    print(py11.course)  # 查看课程
     
    boss_jin = Teacher('太白', '?', 40, py11)  # 实例化一个老师
    print(boss_jin.cls.course)  # 查看课程
    print(boss_jin.cls.__dict__)  # 查看py11的所有属性

    执行输出:

    python
    python
    {'num': 89, 'course': 'python', 'type': '脱产全栈', 'name': '超级无敌s11'}

    如果人数变了,Teacher也能感知到

    py11 = Clas('超级无敌s11', 88, 'python', '脱产全栈')  # 实例化一个班级
    print(py11.course)  # 查看课程
     
    boss_jin = Teacher('太白', '?', 40, py11)  # 实例化一个老师
    print(boss_jin.cls.num)  # 查看人数

    执行输出:

    python
    88

    通过实例化,可以直接组合2个类。

    在这个例子中,Teacher通过cls属性,得到了py11对象的所有属性以及方法。

    这是组合的第二种使用方式

    三、初识面向对象小结

    面向对象的思想

      不关注程序执行的过程

      关心的是一个程序中的角色,以及角色与角色之间的关系

    在python中,一切皆对象

    类:list

    对象,实例: [1,2,3,4,5]

    看list的源代码

    class list(object):
        """
        list() -> new empty list
        list(iterable) -> new list initialized from iterable's items
        """

    发现,list也是一个对象,它有很多方法

    类是给别人使用的,比如list.append()
    类转换为对象,是实例化的过程

    实例化的过程

      创建一个对象

      __init__给对象添加一些属性,对象默认的名字self

      将self所指向的内存空间返回给实例化它的地方

    使用这个对象可以找到两个东西

      对象所在的内存空间中存储的属性

      类对象指针,指向类中所有方法和静态属性

    对象找名字的时候:先找自己内存空间中的,再找类的

    对象没有权利修改类中的静态变量和方法,比如self.类静态变量

    如果修改了,那么就存在自己的对象空间里面

    用类名操作静态变量(属性),可以让全局生效

    类名:实例化对象 调用静态属性 执行方法

    交互:对象可以作为参数传递给类中的方法

    组合:对象可以作为一个对象的属性--什么有什么的关系

    处处都是组合和交互

    class B:pass
    class A:
        def func(self,aaa):
            print(aaa)
     
    a = A()
    b = B()
    a.func(b)

    执行输出:

    <__main__.B object at 0x000002CD1677C780>

    class B:pass
    class A:
        def func(self,aaa):
            print(aaa)
     
    a = A()
    b = B()
    a.func('666')

    执行输出:666

    这也是组合,666是str对象,它也是交互,它是python内置的对象和自定义的对象组合

    class B:pass
    class A:
        def func(self,aaa):
            self.aaa = aaa
     
    a = A()
    b = B()
    a.func('666')  # 传值给aaa
    print(a.aaa)

    执行输出:

    666

    class B:pass
    class A:
        def func(self,aaa):
            self.aaa = aaa
     
    a = A()
    b = B()
    a.func('666')
    print(a.aaa.startswith('6'))

    执行输出:True

    四、初识继承

    面向对象的三大特性:

    继承,多态,封装

    这3大特性是所有面向对象语言特点

    比如游戏的例子

    人物:名字,角色,性别,职业,技能

        治疗

            回血

            医生

            护士

        输出

        防御

    看如下代码:

    class Person:
        def __init__(self, name, hp, ad):
            self.name = name
            self.hp = hp
            self.ad = ad
           
             
    class Dog:
        def __init__(self, name, hp, ad):
            self.name = name
            self.hp = hp
            self.ad = ad

    Person和dog有相同的属性,那么应该有某种联系

    类与类直接的关系:什么是什么的关系

    class Parent:pass
    class Son(Parent):pass #继承关系,Son继承了Parent
    print(Son.__bases__) #内置的属性,查看父类
    print(Parent.__bases__)

    执行输出:

    (<class '__main__.Parent'>,)
    (<class 'object'>,)

    可以看出Son的父类是Parent,Parent的父类是object

    在python3中,所有的类都会默认继承object类
    继承了object类的所有类都是新式类
    如果一个类没有继承任何父类,那么__bases__属性就会显示<class 'object'>

    继承一般有2种:单继承和多继承

    单继承

    class Parent:pass
    class Son(Person):pass

    多继承

    class Parent1:pass
    class Parent2(Parent1):pass
    class Parent3:pass
    class Son(Parent2,Parent3):pass   # 继承关系
    print(Parent2.__bases__)
    print(Son.__bases__)

    执行输出:

    (<class '__main__.Parent1'>,)
    (<class '__main__.Parent2'>, <class '__main__.Parent3'>)

    父类 :别名有2个,基类,超类
    子类 :别名 派生类

    先有了人 狗两个类
    发现两个类有相同的属性、方法
    人和狗 都是游戏里的角色
    抽象出一个animal类型
    继承

    class Animal:
        role = 'Animal'
        def __init__(self,name,hp,ad):
            self.name = name     # 对象属性 属性
            self.hp = hp         #血量
            self.ad = ad         #攻击力
     
    class Person(Animal):pass
    class Dog(Animal):pass
    alex = Person()

    执行报错:

    TypeError: __init__() missing 3 required positional arguments: 'name', 'hp', and 'ad'

    缺少3个参数,继承了父类的init方法

    再次实例化

    alex = Person('alex',10,5)
    print(alex)  # 查看父类
    print(alex.__dict__)  # 查看属性

    执行输出:

    <__main__.Person object at 0x000001DA0687C7B8>
    {'hp': 10, 'name': 'alex', 'ad': 5}

    添加狗实例

    alex = Person('alex',10,5)
    print(alex)
    print(alex.__dict__)
    dog = Dog('teddy',100,20)
    print(dog)
    print(dog.__dict__)

    执行输出:

    <__main__.Person object at 0x0000015E6983C710>
    {'ad': 5, 'name': 'alex', 'hp': 10}
    <__main__.Dog object at 0x0000015E6983C7B8>
    {'ad': 20, 'name': 'teddy', 'hp': 100}

    自个,有各自的属性

    class Animal:
        role = 'Animal'
        def __init__(self,name,hp,ad):
            self.name = name     # 对象属性 属性
            self.hp = hp         #血量
            self.ad = ad         #攻击力
     
        def eat(self):
            print('%s吃药回血了'%self.name)
     
    class Person(Animal):
        r = 'Person'
        def attack(self,dog):   # 派生方法
            print("%s攻击了%s"%(self.name,dog.name))
     
        def eat2(self):
            print('执行了Person类的eat方法')
            self.money = 100
            self.money -= 10
            self.hp += 10
     
    class Dog(Animal):
        def bite(self,person):  # 派生方法
            print("%s咬了%s" % (self.name, person.name))
    alex = Person('alex',10,5)
    dog = Dog('teddy',100,20)
    alex.attack(dog) #继承中的派生方法
    alex.eat()  #继承父类方法自己没有同名方法
    alex.eat2()
    dog.eat()

    执行输出:

    alex攻击了teddy
    alex吃药回血了
    执行了Person类的eat方法
    teddy吃药回血了

    init始终在父类里面,执行了alex实例化,返回给alex

     

    alex实例化时,创建一个实例命令空间

    alex.attack(dog) 直接调用,因为实例空间中存在

    alex.eat() 实例空间找不到,取找类对象指针,指针指向父类Person,从此类中找到了eat方法。

    ....

    对象使用名字的顺序

    alex = Person('alex',10,5)
    alex.eat = 'aaa' #增加一个不存在的方法,名字和父类方法名一样
    print(alex.eat)

      指向输出:

    aaa

    alex = Person('alex',10,5)
    alex.eat = 'aaa'
    print(alex.eat())

    指向报错:

    TypeError: 'str' object is not callable

    因为此时eat是字符串,不是方法

    对象使用名字顺序:

    先找对象自己内存空间中的,再找对象自己类中的,再找父类中的

    经典面试题

    class Parent:
        def func(self):
            print('in parent func')
        def __init__(self):
            self.func()
     
    class Son(Parent):
        def func(self):
            print('in son func')
     
    s = Son()

    上面的代码执行的是son还是parent中的方法?

    执行输出:

    in son func

    分析:

    s = Son() 创建实例命名空间s

    Son继承了Parent,类对象指针指向Son和Parent
    Son没有__init__方法,它继承了Parent的__init__方法。
    执行__init__方法,此时self是指向Son的
    执行self.func()
    根据查找顺序,自己->自己的类->父类
    由于自己没有,从类中找,发现了,执行func
    最终输出in son func

    总结:

    self.名字的时候,不要看self当前在哪个类里,要看这个self到底是谁的对象

    来一张大神的图片

    作业 :

    读博客
    把实例全走一遍
    默写 圆环类与圆类组合
    

     圆环类与圆类组合

    from math import pi
      
      
    class Circle:  # 圆形的面积公式不会变
        def __init__(self, r):
            self.r = r
      
        def cal_area(self):  # 面积
            return pi * self.r ** 2
      
        def cal_perimeter(self):  # 周长
            return pi * self.r * 2
      
      
    class Ring2:  # 圆环
        def __init__(self, out_r, in_r):
            self.out_circle = Circle(out_r)  # 实例化圆作为圆环的大圆
            self.in_circle = Circle(in_r)  # 实例化圆,作为圆环的小圆
      
        def area(self):  # 圆环面积<br>        #用绝对值,保证结果不为负数
            return abs(self.out_circle.cal_area() - self.in_circle.cal_area())  # 大圆面积 - 小圆面积
      
        def cal_perimeter(self):
            return self.out_circle.cal_perimeter() + self.in_circle.cal_perimeter()  # 大圆周长 + 小圆周长
      
      
    r1 = Ring2(10, 5)  # 实例化圆环,传入大圆和小圆的半径
    print(r1.area())
    print(r1.cal_perimeter())
  • 相关阅读:
    Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能
    android 网络异步加载数据进度条
    在WebView中加载HTML页面时显示进度对话框的方法
    Java基础知识总结之IO流
    计算机网络之网络协议
    Java中hashCode的作用
    一篇很好的学习查看Java源代码的文章
    JavaScript 客户端JavaScript之 脚本化文档
    JavaScript 超类与子类 继承
    JavaScript 类、构造函数、原型
  • 原文地址:https://www.cnblogs.com/tu240302975/p/12659080.html
Copyright © 2011-2022 走看看