• 类与对象的命名空间、组合、继承


    ---恢复内容开始---

    # 类与对象的名称空间
    
    class Game_role:
    
        name = "LOL"
        year = 2012
    
        def __init__(self, name, ad, hp):
            self.name = name
            self.ad = ad
            self.hp = hp
    
        def attack(self, obj2):
            Game_role.area = "德玛西亚"
            print("%s攻击了%s, %s掉了%s血,还剩%s血" % (self.name,
                                              obj2.name,
                                              obj2.name,
                                              self.ad,
                                              obj2.hp-self.ad))
    
    p1 = Game_role("盖伦", 10, 100)
    p2 = Game_role("剑豪", 20, 80)
    print(p1.attack(p2))
    
    p1.armor = 90
    print(p1.__dict__)
    # 对象的属性不仅可以在 __init__ 添加,还能再其他地方添加
    
    p2.attack(p1)
    print(Game_role.__dict__)
    # 类的属性,不仅在类内部添加,还可以在类的外部添加
    Game_role.year = 6
    print(Game_role.__dict__)
    
    p1 = Game_role("盖伦", 10, 100)
    print(p1.name)  # 这里不是打印 LOL,而是 盖伦
    
    # 实例化对象先从对象本身的空间找name,没有的话就找类里面的name
    # 因为对象空间中存在一个类对象指针,所以对象可以找到类中的变量以及方法
    # 类名只能找到类中的变量及方法,或者父类中找,不能找对象中的属性
    # 对象与对象之间原则上是不能相互访问的
    print(p1.year)  # 6
    
    p3 = Game_role("提莫", 30, 60)
    # 一. 类名称空间与对象的名称空间
    # 类一创建在内存中就会创建该类的名称空间,用来存储类中定义的所有名字,这些名字都是类的属性
    # 类的属性有两种:
    #   1. 静态属性——直接在类中定义的变量
    #   2. 动态属性就是定义在类中的方法
    
    # 类的数据属性对于所有对象来说是共享的
    class Person:
        language = "人类语言"
        age = 18
    
        def __init__(self):
            pass
    
        def attack(self, a):
            pass
    
    p1 = Person()
    p2 = Person()
    print(id(p1.language), id(p2.language))
    # 2645986525440 2645986525440
    print(id(p1.age), id(p2.age))
    # 1700162848 1700162848
    
    # 注意这里并不是p1改变了类的静态属性,对象只能查看
    # 因此相当于在该对象的内存中开辟了一个空间,存放对象的age属性与值20的对应关系
    p1.age = 20
    print(p1.age)  # 20
    print(p2.age)  # 18
    print(id(p1.age), id(p2.age))
    # 1700162912 1700162848
    
    # 而类的动态属性是绑定到所有对象的
    # 因此这里对象p1调用 attack 方法时可以把自己传进去
    # p1.attack(p1) 不会报错
    
    # 总结:
    # 创建一个对象实例就会创建一个该对象实例的名称空间
    # 存放对象实例的名字,称为对象实例的属性
    # obj.name 会从 obj 的名称空间找name, 找不到就去类中找,再找不到就找父类...最后找不到抛出异常
    # 组合
    # 计算有多少对象调用一个类
    
    class A:
        count = 0
        def __init__(self):
            A.count += 1
    
    obj = A()
    obj1 = A()
    print(A.count)
    
    
    # 组合:将一个对象封装到另一个对象的属性中
    
    class Game_role:
    
        def __init__(self, name, ad, hp):
            self.name = name
            self.ad = ad
            self.hp = hp
    
        def attack(self, obj2):
            print("%s攻击了%s, %s掉了%s血,还剩%s血" % (self.name, obj2.name, obj2.name, self.ad, obj2.hp-self.ad))
    
    
    
    class Weapen:
    
        def __init__(self, name, ad):
            self.name = name
            self.ad = ad
    
        def weapen_attack(self, p1, p2):
            p2.hp -= self.ad + p1.ad  # 这里会受到人和枕头的伤害
            print("%s利用%s攻击了%s,%s还剩%s血" % (p1.name, self.name, p2.name, p2.name, p2.hp))
    
    p1 = Game_role("盖伦", 10, 100)
    p2 = Game_role("剑豪", 20, 80)
    # p1.attack(p2)
    
    pillow = Weapen("枕头", 2)
    pillow.weapen_attack(p1, p2)
    # 这里有个奇怪的地方,即武器是发起者,应该是人作为发起者才合理
    
    # 为了让程序看起来合理,即要人来作为发起者,可以这样: class Game_role: def __init__(self, name, ad, hp): self.name = name self.ad = ad self.hp = hp def attack(self, obj2): print("%s攻击了%s, %s掉了%s血,还剩%s血" % (self.name, obj2.name, obj2.name, self.ad, obj2.hp-self.ad)) def equip_weapen(self, w): self.w = w # 封装属性,给一个对象封装一个属性,该属性是另一个类的对象 class Weapen: def __init__(self, name, ad): self.name = name self.ad = ad def weapen_attack(self, p1, p2): p2.hp -= self.ad + p1.ad # 这里会受到人和枕头的伤害 print("%s利用%s攻击了%s,%s还剩%s血" % (p1.name, self.name, p2.name, p2.name, p2.hp)) p1 = Game_role("盖伦", 10, 100) p2 = Game_role("剑豪", 20, 80) # p1.attack(p2) pillow = Weapen("枕头", 2) pillow.weapen_attack(p1, p2) print(p1.__dict__) # {'name': '盖伦', 'ad': 10, 'hp': 100} p1.equip_weapon(666) print(p1.w) # 666 print(p1.__dict__) # {'name': '盖伦', 'ad': 10, 'hp': 100, 'weapen': 666} # 给人物装备武器 p1.equip_weapen(pillow) # 对象p1调用类 Game_role 的方法 equip_weapen
    # 而 equip_weapen 返回的是一个形参 w
    # p1调用它的时候,相当于给p1增加了一个属性w,而这里这个属性接收的实参是pillow
    print(p1.__dict__)
    # {'name': 'a', 'ad': 20, 'hp': 100, 'w': <__main__.Weapen_1 object at 0x00000166EB9D8D68>}
    # 也就是说,p1.w = pillow p1.w.weapen_attack(p1, p2) # 盖伦利用枕头攻击了剑豪,剑豪还剩68血 # 这样看起来就是p1发起的攻击,这样就具备了合理性 # 组合的好处 # 1. 使代码更合理 # 2. 类与类之间的耦合性增强(耦合性也不是越强越好)
    # 二. 面向对象的三大特性:
    # 继承
    # 多态
    # 封装
    
    # 继承
    # 继承是一种创建新类的方式,新建的类可以继承一个或多个父类
    # 父类又称为基类或超类,新建的类称为子类或派生类
    class Animal:
        def __init__(self, name, sex, age):
            self.name = name
            self.sex = sex
            self.age = age
    
    class Person:
        def __init__(self, name, sex, age):
            self.name = name
            self.sex = sex
            self.age = age
    
    class Cat:
        def __init__(self, name, sex, age):
            self.name = name
            self.sex = sex
            self.age = age
    
    class Dog:
        def __init__(self, name, sex, age):
            self.name = name
            self.sex = sex
            self.age = age
    # 可以看出上面每个类中有很多相同的属性
    class Animal:
    
        type_name = "动物类"
    
        def __init__(self, name, sex, age):
            self.name = name
            self.sex = sex
            self.age = age
    
        def eat(self):
            print("吃东西")
    
    # Animal: 父类
    # Person, Cat, Dog: 子类
    
    class Person(Animal):
        # type_name = "人类"
        def __init__(self, name, sex, age):
            self.name = name
            self.sex = sex
            self.age = age
    
    class Cat(Animal):
        pass
    
    class Dog(Animal):
        pass
    
    # 子类可以调用父类的属性和方法
    Person.eat(111)
    print(Person.type_name)  # 动物类
    
    # 如果子类中也有 type_name 这个变量,则只调用它自己的属性
    print(Person.type_name)  # 人类

    # 对象:
    # 实例化对象
    p1 = Person("小春", "", 28)
    # 这里Person类并没有 __init__ p1会调用父类的 __init__
    print(p1.__dict__) # {'name': '小春', 'sex': '', 'age': 28}
    p1.type_name = 666
    # 对象不能更改的属性,所以这里其实是对象给自己封装了一个属性
    print(p1) # <__main__.Person object at 0x0000025E6C038C88>
    p1.eat() # 吃东西
    # p1.eat() 会从 p1 的名称空间找eat(), 找不到就去类中找,再找不到就找父类...最后找不到抛出异常
    # 继承又分为单继承和多继承
    # 定义父类1
    class Parent_Class1:
        pass
    
    # 定义父类2
    class Parent_Class2:
        pass
    
    # 单继承,基类是Parent_Class1,派生类是 Sub_Class1
    class Sub_Class1(Parent_Class1):
        pass
    
    # 多继承
    class Sub_Class2(Parent_Class1, Parent_Class2):
        pass
    
    # 继承查看方法:
    # __base__只查到从左到右继承的第一个父类
    print(Sub_Class1.__bases__)  # <class '__main__.Parent_Class1'>
    
    print(Sub_Class2.__bases__)  # (<class '__main__.Parent_Class1'>, <class '__main__.Parent_Class2'>)
    
    # 如果没有指定基类, Python的类会默认继承 object 类,object是所有类的基类,它提供了一些常见方法(如__str__)的实现
    print(Parent_Class1.__bases__)  # (<class 'object'>,)
    print(Parent_Class2.__bases__)  # (<class 'object'>,)
    # 继承的重要性——减少代码重用
    # 单继承
    # 如果一个实例化对象既要执行子类方法,又要执行父类方法
    
    # 方法一;
    class Animal:
    
        type_name = "动物类"
    
        def __init__(self, name, sex, age):
            self.name = name
            self.sex = sex
            self.age = age
    
        def eat(self):
            print("吃东西")
    
    class Person(Animal):
        # type_name = "人类"
        def __init__(self, name, sex, age, mind):
            Animal.__init__(self, name, sex, age)
            self.mind = mind
    
    class Cat(Animal):
        def __init__(self, name, sex, age, clmib):
            Animal.__init__(self, name, sex, age)
            self.clmib = clmib
    
    class Dog(Animal):
        def __init__(self, name, sex, age, look):
            Animal.__init__(self, name, sex, age)
            self.look = look
    
    p1 = Person("小春", "", 28, "有思想")
    print(p1.__dict__)  # {'name': '小春', 'sex': '男', 'age': 28, 'mind': '有思想'}
    c1 = Cat("土豆", "", 2, "爬树")
    print(c1.__dict__)  # {'name': '土豆', 'sex': '女', 'age': 2, 'clmib': '爬树'}
    d1 = Dog("大嘴", "", 3, "看门")
    print(d1.__dict__)  # {'name': '大嘴', 'sex': '女', 'age': 3, 'look': '看门'}
    
    
    # 方法二:super
    class Animal:
    
        type_name = "动物类"
    
        def __init__(self, name, sex, age):
            self.name = name
            self.sex = sex
            self.age = age
    
        def eat(self):
            print("吃东西")
    
    class Person(Animal):
        # type_name = "人类"
        def __init__(self, name, sex, age, mind):
            super().__init__(name, sex, age)
            self.mind = mind
    
        def eat(self):  # 如果都有 eat(),则要先执行父类的话,应该这样写
            super().eat()
            print("%s 吃饭" % self.name)
    
    class Cat(Animal):
        def __init__(self, name, sex, age, clmib):
            super().__init__(name, sex, age)
            self.clmib = clmib
    
    class Dog(Animal):
        def __init__(self, name, sex, age, look):
            super().__init__(name, sex, age)
            self.look = look
    
    p1 = Person("小春", "", 28, "有思想")
    print(p1.__dict__)  # {'name': '小春', 'sex': '男', 'age': 28, 'mind': '有思想'}
    c1 = Cat("土豆", "", 2, "爬树")
    print(c1.__dict__)  # {'name': '土豆', 'sex': '女', 'age': 2, 'clmib': '爬树'}
    d1 = Dog("大嘴", "", 3, "看门")
    print(d1.__dict__)  # {'name': '大嘴', 'sex': '女', 'age': 3, 'look': '看门'}
    p1.eat()
    # 吃东西
    # 小春 吃饭

    ---恢复内容结束---

  • 相关阅读:
    我不喜欢的 Rust 特性 (之一) eager drop
    为 Windows Phone 8.1 app 解决“The type does not support direct content.”的问题
    输入10个互不相同的数字并分成5对,问有多少种分法。
    code wars quiz: toInteger
    my first emacs custom key binding
    http协议消息报头学习笔记
    移动端经常遇到的小bug
    js小技巧
    ajax
    js正则表达
  • 原文地址:https://www.cnblogs.com/shawnhuang/p/10271999.html
Copyright © 2020-2023  润新知