python3--类的组合,初始类的继承

面向对象的组合用法

软件重用的重要方式除了继承之外还有另外一种方式,即:组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

例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  # 设置初始钱为0

    def attack(self, d):  # 人的攻击方法
        d.hp -= self.ad
        print('{}攻击了{},{}掉了{}点血'.format(self.name, d.name, d.name, self.ad))

    def pay(self):  # 充值方法
        money = int(input('请输入你要充值的金额:').strip())
        self.money += money
        print('你的余额是:{}'.format(self.money))

    def wear(self, weapon):
        # 武器
        if self.money >= weapon.price:
            # 武器类的对象作为人类对象的一个属性
            self.weapon = weapon  # 组合 给人装备了武器
            self.money -= weapon.price
            print('购买成功,你已经顺利装备了{}'.format(weapon.name))
        else:
            print('余额不足,请充值!')

    def attack_with_weapon(self, dog):
        if 'weapon' in self.__dict__:
            self.weapon.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('{}咬了{}一口,{}掉了{}点血'.format(self.name, p.name, p.name, self.ad))


class Weapon:
    def __init__(self, name, price, ad, level):  # 名字,价格,攻击,品级
        self.name = name
        self.price = price
        self.level = level
        self.ad = ad * self.level

    def skill(self, dog):
        dog.hp -= self.ad
        print('{}受到了{}的伤害,{}掉了{}点血'.format(dog.name, self.name, dog.name, self.ad))


# 武器 是人的一个属性
# 武器也是一个类
# 攻击力 价格 名字 品级
# 技能:方法
# 武器 装备

# 实例化 人,狗,武器
sam = Person('奥特曼', '不详', 1, 1)
teddy = Dog('笨笨', 'teddy', 50, 20)
futou = Weapon('斧头', 1000, 100, 1)

lst = ['攻击', '充值', '装备武器', '使用武器攻击']
while True:
    for index, value in enumerate(lst, 1):
        print(index, value)
    num = int(input('请选择操作序号 >>>'))
    if num == 1:
        sam.attack(teddy)
    elif num == 2:
        sam.pay()
    elif num == 3:
        print('装备前余额{}'.format(sam.money))
        sam.wear(futou)
        print('装备后余额{}'.format(sam.money))
    elif num == 4:
        sam.attack_with_weapon(teddy)
    else:
        print('无效的序号')

执行效果

圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长

这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积,然后在"环形类"中组合圆形的实例作为自己的属性来用

例2,上面类组合的例子不是很懂?来看个简单的吧

from math import pi  # 导入模块math中的pi
class Circle:  # 圆形面积类
    # 关联:圆形的面积公式不会变
    def __init__(self, r):
        self.r = r

    def cal_area(self):  # 计算圆面积
        return pi * self.r ** 2

    def cal_perimter(self):  # 计算圆周长
        return pi * self.r * 2

class Ring:  # 圆环
    def __init__(self, out_r, in_r):  # out_r外圆半径,in_r内圆半径

        # 组合(即和上面圆形求面积的类关联起来) 实例化一个类Circle,把外圆半径传进去
        self.out_circle = Circle(out_r)

        # 组合(即和上面圆形求面积的类关联起来) 实例化一个类Circle,把内圆半径传进去
        self.in_circle = Circle(in_r)

    def area(self):
        # 圆环面积 = 外圆面积-内圆面积
        return self.out_circle.cal_area() - self.in_circle.cal_area()

    def perimter(self):
        # 圆环周长 = 外圆周长+内圆周长
        return self.out_circle.cal_perimter() + self.in_circle.cal_perimter()

# 实例化Ring类,并传入大圆半径和小圆半径
st = Ring(6, 2)

# 打印圆环面积
print('圆环的面积为:{}'.format(st.area()))

# 打印圆环周长
print('圆环的周长为:{}'.format(st.perimter()))

执行顺序

执行结果

圆环的面积为:100.53096491487338

圆环的周长为:50.26548245743669

例3,还是类组合!

class BirthDate:  # 生日类
    def __init__(self, year, month, day):
        self.year = year  # 年
        self.month = month  # 月
        self.day = day  # 日

class Couse:  # 课程类
    def __init__(self, name, price, period):
        self.name = name  # 课程名
        self.price = price  # 课程价格
        self.period = period  # 课程班级(期)

class Teacher:  # 老师类
    def __init__(self, name, gender, birth, course):
        self.name = name  # 姓名
        self.gender = gender  # 性别
        self.birth = birth  # 生日
        self.course = course  # 课程

    def teach(self):
        print('teaching')

# 实例化 组合:一个类的对象作为另一个类对象的属性
pl = Teacher('sam', 'boy',BirthDate('2008', '12', '12'),Couse('Python3', '20000', '11期'))
print(pl.birth.year, pl.birth.month, pl.birth.day)
print(pl.course.name, pl.course.price, pl.course.period)

执行结果

2008 12 12

Python3 20000 11期

类组合什么时候需要用到?

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

初始面向对象小结

面向对象的思想

    不关注程序执行的过程

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

在python中一切皆对象

实例化的过程

创建一个对象

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

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

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

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

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

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

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

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

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

组合:对象可以作为一个对象的属性

组合和交互在python中随处可见

例1

class B:pass

class A:
    def func(self, aaa):
        print(aaa)

a = A()
b = B()
a.func(b)  # b = B()

执行结果,打印类(B)的内存地址

例2

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

a = A() # 实例化
b = B() # 实例化
a.func('666') #执行func并传入一个字符串'666'
print(a.aaa.startswith('6')) #判断'666'是否以'6'开头

执行结果

True

面向对象的三大特性

继承

多态

封装

什么是继承

继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类

python中类的继承分为:单继承和多继承

class ParentClass1:  # 定义父类
    pass

class ParentClass2:  # 定义父类
    pass

class SubClass1(ParentClass1):  # 单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1, ParentClass2):  # python支持多继承,用逗号分隔开多个继承的类
    pass

# 查看继承
# __base__只查看从左到右继承的第一个子类, __bases__则是查看所有继承的父类
print(SubClass1.__bases__)
print(SubClass2.__bases__)

# 提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类
print(ParentClass1.__bases__)
print(ParentClass2.__bases__)

执行结果

(

(

继承与抽象(先抽象再继承)

抽象即抽取类似或者说比较像的部分

抽象分为两个层次

  1. 将奥巴马和梅西这两对象比较像的部分抽取成类
  2. 将人,猪,狗这三个类比较像的部分抽取成父类

抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构

抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类

例子:

"""
示例 1
    猫: 猫喵喵,吃喝拉撒
    狗: 狗汪汪,吃喝拉撒
    如果要分别为猫和狗创建一个类,那么就需要为猫狗定义它们的动作
    代码如下
"""
class 猫:
    def 喵喵(self):
        return '喵喵'
    def 吃(self):
        return '吃'
    def 喝(self):
        return '喝'
    def 拉(self):
        return '拉'
    def 撒(self):
        return '撒'

class 狗:
    def 汪汪(self):
        return '汪汪'
    def 吃(self):
        return '吃'
    def 喝(self):
        return '喝'
    def 拉(self):
        return '拉'
    def 撒(self):
        return '撒'

# 上述代码不难看出,吃喝拉撒是猫和狗都具有的属性,而却分别为猫狗都定义了一次,代码重复性太高

如果用继承的思想,代码应该这样写,如下

class 动物:
    def 吃(self):
        return '吃'
    def 喝(self):
        return '喝'
    def 拉(self):
        return '拉'
    def 撒(self):
        return '撒'

# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 猫(动物):
    def 喵喵(self):
        return '喵喵'

# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 狗(动物):
    def 汪汪(self):
        return '汪汪'

上面代码只是一个继承的思想(严格来说,最好不要用中文)实际代码如下:

class Animal:
    def eat(self):
        print('{}吃'.format(self.name))
    def drink(self):
        print('{}喝'.format(self.name))
    def shit(self):
        print('{}拉'.format(self.name))
    def pee(self):
        print('{}撒'.format(self.name))

class Cat(Animal):
    def __init__(self, name):
        self.name = name
        self.breed = '猫'

    def cry(self):
        print('喵喵')

class Dog(Animal):
    def __init__(self, name):
        self.name = name
        self.breed = '狗'

    def cry(self):
        print('{}汪汪'.format(self.name))

cl = Cat('小白家的小黑猫')
cl.eat()
c2 = Cat('小黑的小白猫')
c2.drink()
d1 = Dog('胖子家的二哈')
d1.eat()
d2 = Dog('瘦子家的藏獒')
d2.cry()

执行结果

小白家的小黑猫吃

小黑的小白猫喝

胖子家的二哈吃

瘦子家的藏獒汪汪

在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同,不可能从头开始写一个类B,这就用到了类的继承的概念

通过继承的方式新建类B,让B继承A,B会'遗传'A的所有属性(数据属性和函数属性)

例1

class Animal:
    '''
    人和狗都是动物,所以创造一个Animal基类
    '''
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 人和狗都有自己的昵称
        self.aggressivity = aggressivity  # 人和狗都有自己的攻击力
        self.life_value = life_value  # 人和狗都有自己的生命值

    def eat(self):
        print('{} is eating'.format(self.name))

class Dog(Animal):
    pass

class Person(Animal):
    pass

egg = Person('egon', 10, 1000)
ha2 = Dog('二愣子', 50, 1000)
egg.eat()
ha2.eat()

执行结果

egon is eating

二愣子 is eating

提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置,大大降低了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就大大缩短了软件开发周期,对大型软件开发来说,意义重大

派生

当然子类也可以添加自己新的属性或者在自己这里重新定义这种属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。

class Animal:
    '''
    人和狗都是动物,所以创造一个Animal基类
    '''
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 人和狗都有自己的昵称;
        self.aggressivity = aggressivity  # 人和狗都有自己的攻击力;
        self.life_value = life_value  # 人和狗都有自己的生命值;

    def eat(self):
        print('%s is eating' % self.name)

class Dog(Animal):
    '''
    狗类,继承Animal类
    '''
    def bite(self, people):
        '''
        派生:狗有咬人的技能
        :param people:
        '''
        people.life_value -= self.aggressivity

class Person(Animal):
    '''
    人类,继承Animal
    '''
    def attack(self, dog):
        '''
        派生:人有攻击的技能
        :param dog:
        '''
        dog.life_value -= self.aggressivity

egg = Person('egon',10,1000)
ha2 = Dog('二愣子',50,1000)
print(ha2.life_value)
egg.attack(ha2)
print(ha2.life_value)

执行结果

1000

990

像ha2.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找,直到最顶级的父类

经典面试题

class Parent:
    def __init__(self):
        self.func()
    def func(self):
        print('in parent func')

class Son(Parent):
    def func(self):
        print('in son func')

class Att(Son):
    def func(self):  #如果子类中的方法与父类中的方法名字相同,执行自己的(子类的)
        print('in att func')

s = Att()

执行结果

in att func

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏mathor

动态规划(二)

12940
来自专栏落影的专栏

程序员进阶之算法练习(三十五)LeetCode专场

LeetCode上的题目是大公司面试常见的算法题,今天的目标是拿下5道算法题: 题目1是基于链表的大数加法,既考察基本数据结构的了解,又考察在处理加法过程中的边...

495160
来自专栏工科狗和生物喵

【计算机本科补全计划】链式存储线性表的一些相关操作

正文之前 不管怎么说,好歹是吧王道的第二章看完了!线性表算法写的我都快吐了,不过成果也是有的,可以写一些稍微复杂的算法了!感动,希望尽早达到老师的要求,然后去实...

33160
来自专栏软件开发

C语言 经典编程100题

一、题目 【程序1】 题目:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? ===========================...

2.8K80
来自专栏计算机视觉与深度学习基础

Leetcode 287. Find the Duplicate Number

Given an array nums containing n + 1 integers where each integer is between 1 a...

37440
来自专栏小詹同学

Leetcode打卡 | No.015 三数之和

欢迎和小詹一起定期刷leetcode,每周一和周五更新一题,每一题都吃透,欢迎一题多解,寻找最优解!这个记录帖哪怕只有一个读者,小詹也会坚持刷下去的!

14120
来自专栏Golang语言社区

Golang语言关于零值的定义

原文:https://golang.org/ref/spec#The_zero_value The 零值 当一个变量或者新值被创建时, 如果没有为其明确指定初始...

368110
来自专栏算法channel

不基于比较的基数排序原理图解

主要推送关于对算法的思考以及应用的消息。坚信学会如何思考一个算法比单纯地掌握100个知识点重要100倍。本着严谨和准确的态度,目标是撰写实用和启发性的文章,欢迎...

490130
来自专栏木宛城主

从编译原理看一个解释器的实现

『设计模式』中有一个模式可以解释特定的语法规则,它就是解释器模式(Interpreter Pattern)。不同于常见的策略模式或者是工厂模式,解释器模式在....

805100
来自专栏Linyb极客之路

UML类图(上):类、继承和实现

对于一个程序员来说,在工作的开始阶段通常都是别人把东西设计好,你来做。伴随着个人的成长,这个过程将慢慢变成自己设计一部分功能来实现,自己实现。如果要自己设计,无...

14230

扫码关注云+社区

领取腾讯云代金券