面向对象的三大特征

面向对象的特征


一、继承

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

#!/usr/bin/env python
# -*- coding:utf-8 -*-


class Father1:  # 定义一个父类
    pass


class Father2:  # 定义另一个父类
    pass


class Sun1(Father1):  # 单继承
    pass


class Sun2(Father1, Father2):
    pass


# 使用__base__查看从左到又继承的第一个子类
print(Sun1.__base__)
# 使用__bases__查看所有继承的父类
print(Sun1.__bases__)
print(Sun2.__bases__)
# 如果没有指定基类(父类)会默认继承与object
print(Father1.__bases__)
'''
运行结果:
<class '__main__.Father1'>
(<class '__main__.Father1'>,)
(<class '__main__.Father1'>, <class '__main__.Father2'>)
(<class 'object'>,)
'''

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

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

  抽象分为两个层次:

    ①将张三和李四这两个对象比较像的部分抽取成类

    ②将人、猪,狗,这些比较接近的部分抽取成父类

    

 

 

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

三、继承与重用性

==========================第一部分
例如

  猫可以:吃、喝、爬树

  狗可以:吃、喝、看家

如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下:


#猫和狗有大量相同的内容
class 猫:

    def 吃(self):
        # do something

    def 喝(self):
        # do something

    def 爬树(self):
        # do something



class 狗:

    def 吃(self):
        # do something

    def 喝(self):
        # do something

    def 看家(self):
        #do something


==========================第二部分
上述代码不难看出,吃、喝是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:

  动物:吃、喝

     猫:爬树(猫继承动物的功能)

     狗:看家(狗继承动物的功能)

伪代码如下:
class 动物:

    def 吃(self):
        # do something

    def 喝(self):
        # do something

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

    def 爬树(self):
        print '喵喵叫'

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

    def 看家(self):
        print '汪汪叫'


==========================第三部分
#继承的代码实现
class Animal:

    def eat(self):
        print("%s 吃 " %self.name)

    def drink(self):
        print ("%s 喝 " %self.name)

class Cat(Animal):

    def __init__(self, name):
        self.name = name
        self.breed = ''

    def climb(self):
        print('爬树')

class Dog(Animal):

    def __init__(self, name):
        self.name = name
        self.breed=''

    def look_after_house(self):
        print('汪汪叫')


# ######### 执行 #########

c1 = Cat('小白家的小黑猫')
c1.eat()

c2 = Cat('小黑的小白猫')
c2.drink()

d1 = Dog('胖子家的小瘦狗')
d1.eat()
使用继承来解决代码重用的例子
使用继承解决重用的例子

在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时

我们不可能从头开始写一个类B,这就用到了类的继承的概念。

通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用

四、新增属性

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

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)
print(egg.attack(ha2))
print(ha2.life_value)

 

在python3中,子类执行父类的方法也可以直接用super方法.

class A:
    def hahaha(self):
        print('A')

class B(A):
    def hahaha(self):
        super().hahaha()
        #super(B,self).hahaha()
        #A.hahaha(self)
        print('B')

a = A()
b = B()
b.hahaha()
super(B,b).hahaha()
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 __init__(self,name,breed,aggressivity,life_value):
        super().__init__(name, aggressivity, life_value) #执行父类Animal的init方法
        self.breed = breed  #派生出了新的属性

    def bite(self, people):
        '''
        派生出了新的技能:狗有咬人的技能
        :param people:  
        '''
        people.life_value -= self.aggressivity

    def eat(self):
        # Animal.eat(self)
        #super().eat()
        print('from Dog')

class Person(Animal):
    '''
    人类,继承Animal
    '''
    def __init__(self,name,aggressivity, life_value,money):
        #Animal.__init__(self, name, aggressivity, life_value)
        #super(Person, self).__init__(name, aggressivity, life_value)
        super().__init__(name,aggressivity, life_value)  #执行父类的init方法
        self.money = money   #派生出了新的属性

    def attack(self, dog):
        '''
        派生出了新的技能:人有攻击的技能
        :param dog: 
        '''
        dog.life_value -= self.aggressivity

    def eat(self):
        #super().eat()
        Animal.eat(self)
        print('from Person')

egg = Person('egon',10,1000,600)
ha2 = Dog('二愣子','哈士奇',10,1000)
print(egg.name)
print(ha2.name)
egg.eat()

通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师

>>> class Teacher:
...     def __init__(self,name,gender):
...         self.name=name
...         self.gender=gender
...     def teach(self):
...         print('teaching')
... 
>>> 
>>> class Professor(Teacher):
...     pass
... 
>>> p1=Professor('egon','male')
>>> p1.teach()
teaching

五 、抽象类与接口

接口类:   

  继承有两种用途:

    一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)  

    二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能

class Alipay:
    '''
    支付宝支付
    '''
    def pay(self,money):
        print('支付宝支付了%s元'%money)

class Applepay:
    '''
    apple pay支付
    '''
    def pay(self,money):
        print('apple pay支付了%s元'%money)


def pay(payment,money):
    '''
    支付函数,总体负责支付
    对应支付的对象和要支付的金额
    '''
    payment.pay(money)


p = Alipay()
pay(p,200)
class Alipay:
    '''
    支付宝支付
    '''
    def pay(self,money):
        print('支付宝支付了%s元'%money)

class Applepay:
    '''
    apple pay支付
    '''
    def pay(self,money):
        print('apple pay支付了%s元'%money)

class Wechatpay:
    def fuqian(self,money):
        '''
        实现了pay的功能,但是名字不一样
        '''
        print('微信支付了%s元'%money)

def pay(payment,money):
    '''
    支付函数,总体负责支付
    对应支付的对象和要支付的金额
    '''
    payment.pay(money)


p = Wechatpay()
pay(p,200)   #执行会报错

接口初成:手动报异常:NotImplementedError来解决开发中遇到的问题

class Payment:
    def pay(self):
        raise NotImplementedError

class Wechatpay(Payment):
    def fuqian(self,money):
        print('微信支付了%s元'%money)


p = Wechatpay()  #这里不报错
pay(p,200)      #这里报错了

实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。

继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。

归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。

依赖倒置原则:
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程

在python中根本就没有一个叫做interface的关键字,上面的代码只是看起来像接口,其实并没有起到接口的作用,子类完全可以不用去实现接口 ,如果非要去模仿接口的概念,可以借助第三方模块:

http://pypi.python.org/pypi/zope.interface

twisted的twisted\internet\interface.py里使用zope.interface

文档https://zopeinterface.readthedocs.io/en/latest/

设计模式:https://github.com/faif/python-patterns

接口提取了一群类共同的函数,可以把接口当做一个函数的集合。

然后让子类去实现接口中的函数。

这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。

再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
为何要用接口

原文来自Eva_J的博文:初识面向对象

 

上一节       下一节   返回目录

 

posted @ 2021-01-09 16:01  贫僧敲代码  阅读(230)  评论(0)    收藏  举报