day15-面向对象基础(二)

今天整理类的组合以及类的三大特性python

1.类的组合程序员

2.类的继承算法

3.类的封装python3.x

4.类的多态app

 

开始今日份整理函数

1.类的组合测试

类与类之间,并非独立的,不少的时候在正常使用的时候都是类与类之间互相调用,因此就须要对类与类之间的关联或者是联系进行整理一下,就拿以前的英雄联盟的游戏人物之间的关系举例。ui

  1.1类的组合spa

在定义lol中不一样的英雄人物,那么正常游戏过程当中,是须要使用武器等,这个时候就用到了组合,查看代码code

class Game_role():
    def __init__(self,name,ad,hp):
        self.name = name
        self.ad = ad
        self.hp = hp
    def attack(self,obj):
        obj.hp -= self.ad
        print('{}打了{}一次,{}丢失{}点血量,如今血量为{}'.format(self.name,obj.name,obj.name,self.ad,obj.hp))
    def equip_weapon(self,obj):
        self.weapon = obj


class Weapon():
    def __init__(self,name,ad):
        self.name = name
        self.ad = ad

    def attack_weapon(self,obj1,obj2):
        obj2.hp -=(self.ad+obj1.ad)
        print('{}用{}打了{}一下,{}还剩{}点血量'.format(obj1.name,self.name,obj2.name,obj2.name,obj2.hp))

r1 = Game_role('盖伦',15,200)
r2 = Game_role('剑姬',30,140)
w1 = Weapon('三相',20)
r1.equip_weapon(w1)
r1.weapon.attack_weapon(r1,r2)
#结果
盖伦用三相打了剑姬一下,剑姬还剩105点血量

这个算最基本的类之间的组合,对于类的组合,其实就是给一个对象封装一个属性,而这个属性是另一个对象,也是最low的了。不过类的组合也是有好处的

  • 代码之间的关系更加合理
  • 类与类之间的耦合性加强

一些小的测试题

1,暴力摩托程序(完成下列需求):
1.1建立三个游戏人物,分别是:
•    苍井井,女,18,攻击力ad为20,血量200
•    东尼木木,男,20,攻击力ad为30,血量150
•    波多多,女,19,攻击力ad为50,血量80
1.2建立三个游戏武器,分别是:
•    平底锅,ad为20
•    斧子,ad为50
•    双节棍,ad为65

1.3 建立三个游戏摩托车,分别是:

•    小踏板,速度60迈
•    雅马哈,速度80迈
•    宝马,速度120迈。

完成下列需求(利用武器打人掉的血量为武器的ad + 人的ad):
(1)苍井井骑着小踏板开着60迈的车行驶在赛道上。
(2)东尼木木骑着宝马开着120迈的车行驶在赛道上。
(3)波多多骑着雅马哈开着80迈的车行驶在赛道上。
(4)苍井井赤手空拳打了波多多20滴血,波多多还剩xx血。
(5)东尼木木赤手空拳打了波多多30滴血,波多多还剩xx血。
(6)波多多利用平底锅打了苍井井一平底锅,苍井井还剩xx血。
(7)波多多利用斧子打了东尼木木一斧子,东尼木木还剩xx血。
(8)苍井井骑着宝马打了骑着小踏板的东尼木木一双节棍,东尼木木哭了,还剩xx血。(选作)
(9)波多多骑着小踏板打了骑着雅马哈的东尼木木一斧子,东尼木木哭了,还剩xx血。

  1.2类的依赖

类的依赖:给一个类的方法传了一个参数,而这个参数是一个类的对象,这种依赖关系是关系中紧密型最低的,耦合性最低的,就像上面的lol中人自身的攻击手段,他调用的是对方的对象,这个时候盖伦中有剑姬,而剑姬没有盖伦,并非像武器,武器已经彻底变成盖伦的属性而存在,紧密结合。用大象关冰箱来作一个测试。

class Elephant():
    def __init__(self,name):
        self.name = name

    def open_door(self,obj):
        print('1,2,3,开门')
        obj.open_()

    def close_door(self,obj):
        print('3,2,1,关门 ')
        obj.close_()

class Refrigerator():
    def __init__(self,name):
        self.name = name

    def open_(self):
        print('门正在打开')

    def close_(self):
        print('门正在关闭')
e1 = Elephant('神奇的大象')
r1 = Refrigerator('海尔')
e1.open_door(r1)
e1.close_door(r1)

和上面的相比,此次对象一样调用了另外的obj,但是只是使用了方法,并无让obj成为本身的属性

  1.3类的关联,聚合关系,组合关系

其实这个关系仍是组合,仍是举俩个例子吧,一个陪女朋友吃饭,一个是教师关联学校

陪女朋友吃饭

class Boy():
    def __init__(self,name,girelfriend=None):
        self.name = name
        self.girlfrind = girelfriend

    def have_dinner(self):
        if self.girlfrind == None:
            print('单身狗,吃啥吃!')
        else:
            print('%s 与%s共进晚餐'%(self.name,self.girlfrind.name))

    def get_girlfrind(self,obj):
        self.girlfrind = obj

    def lose_girlfriend(self):
        self.girlfrind = None


class Girl():
    def __init__(self,name):
        self.name = name
b1 = Boy('屌丝')#无女朋友
g1 = Girl('美女')
b2 = Boy('小哥',g1)#有娃娃亲

b1.have_dinner()
b2.have_dinner()

教师关联学校

class School():
    def __init__(self,city,address):
        self.city = city
        self.address = address
        self.teacher_list = []

class Teacher():
    def __init__(self,name,language):
        self.name = name
        self.language = language
        self.school = None

    def binding(self,li):
        for i,j in enumerate(li):
            print(i,'学校所在地址为%s'%j.city)
        choice = int(input('你所选择的学校的序号>>>').strip())
        self.school = li[choice]#教师绑定了学校
        li[choice].teacher_list.append(self)#学校绑定了教师
        print('学校绑定教师成功')


school1 =School('北京','昌平')
school2 =School('上海','张江')

li = [school1,school2]

test1 =Teacher('alex','python')
test1.binding(li)

for i in school1.teacher_list:
    print(i.name,i.language)

2.类的继承

就像人和狗都属于动物,人和狗都有年龄啊什么的共同方法,那么咱们就能够写一个公共类,狗和人继承好了

class Animal:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def eat(self):
        print('在吃东西')

class People(Animal):
    def __init__(self,name,age,sex,skin):
        #方式一
        Animal.__init__(self,name,age,sex)
        #方式二
        super().__init__(name,age,sex)
        self.skin = skin
    def eat(self):
        print('人在吃东西')
#对于狗也是同样

  2.1类的单继承

类的单继承就是字面意思,一个类只有一个父类用于继承,父类又称之为基类或者是超类,子类又称之为派生类

就像上面的例子,people类继承了animal类,people类可使用父类的属性以及方法。

查看子类的继承状况为:print(类.__base__)

# 既要执行子类方法,又要执行父类方法
# 方法一: Aniaml.__init__(self,name,sex,age)
# p1 = Person('春哥','laddboy',18,'有思想')
# print(p1.__dict__)

# 方法二:super
# p1 = Person('春哥','laddboy',18,'有思想')
# print(p1.__dict__)
# def func(self):
#     pass
# self = 3
# func(self)

# p1 = Person('春哥','laddboy',18,'有思想')
# p1.eat()

对于单继承就是这样了。

  2.2类的多继承

类的多继承能够这么理解,就是一个儿子有多个爹,而后类的继承就须要排序了。对于类的分类,主要有经典类与新式类俩种

  • 经典类:python 2.x系列中,不继承基类object,对于类的查找属于一条道走到黑,深度优先
  • 新式类:python 2.x系列中,继承基类object,python3.x系列中,默认都是新式类,查找按照c3算法

经典类的查询,如图,就是一条道走到黑

TIM图片20190118222933

TIM图片20190118222943

新式类的查询,采用c3算法

c3算法:# #mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )

TIM图片20190118223047

下面是推倒过程

# #mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
mro(K(H,I)) =[K]+merge(mro(H),mro(I),[H,I])

mro(H(E,F)) = [H] +merge(mro(E),mro(F),[E,F])
mro(E(B,C)) = [E] +merge([B,A],[C,A],[B,C])
mro(E(B,C)) = [E,B] +merge([A],[C,A],[C])

mro(E(B,C)) = [E,B,C,A]

mro(F(C,D)) = [F] +merge([C,A],[D,A],[C,D])
mro(F(C,D)) = [F,C] +merge([A],[D,A],[D])

mro(F(C,D)) = [F,C,D,A]

mro(H(E,F)) = [H] +merge([E,B,C,A],[F,C,D,A],[E,F])
mro(H(E,F)) = [H,E] +merge([B,C,A],[F,C,D,A],[F])
mro(H(E,F)) = [H,E,B] +merge([C,A],[F,C,D,A],[F])
mro(H(E,F)) = [H,E,B,F] +merge([C,A],[C,D,A])
mro(H(E,F)) = [H,E,B,F,C,] +merge([A],[D,A])

mro(H(E,F)) = [H,E,B,F,C,D,A]

mro(I(F,G)) =[I] +merge(mro(F),mro(G),[F,G])
mro(F(C,D)) = [F] +merge(mro[C],mro[D],[C,D])
mro(F(C,D)) = [F] +merge([C,A],D,A,[C,D])
mro(F(C,D)) = [F,C] +merge([A],D,A,[D])

mro(F(C,D)) = [F,C,D,A]

mro(G)=[D,A]

mro(I(F,G)) =[I] +merge([F,C,D,A],[G,D,A],[F,G])
mro(I(F,G)) =[I,F] +merge([C,D,A],[G,D,A],[G])
mro(I(F,G)) =[I,F,C] +merge([D,A],[G,D,A],[G])
mro(I(F,G)) =[I,F,C,G] +merge([D,A],[D,A])
mro(I(F,G)) =[I,F,C,G,D,A]

mro(K(H,I)) =[K]+merge([H,E,B,F,C,D,A],[I,F,C,G,D,A],[H,I])
mro(K(H,I)) =[K,H]+merge([E,B,F,C,D,A],[I,F,C,G,D,A],[I])
mro(K(H,I)) =[K,H,E,B,I]+merge([F,C,D,A],[F,C,G,D,A])
mro(K(H,I)) =[K,H,E,B,I,F,C]+merge([D,A],[G,D,A])
mro(K(H,I)) =[K,H,E,B,I,F,C,G,D,A]
#最后的执行顺序就是[K,H,E,B,I,F,C,G,D,A]

若是不用这种手算的推导式,其实能够直接调用方法查看

print(K.__mro__)

#结果和上面是一致的

super()也是按照上面的c3算法来调用的

根据上面作一些派生

子类能够添加本身新的属性或者在本身这里定义这些属性(不会影响父类),须要注意的是,一旦从新定义了与父类相同的名字的属性,会以本身为准。

在子类中,新建的重名函数类型,在编辑函数功能时,有可能调用父类的相同名字的函数功能时,应该用普通函数方法同样,所以即便是self也应该传值进去,例如类名.func(参数)

继承原理:

  • 子类会先于父类检查
  • 多个父类会根据他们在列表中的顺序被检查
  • 若是对下一个类存在俩个合法选择,选择第一个父类

3.类的封装

在讲类的封装的前提须要说一下类方法的隐藏

  3.1隐藏

类的结构中能够分静态属性以及动态属性,按照另一个角度,能够划分为公有,私有属性

class Boss():
    name = 'alex'
    __secretary =['女一','男二','野模']#私有静态属性

    def __init__(self,username,password):
        self.username = username
        self.__password = password#私有对象属性

    def func(self):
        print('老板在办公')

    def __func1(self):
        print('老板在和秘书办公')#私有方法

    def print(self):
        print(self.__secretary)
        self.__func1()
b1 = Boss('alex',123)

print(b1.name)
#print(b1.__secretary)
b1.print()

咱们会发现,按照之前的同样的调用方法,根本得不到这些私有属性以及私有方法,若是咱们在类内定义一个函数专门用于调用这些私有方法,才能看到这些私有属性,单独的去调用这些私有属性只会报错没有这些方法,咱们会发现只有类的内部才能调用,类的外部以及派生类根本没法调用属性。

不过并非彻底没法调用,当咱们看类的__dict__方法时,咱们会发现,python对类内部这些私有方法属性进行了包装

{'_Boss__secretary': ['女一', '男二', '野模'], '__doc__': None, '__init__': <function Boss.__init__ at 0x016246A8>, 'name': 'alex', '_Boss__func1': <function Boss.__func1 at 0x016D40C0>, 'print': <function Boss.print at 0x016D4078>, '__weakref__': <attribute '__weakref__' of 'Boss' objects>, '__dict__': <attribute '__dict__' of 'Boss' objects>, 'func': <function Boss.func at 0x016D4030>, '__module__': '__main__'}

咱们采用_boss__password这样的形式仍是能够调用的。不过通常不建议这么使用,变形后的注意问题

  • 这种隐藏并非真正意思上的隐藏
  • 变形的过程发生在类的定义发生一次,定义后的操做,不会变形
  • 在继承中,若是父类不想子类覆盖子类本身的方法,能够将方法定义为私有,子类就不会继承到这些方法

  3.2封装的意义

封装的意义为

  • 封装数据:明确区分类内外的,控制外部对隐藏属性的操做
  • 封装方法:隔离复杂度,只须要提供接口给其余人使用便可

  3.3propetry函数

propetry也叫作类的私有属性

class Market():
    def __init__(self,name,prix,discount):
        self.name = name
        self.__prix = prix
        self.__discount = discount

    @property#设定为私有方法
    def price(self):
        return self.__prix*self.__discount

    @price.setter#对私有方法的内容进行修改
    def price(self,new_price):
        self.__prix = new_price

    @price.deleter#对私有方法的内容进行删除,不过不是常常用
    def price(self):
        del self.__prix

m1 = Market('洗发水',45,0.8)
print(m1.price)
m1.price = 48
print(m1.price)
del m1.price
print(m1.price)

将一个类的方法修改成私有方法,这样就假装成属性,虽然在代码逻辑上没有什么提升,可是会让执行起来更合理一些,

4.类的多态

多态指的是一类事物有多种形态,好比:动物有多种形态:人,狗,猪

import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #动物的形态之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')

  4.1类的多态性

多态性是指在不考虑实例类型的状况下使用实例,多态性分为静态多态性和动态多态性

静态多态性:如任何类型均可以用运算符+进行运算

动态多态性:以下

peo=People()
dog=Dog()
pig=Pig()

#peo、dog、pig都是动物,只要是动物确定有talk方法
#因而咱们能够不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()

#更进一步,咱们能够定义一个统一的接口来使用
def func(obj):
    obj.talk()

其实你们从上面多态性的例子能够看出,咱们并无增长什么新的知识,也就是说python自己就是支持多态性的,这么作的好处是什么呢?

  • 增长了程序的灵活性:以不变应万变,不论对象变幻无穷,使用者都是同一种形式去调用,如func(animal)
  • 增长了程序额可扩展性:经过继承animal类建立了一个新的类,使用者无需更改本身的代码,仍是用func(animal)去调用  

  4.2鸭子方法

Python崇尚鸭子类型,即‘若是看起来像、叫声像并且走起路来像鸭子,那么它就是鸭子’

python程序员一般根据这种行为来编写程序。例如,若是想编写现有对象的自定义版本,能够继承该对象

也能够建立一个外观和行为像,但与它无任何关系的全新对象,后者一般用于保存程序组件的松耦合度。

例1:利用标准库中定义的各类‘与文件相似’的对象,尽管这些对象的工做方式像文件,但他们没有继承内置文件对象的方法

#两者都像鸭子,两者看起来都像文件,于是就能够当文件同样去用
class TxtFile:
    def read(self):
        pass

    def write(self):
        pass

class DiskFile:
    def read(self):
        pass
    def write(self):
        pass