面向对象(3)

面向对象

image

1、面向对象三大特性

# 面向对象的三大特性
'''
    1、封装:
        什么是封装:
            所谓的封装就是将重复的代码封装到一起,
        封装的目的:
            封装的目的是每个对象在使用的时候可以减少冗余的代码量
        如何封装:
            对象封装之前说了就是把一个个对象封装成一个类,
            在产生对象的时候只需要调用类就可以了,而在类里定义了每个对象都共有的属性和方法,
            调用的时候简单了很多
    2、继承:
        什么是继承:
            所谓的继承就是另外一种定义类的方式,继承的思想就是一种什么是什么的概念,
            被继承的类称为父类或基类,继承类称为父类的子类或者派生类
        继承的目的:
            参照封装的目的,继承则是将拥有同一功能的类都继承于或者说是遗传于一个父类,
            其目的也是为了减少每个子类里重复的代码,继承就是子类拥有了父类的一些功能和属性,
            使得每个子类在使用这种功能或属性的时候,可以直接从父类中找到
        如何继承:
            继承的基本结构
                class School:
                    pass
                class Student(School):
                    pass
                class Teacher(School):
                    pass
            继承的语法结构就是类名()括号里写上父类的名字,即该类是括号里类的子类或者派生类
    3、多态性:
        多态性又是什么?????
'''

image

2、继承

在python中,继承又可分为两种:
1、单继承
单继承是指,只有一个父类或基类,即只有一个爹
2、多继承
多继承只有在python里面有,是指有多个父类或基类,即有多个爹
继承的核心思想:什么'是'什么的思维习惯
在提到类时,就必然会产生属性的查找顺序这一概念,那么在继承里,属性的查找顺序又是什么样的?

# 单继承里的属性查找
# 示例一:
class Foo:
    def f(self):
        print('from Foo')

    def f3(self):
        print('from Foo')


class F1(Foo):
    def f1(self):
        print('from F1')


class F2(F1):
    def f3(self):
        print('from F2')


class F(F2):
    def f3(self):
        print('from F')


obj1 = F()
obj1.f3()  # from F
obj1.f1()  # from F1
obj1.f()  # from Foo   from F2


# 示例二:
class Foo:
    def __F(self):  # _Foo__F
        print('from Foo')

    def F(self):
        self.__F()  # _Foo__F


class F1(Foo):
    def f1(self):
        print('from F1')


class F2(F1):
    def f2(self):
        print('from F2')


class F(F2):
    def __F(self):  # _F__F
        print('from F')
obj1 = F()
obj1.F()  # from Foo
'''
从以上两个实例中,我们可以得出在单继承中属性查找的顺序为:
    对象本身>>>产生对象的类>>>父类>>>父类的父类>>>>.....
其实我们可以通过类的内置方法__base__方法来查看当前类的父类是谁
'''
class F1():
    def f1(self):
        print('from F1')

class F2(F1):
    def f2(self):
        print('from F2')

print(F2.__base__)  # <class '__main__.F1'>

# 多继承里的属性查找
'''
在讲多继承之前,先引进两个额外的概念:新生类与经典类
    经典类:没有继承object类或者object子类等
    新式类:继承了object类或者object的子类的类等
在python2中,有经典类和新式类之分,在python3中一律为新式类,这是因为,在python3中,默认所有的类都是继承于object类
    示例:
    python2中:
        class F:  # 经典类
            pass
        class F(object):  # 新式类
            pass
    python3中:
    表现方式一:class F:  # 新式类  ==>> 表现方式二: class F(object):
                pass                                    pass
    在python3中是默认所有的类皆继承于object类,至于object类是什么类,这是python内部定义的一个类,不必深究,
    但是python3里的新生类如果想要在python3里正常显示,必须要指明继承于object类,
    在python2里如果不指明,则默认为经典类,无继承的父类
'''
# 以python3为例
# 示例一:
class A:
    def text(self):
        print('from A')
class B(A):
    # def text(self):
    #     print('from B')
    pass
class C(B):
    # def text(self):
    #     print('from C')
    pass
class D(A):
    # def text(self):
    #     print('from D')
    pass
class E(D):
    def text(self):
        print('from E')
class F(C, E):
    pass
print(F.__bases__)  # (<class '__main__.C'>, <class '__main__.E'>)
obj = F()
obj.text()  # from E
# 示例二:
class B:
    # def text(self):
    #     print('from B')
    pass
class C(B):
    # def text(self):
    #     print('from C')
    pass
class D:
    def text(self):
        print('from D')
class E(D):
    # def text(self):
    #     print('from E')
    pass
class F(C, E):
    pass
print(F.__bases__)  # (<class '__main__.C'>, <class '__main__.E'>)
obj = F()
obj.text()  # from D


'''
对于多继承属性的查找顺序,要引入另外两个概念:
    多属性查找遵循mro列表,mro列表就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表
    非菱形查找:只适用于经典类里的多继承属性查找,先从最左边的父类开始查找,找不到再从后面的开始找,
              所谓的非菱形也就是说,所有的父类或者父类的父类皆没有一个共同的父类,而python3中所有的类都默认是object的子类,
              因此,python3里的多继承查找皆不是非菱形查找
    菱形查找:适用于新式类里的多继承属性查找,所谓的菱形查找,恰恰相反,即要查找的属性的父类皆有一个共同的父类,且在python2和python3中
            菱形查找所遵循的原则也有说不同,这里就先下个结论:
                python2中:左边第一个开始查找,第一次就直接一条道走到黑,找到父类的共同父类,
                如果找不到,则在下面的查找中,不再从共同的父类中查找,这种方法又叫深度优先查找
                python3中:从左边第一个开始查找,不会从共同的父类中查找,
                当找到最后一个父类还没找到时,再从父类共同的类里面查找,这种方法又叫广度优先查找
'''
'''
牵扯到继承时,就引出了另外一个概念:
    在子类派生的新方法中如何重用父类的功能????
'''

# 先从单继承开始说
# 方式一:指名道姓的找 ==>> 不依赖于继承
class School:
    School = 'old_boy'

    def __init__(self, name, age):
        self.name = name
        self.age = age


class Student(School):
    def __init__(self, name, age):
        School.__init__(self, name, age)  # 指名道姓的调用类里的方法


class Teacher(School):
    def __init__(self, name, age, salary, level):
        School.__init__(self, name, age)  # 指名道姓的调用类里的方法
        self.salary = salary
        self.level = level


obj1 = School('jason', 18)
print(obj1.name)  # jason
obj2 = Teacher('json', 20, 3000, 10)
print(obj2.name)  # json
'''
这种指名道姓调用的方法严格来说,并不属于继承里独有的属性查找方式,
下面介绍另外一种调用的方式:这里再调用两个概念:
    super()方法
    mro列表
    先从实例中慢慢摸索
'''

# 方式二:使用super()方法 ==>>严格依赖于继承关系
class School:
    School = 'old_boy'

    def __init__(self, name, age):
        self.name = name
        self.age = age


class Student(School):
    def __init__(self, name, age):
        # 在python2中:super(当前类,对象).__init__(self, name, age)
        # 在python3中,super不需要填参数
        super().__init__(name, age)


class Teacher(School):
    def __init__(self, name, age, salary, level):
        # 在python2中:super(当前类,对象).__init__(self, name, age)
        # 在python3中,super不需要填参数
        super().__init__(name, age)
        self.salary = salary
        self.level = level

obj = Student('jason', 18)
print(obj.__dict__)  # {'name': 'jason', 'age': 18}
obj2 = Teacher('egon', 20, 3000, 10)
print(obj2.__dict__)  # {'name': 'egon', 'age': 20, 'salary': 3000, 'level': 10}
'''
在单继承里使用super()方法调用类里的方法时,super()方法会返回一个特殊的对象,
该对象参照发起属性查找的类的mro列表,从当前类的下一个类(父类)里进行查找
'''
# 多继承下如何调用父类里的方法
'''
在利用单继承来调用父类里的方法时,我们可以用指名道姓的方式,也可以用super()方法直接调用,
但是在多继承里调用时,指名道姓这种查找顺序就不再试用了,因为会涉及到父类的查找顺序,而在
    多继承里,我们使用super()这种方法,遵循mro列表里的类查找顺序
'''
# 示例一:
class B(object):
    # def text(self):
    #     pass
    pass
class C(B):
    # def text(self):
    #     print('from C')
    pass
class D(object):
    def text(self):
        print('from D')
class E(D):
    # def text(self):
    #     print('from E')
    pass
class F(C, E):
    pass
#

obj = F()
print(F.mro())  # [<class '__main__.F'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class 'object'>]
obj.text()  # from D

# 示例二:
class A(object):
    def text(self):
        print('from A')
class B(A):
    def text(self):
        super().text()
class C(B):
    # def text(self):
    #     print('from C')
    pass
class D(A):
    # def text(self):
    #     print('from D')
    pass
class E(D):
    # def text(self):
    #     print('from E')
    pass
class F(C, E):
    pass

obj = F()
print(F.mro())  # [<class '__main__.F'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>]
obj.text()  # from A
'''
这就提到了我们之前说的,在python3中,多继承查找顺序,采用广度优先查找顺序,
    而类的mro列表,其实就是多继承在查找属性时的顺序
    而super()方法我们也可以在这里定义:
        super()方法会产生一个特殊的对象,该对象会参照发起属性查找的类(发起者)的mro列表,
        从当前类的后面的类里查找,不能再次返回找,找不到即报错
'''
'''
在讲多继承之前,我们提到了只有python中才存在有多继承的问题,那么多继承是不是就是完美的?
    答案当然不是,没有任何一种方法是完美的,那么多继承的利与弊又是什么?
        利:可以继承多个父类的属性与功能,最大限度的重用父类的功能
        弊:1、打破了人正常的思维逻辑:什么'是'什么
            2、在进行属性查找和子类里派生的新方法重用父类里的方法时,查找起来很麻烦,且很费时间,
               使得代码的可读性变差,进而导致扩展性变差,那么如何避免这种情况?
    下面就讲述一下多继承正确打开的方式之:Mixins机制      
'''


# Mixin机制核心:在多继承的背景下,尽可能的提升多继承的可读性
# 父类就是一个标识,即子类是什么,让子类有一个归属
# 示例:
class Vehicle:
    pass


class FlyableMixin:
    def fly(self):
        pass


class Plane(FlyableMixin, Vehicle):  # 意思就是混合功能,即给飞机混合进去飞行的功能,实际上飞机的父类只有一个,还是交通工具类
    pass


class Car(Vehicle):
    pass
'''
Mixin方法实际上只是混入了一些功能,意即临时混入的一些功能,并不是指是谁的父类
使用Mixins多继承时要注意以下几点:
    1、只能存放某种功能,而非物品,python对于mixin类的一般定义为mixin结尾或者able结尾
    2、责任必须单一,如果有多个功能,则写多个mixin类,一个类可以继承多个mixin类,为了遵循
    继承里什么什么'是'什么的思维逻辑,只能继承一个标识其归属的父类
    3、不依赖于子类的实现,是一个独立的类,和子类是没有关系的,只是附加给子类的一种功能
    4、子类即便没有mixin类,依旧可以正常实现,只是不具备了mixin类里的功能而已
但是Mixins的本质还是多继承,写的越多,依旧会造成代码的可读性变差
'''

image

3、多态

  • 什么是多态:
    多态指的是同一种事物有多种形态
    现实生活中:
    eg:人,狗,猪皆属于动物类,这里就可以说,动物的形态可以是人,狗或者猪,这就是我们所说的多态
    编程里:
    在程序里,如何表现出这一概念?
# 多态实际上就是在继承的背景之下衍生出的一种表现形式
class Animal:
    pass
class People(Animal):
    pass
class Dog(Animal):
    pass
class Pig(Animal):
    pass

image

  • 为什么要有多态:多态会带来什么样的特性,==>>多态性
    多态性指的是在不考虑对象的类型的情况下直接使用对象

class Animal:  # 统一所有子类的方法
    def say(self):
        print('动物基本的发声频率...',end=' ')
class People(Animal):
    def say(self):
        super().say()
        print('嘤嘤嘤嘤嘤嘤')
class Dog(Animal):
    def say(self):
        super().say()
        print('汪汪汪')
class Pig(Animal):
    def say(self):
        super().say()
        print('哼哼哼')
obj1 = People()
obj2 = Dog()
obj3 = Pig()
obj1.say()  # 动物基本的发声频率... 嘤嘤嘤嘤嘤嘤
obj2.say()  # 动物基本的发声频率... 汪汪汪
obj3.say()  # 动物基本的发声频率... 哼哼哼

def animal_say(item):
    return item.say()
animal_say(obj1)
'''
在使用多态性时,我们可以不考虑对象的类型的情况下,直接使用对象,
这完全归功于父类统一了所有子类的方法,在定义完以后,只需要将方法封装成一个接口,
只要在调用的时候传入相应的对象就可以实现想要的功能,实际上是方便了使用,
而在我们平常就一直在用多态
'''
# 以len方法为例
# 其实len方法就是每一个数据类型都具有一个__len__方法,然后将该方法封装成一个统一的接口
# 只需要填入相应的数据类型就可以计算出长度
def my_len(item):
    return item.__len__()


print(my_len('jason'))  # 5

'''
但是python不推崇这种写法,python所推崇的是一种叫做鸭子类型(只要会跳,会鸭子叫,长得像鸭子,就是鸭子)的写法,
即,只要不同的类都具有相似的功能,就可以把他们归为一个类,而不再使用继承父类的方式
'''
# 示例
class People:
    def say(self):
        print('嘤嘤嘤嘤嘤嘤')
class Dog:
    def say(self):
        print('汪汪汪')
class Pig:
    def say(self):
        print('哼哼哼')
obj1 = People()
obj2 = Dog()
obj3 = Pig()
# 将这几个类共同的方法封装成一个接口
def say(item):
    return item.say()


say(obj1)  # 嘤嘤嘤嘤嘤嘤
say(obj2)  # 汪汪汪
say(obj3)  # 哼哼哼

'''
如果非要用父类来规范子类所具有的一些方法时,可以通过导入一个模块来进行规范
'''
import abc  # ==>> abstract抽象
# 抽象类只能被继承,不能被实例化
class Animal(metaclass=abc.ABCMeta ):  # 统一所有子类的方法
    @abc.abstractmethod  # 抽象方法,子类在继承时,子类里必须要有这种方法,且方法名必须一样,不然就会报错
    def say(self):
        print('动物基本的发声频率...',end=' ')
class People(Animal):
    def shuo(self):
        super().say()
        print('嘤嘤嘤嘤嘤嘤')
class Dog(Animal):
    # def say(self):
    #     super().say()
    #     print('汪汪汪')
    pass
class Pig(Animal):
    def say(self):
        super().say()
        print('哼哼哼')
obj1 = People()
obj2 = Dog()
obj2.say()  # 报错
obj1.say()  # 报错

image

posted @ 2021-12-06 21:43  PyLy  阅读(44)  评论(0)    收藏  举报

念两句诗

入我相思门,知我相思苦,长相思兮长相忆,短相思兮无穷极。
【唐代】李白