对象的组合,初识继承

一、组合

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

  先来说一说__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__方法里面,约束某些属性,必须要传,否则方法执行出错。

总结:__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))

#实例化一个武器:
Uzi = Weapon('Uzi',1000,100,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

  总结:当一个类的对象作为另一个类的属性,说明这两个类组合在一起了。那么类就可以使用另外一个类的属性和方法了

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

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

当然人也可以装备多件装备,调用就可以了

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点血

小练习:

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

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

# 运用昨天的方法:
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()) # 大圆面积 - 小圆面积 注意这里不需要参数,在执行之前就已经运行了Circle(out_r) 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())

2、老师和班级

老师
属性:姓名 年龄 性别 班级 : 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的所有属性

  总结:

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

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

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


三、初识面向对象小结

   面向对象的思想:

    不关注程序执行的过程

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

在Python中,一切皆对象

  比如:类:list

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

  看list的源代码:

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

 #
发现,list也是一个对象,它有很多方法类是给别人使用的,比如list.append()类转换为对象,是实例化的过程

 总结:

  创建一个对象:

      __init__给对象添加一些属性,对象默认的名字self ,将self所指向的内存空间返回给实例化它的地方

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

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

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

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

  对象没有权利修改类中的静态变量和方法,比如self.类静态变量,如果修改了,那么就存在自己的对象空间里面

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

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

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

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

处处都是组合和交互:

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

执行输出:
<__main__.B object at 0x000002CD1677C780> # 这是b实例对象的内存地址

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

#运行结果:666  这也是组合,666是str对象,它也是交互,它是python内置的对象和自定义的对象组合

#3
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 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'>

  

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

#单继承
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个,基类,超类
子类 :别名 派生类

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

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'

#再次执行输出:
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吃药回血了

 

  总结:

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

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

    alex.eat() 实例空间找不到,取找类对象指针,指针指向父类Person,从此类中找到了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.名字的时候,不要看self当前在哪个类,要看self到底是谁的对象。

来只一张大神的图:

原文地址:https://www.cnblogs.com/double-W/p/9777416.html