面向对象---继承/封装/多肽

1. 继承

比较官方的说法就是:

  • 继承(英语:inheritance)是面向对象软件技术当中的一个概念。
  • 如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。
  • 继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。
  • 在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,
  • 即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。

1.1 案例

class Person:
    def __init__(self,name,sex,age):
        self.name = name
        self.age = age
        self.sex = sex
class Cat:
    def __init__(self,name,sex,age):
        self.name = name
        self.age = age
        self.sex = sex
class Dog:
    def __init__(self,name,sex,age):
        self.name = name
        self.age = age
        self.sex = sex
# 继承的用法:
class Aniaml(object):
    def __init__(self,name,sex,age):
            self.name = name
            self.age = age
            self.sex = sex
class Person(Aniaml):
    pass
class Cat(Aniaml):
    pass
class Dog(Aniaml):
    pass

1.2 继承的有点也是显而易见的:

  • 增加了类的耦合性(耦合性不宜多,宜精)。
  • 减少了重复代码。
  • 使得代码更加规范化,合理化。

1.3 继承的分类

1.3.1 就向上面的例子:

  • Aminal 叫做父类,基类,超类。
  • Person Cat Dog: 子类,派生类。
  • 继承:可以分单继承,多继承

1.3.2 这里需要补充一下python中类的种类(继承需要

  • python3x版本中只有一种类。
  • python3中使用的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object。

1.3.3 在python2x版本中存在两种类.:

  • 一个叫经典类. 在python2.2之前. 一直使用的是经典类. 经典类在基类的根如果什么都不写.
  • 一个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。

1.3.4 经典与新式的查找顺序

  • 经典深度优先,一条路走到黑,没找到,折回上一级继续找
  • 新式广度优先,从左往右找

1.4 单继承

1.4.1 类名,对象执行父类方法

class Aniaml(object):
    type_name = '动物类'
    def __init__(self,name,sex,age):
            self.name = name
            self.age = age
            self.sex = sex
    def eat(self):
        print(self)
        print('吃东西')
class Person(Aniaml):
    pass
class Cat(Aniaml):
    pass
class Dog(Aniaml):
    pass
# 类名:
print(Person.type_name)  # 可以调用父类的属性,方法。
Person.eat(111)
print(Person.type_name)
# 对象:
# 实例化对象
p1 = Person('春哥','男',18)
print(p1.__dict__)
# 对象执行类的父类的属性,方法。
print(p1.type_name)
p1.type_name = '666'
print(p1)
p1.eat()

1.4.2 执行顺序

class Aniaml(object):
    type_name = '动物类'
    def __init__(self,name,sex,age):
            self.name = name
            self.age = age
            self.sex = sex
    def eat(self):
        print(self)
        print('吃东西')
class Person(Aniaml):
    def eat(self):
        print('%s 吃饭'%self.name)
class Cat(Aniaml):
    pass
class Dog(Aniaml):
    pass
p1 = Person('barry','男',18)
# 实例化对象时必须执行__init__方法,类中没有,从父类找,父类没有,从object类中找。
p1.eat()
# 先要执行自己类中的eat方法,自己类没有才能执行父类中的方法。

1.4.3 同时执行类以及父类方法

  • 方法一:

如果想执行父类的func方法,这个方法并且子类中夜用,那么就在子类的方法中写上:

父类.func(对象,其他参数)

class Aniaml(object):
    type_name = '动物类'
    def __init__(self,name,sex,age):
            self.name = name
            self.age = age
            self.sex = sex
    def eat(self):
        print('吃东西')
class Person(Aniaml):
    def __init__(self,name,sex,age,mind):
        '''
        self = p1
        name = '春哥'
        sex = 'laddboy'
        age = 18
        mind = '有思想'
        '''
        # Aniaml.__init__(self,name,sex,age)  # 方法一
        self.mind = mind
    def eat(self):
        super().eat()
        print('%s 吃饭'%self.name)
class Cat(Aniaml):
    pass
class Dog(Aniaml):
    pass
# 方法一: Aniaml.__init__(self,name,sex,age)
# p1 = Person('春哥','laddboy',18,'有思想')
# print(p1.__dict__)
# 对于方法一如果不理解:
# def func(self):
#     print(self)
# self = 3
# func(self)
  • 方法二(推荐)

利用super,super().func(参数)

class Aniaml(object):
    type_name = '动物类'
    def __init__(self,name,sex,age):
            self.name = name
            self.age = age
            self.sex = sex
    def eat(self):
        print('吃东西')
class Person(Aniaml):
    def __init__(self,name,sex,age,mind):
        '''
        self = p1
        name = '春哥'
        sex = 'laddboy'
        age = 18
        mind = '有思想'
        '''
        # super(Person,self).__init__(name,sex,age)  # 方法二
        super().__init__(name,sex,age)  # 方法二
        self.mind = mind
    def eat(self):
        super().eat()
        print('%s 吃饭'%self.name)
class Cat(Aniaml):
    pass
class Dog(Aniaml):
    pass
# p1 = Person('春哥','laddboy',18,'有思想')
# print(p1.__dict__)

1.5 新式类多继承

class ShenXian: # 神仙
    def fei(self):
        print("神仙都会飞")
class Monkey: # 猴
    def chitao(self):
        print("猴子喜欢吃桃子")
class SunWukong(ShenXian, Monkey): # 孙悟空是神仙, 同时也是一只猴
    pass
sxz = SunWukong() # 孙悟空
sxz.chitao() # 会吃桃子
sxz.fei() # 会飞

1.6 经典类的多继承

  • 虽然在python3中已经不存在经典类了. 但是经典类的MRO最好还是学一学. 这是一种树形结构遍历的一个最直接的案例.
  • 在python的继承体系中. 我们可以把类与类继承关系化成一个树形结构的图. 来, 上代码:
class A:
    pass
class B(A):
    pass
class C(A):
    pass
class D(B, C):
    pass
class E:
    pass
class F(D, E):
    pass
class G(F, D):
    pass
class H:
    pass
class Foo(H, G):
    pass

对付这种mro画图就可以: 

继承关系图已经有了. 那如何进行查找呢? 记住一个原则. 在经典类中采用的是深度优先,遍历方案. 什么是深度优先. 就是一条路走到头. 然后再回来. 继续找下一个.

  • 图中每个圈都是准备要送鸡蛋的住址. 箭头和黑线表示线路. 那送鸡蛋的顺序告诉你入口在最下面R. 并且必须从左往右送. 那怎么送呢?
  • 如图. 肯定是按照123456这样的顺序来送. 那这样的顺序就叫深度优先遍历.
  • 而如果是142356呢? 这种被称为⼴度优先遍历. 好了. 深度优先就说这么多. 那
  • 么上面那个图怎么找的呢? MRO是什么呢? 很简单. 记住. 从头开始. 从左往右. 一条路跑到头, 然后回头. 继续一条路跑到头. 就是经典类的MRO算法.
  • 类的MRO: Foo-> H -> G -> F -> E -> D -> B -> A -> C.

 2. 封装

  • 把很多数据封装到一个对象中. 把固定功能的代码封装到一个代码块, 函数, 对象, 打包成模块. 这都属于封装的思想.
  • 具体的情况具体分析. 比如. 你写了一个很牛B的函数. 那这个也可以被称为封装. 在面向对象思想中. 是把一些看似无关紧要的内容组合到一起统一进行存储和使用. 这就是封装.
  • 封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
  • 所以,在使用面向对象的封装特性时,需要:

        将内容封装到某处,

        从某处调用被封装的内容

2.1 第一步:将内容封装到某处

  • self 是一个形式参数,当执行 obj1 = Foo(‘wupeiqi’, 18 ) 时,self 等于 obj1
  • ​ 当执行 obj2 = Foo(‘alex’, 78 ) 时,self 等于 obj2
  • 所以,内容其实被封装到了对象 obj1 和 obj2 中,每个对象中都有 name 和 age 属性,在内存里类似于下图来保存。

2.2 第二步:从某处调用被封装的内容

2.2.1 调用被封装的内容时,有两种情况:

  • 通过对象直接调用
  • 通过self间接调用

2.2.2 通过对象直接调用被封装的内容

  • 上图展示了对象 obj1 和 obj2 在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名
class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
obj1 = Foo('wupeiqi', 18)
print obj1.name    # 直接调用obj1对象的name属性
print obj1.age     # 直接调用obj1对象的age属性
obj2 = Foo('alex', 73)
print obj2.name    # 直接调用obj2对象的name属性
print obj2.age     # 直接调用obj2对象的age属性

2.2.3 通过self间接调用被封装的内容

  • 执行类中的方法时,需要通过self间接调用被封装的内容
class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def detail(self):
        print self.name
        print self.age
obj1 = Foo('wupeiqi', 18)
obj1.detail()  # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18
obj2 = Foo('alex', 73)
obj2.detail()  # Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 alex ; self.age 是 78

 2.3 总结

  • 综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。
  • 子类可以自动拥有父类中除了私有属性外的其他所有内容.
  • 说白了, 儿子可以随便用爹的东西. 但是朋友们, 一定要认清楚一个事情. 必须先有爹, 后有儿子. 顺序不能乱,
  • 在python中实现继承非常简单. 在声明类的时候, 在类名后面添加一个小括号,就可以完成继承关系.
  • 那么什么情况可以使用继承呢? 单纯的从代码层面上来看. 两个类具有相同的功能或者特征的时候. 可以采用继承的形式. 提取一个父类, 这个父类中编写着两个类相同的部分. 然后两个类分别取继承这个类就可以了. 这样写的好处是我们可以避免写很多重复的功能和代码.
  • 如果从语义中去分析的话. 会简单很多. 如果语境中出现了x是一种y.
  • 这时, y是一种泛化的概念. x比y更加具体. 那这时x就是y的子类.
  • 比如. 猫是一种动物. 猫继承动物. 动物能动. 猫也能动. 这时猫在创建的时候就有了动物的”动”这个属性. 再比如, 白骨精是一个妖怪. 妖怪天生就有一个比较不好的功能叫”吃人”, 白骨精一出生就知道如何”吃人”. 此时 白骨精继承妖精.

3 多态

  • 同一个对象, 多种形态. 这个在python中其实是很不容易说明白的. 因为我们一直在用. 只是没有具体的说.
  • 比如. 我们创建一个变量a = 10 , 我们知道此时a是整数类型. 但是我们可以通过程序让a = “alex”, 这时, a又变成了字符串类型. 这是我们都知道的.
  • 但是, 我要告诉你的是. 这个就是多态性. 同一个变量a可以是多种形态。
  • 多态,同一个对象,多种形态。python默认支持多态。
# 在java或者c#定义变量或者给函数传值必须定义数据类型,否则就报错。
def func(int a):
    print('a必须是数字')
# 而类似于python这种弱定义类语言,a可以是任意形态(str,int,object等等)。
def func(a):
    print('a是什么都可以')
# 再比如:
class F1:
    pass
class S1(F1):
    def show(self):
        print 'S1.show'
class S2(F1):
    def show(self):
        print 'S2.show'
# 由于在Java或C#中定义函数参数时,必须指定参数的类型
# 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
# 而实际传入的参数是:S1对象和S2对象
def Func(F1 obj):
"""Func函数需要接收一个F1类型或者F1子类的类型"""
    print obj.show()
s1_obj = S1()
Func(s1_obj)  # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show
s2_obj = S2()
Func(s2_obj)  # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
Python伪代码实现Java或C  # 的多态

3.2 鸭子类型

python中有一句谚语说的好,你看起来像鸭子,那么你就是鸭子。
对于代码上的解释其实很简答:
class A:
    def f1(self):
        print('in A f1')
    def f2(self):
        print('in A f2')
class B:
    def f1(self):
        print('in A f1')
    def f2(self):
        print('in A f2')
obj = A()
obj.f1()
obj.f2()
obj2 = B()
obj2.f1()
obj2.f2()
# A 和 B两个类完全没有耦合性,但是在某种意义上他们却统一了一个标准。
# 对相同的功能设定了相同的名字,这样方便开发,这两个方法就可以互成为鸭子类型。
# 这样的例子比比皆是:str  tuple list 都有 index方法,这就是统一了规范。
# str bytes 等等 这就是互称为鸭子类型。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骑猪去兜风z1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值