Python面向对象的三大特性

面向对象的三大特性

面向对象的三大特征分别为:封装、继承、多态

一、面向对象三大特性之封装

封装指的就是把数据与功能都整合到一起,听起来是不是很熟悉。
没错,我们之前所说的‘整合’二字其实就是封装的通俗说法

什么是封装

在程序设计中,封装(Encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其含义是其他程序无法调用。

要了解封装,就离不开’私有化’,就是将类或者是函数中的某些属性限制在某一个区域之内,外部无法调用。

为什么要封装

封装数据的主要原因是:保护隐私(把不想别人知道的东西封装起来)

二、面向对象三大特性之继承

三大特性中继承最为核心(实操最多)

1.什么是继承?

什么是继承?
    继承就是一种新建类的方式,新建出来的类我们称为是'子类或者叫派生类',
    被继承的类我们称为是’父类或者叫基类‘
    # 子类可以继承父类的所有属性和方法!

通俗的说:
    在现实生活中继承表示人与人之间资源的从属关系
    eg:在生活中儿子继承了父亲就拥有了父亲所有资源的支配权限
    
    在编程世界中继承表示类与类之间资源的从属关系
    eg:在编程时间中类A继承类B就拥有了类B的所有的数据和方法使用权限

2.为什么要用继承?

类解决了什么问题:对象与对象之间的代码冗余问题
继承解决了什么问题:类与类之间的代码冗余问题

3.怎么样继承?

怎么样继承?
    经典类:没有继承object类的子子孙孙类都是经典类(深度优化算法)
        格式写法:
            class  类名:
                类体代码
    新式类:继承了object类的子子孙孙类的都是新式类(广度优先算法)
        格式写法:
            class 类名(object):
                类体代码
        '在python3中类名后面小括号中不写object也是会继承一个object类'
        
'在有python2中才会区分经典类和新式类,在python3中只有新式类'
也就是说python3中默认的类都是继承了object类,
所以在python3中就没有了经典类和新式类的说法了

=========================================================
'''
继承中有单继承和多继承
    单继承:一个类只继承了一个类
        格式:
            class 类名(A):
                类体代码
        # 单继承就是括号内填写一个类名
        
    多继承:一个类继承两个或两个以上的类
        格式:
            class 类名(A,B):
                类体代码
        # 多继承就是括号内填写多个类名,彼此用逗号隔开
'''
class Parent1:  # 经典类写法
    pass

class Parent2(object):  # 新式类写法
    pass


'''Sub1类继承了Parent1的类,所以Sub1就称为子类,Parent1就称为父类'''
class Sub1(Parent1):
    pass

'''多继承的类:Sub2就是子类,Parent1和Parent2都是父类'''
class Sub2(Parent1,Parent2):
    pass

'查看继承的个数的方法'
print(Sub2.__bases__)  # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)
print(Sub1.__bases__)  # (<class '__main__.Parent1'>,)
3.1简单单继承实操
'''简单单继承实操'''
class A:
    name = 'chen'

    def game(self):
        print('game game game')

    @classmethod  # 使类直接调用,无需传参
    def game1(cls):
        print('game game')

class B(A):
    pass

'1.通过子类直接调用用父类中的数据和方法'
print(B.name)  # chen
print(B.game)  # <function A.game at 0x0000022C9307A1F0>
'因为默认是对象方法,所以用类直接调用需要传参'
B.game(1)  # game game game
'所以可以用装饰器classmethod直接用子类调用父类中的对象方法,无需传参'
B.game1()  # game game

print('===============')

# 2.通过子类实例化对象调用父类中的数据和方法
obj = B()
print(obj.name)  # chen
obj.game()  # game game game
obj.game1()  # game game
3.2简单多继承实操
'''简单多继承实操'''
class A1:
    print('from A1')
    name1 = 'a1'

class A2:
    print('from A2')
    name2 = 'a2'

class A3:
    print('from A3')
    name3 = 'a3'

class B(A1, A2, A3):
    pass

B()
print(B.name1)  # a1
print(B.name2)  # a2
print(B.name3)  # a3

4.继承的本质

继承的本质:
    本质1:对象:数据和功能的结合体
        类(子类):多个对象相同数据和功能的结合体
        父类:多个类(子类)相同数据和功能的结合体
    >>>>:类与父类本质都是为了节省代码
    
    本质2:继承的本质一个分为两部分
        抽象:将多个类相同的东西抽出去形成一个新的类
        继承:将多个类继承刚刚抽出去的新的类


'''第一步:定义两个独立的类'''
class Student():
    school = 'HF'
    def __init__(self,name,age,gender):
        self.name =name
        self.age = age
        self.gender = gender

    def choose_course(self):
        print(f"{self.name}正在选课")


class Teacher():
    school = 'HF'
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender

    def choose_course(self):
        print(f"{self.name}正在授课")


stu = Student('jack', 18, 'male')
teacher1 = Teacher('chen', 25, 'male')
print(stu.__dict__)  # {'name': 'jack', 'age': 18, 'gender': 'male'}
print(stu.name)  # jack
stu.choose_course()  # jack正在选课
print(teacher1.__dict__)  # {'name': 'chen', 'age': 25, 'gender': 'male'}
print(teacher1.name)  # chen
teacher1.choose_course()  # chen正在授课


'''
从上可以看出,两个类中有很多相同的数据,会显得代码很冗余
我们可以用抽象的方式把两个类中
相同的数据抽出去形成一个新的类,然后将新类分别给这两个类继承,
这样就可以解决类与类之间代码冗余的问题
'''
'''第二步:用继承的方式把类冗余的代码抽到新的类中'''
'''
可以把学生类和老师类中的相同的代码,放到一个新类中,让新类作它们的父类
'''
class People():
    school = 'HF'
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

class Student(People):  # 学生类
    def __init__(self, name, age, gender,course=None):
        # 因为把子类中冗余的方法数据放到父类中了,可以通过指名道姓的方法调用
        # People.__init__(self,name,age,gender)  # 指名道姓的方式
        '''知名道姓的调用方式,不依赖于继承'''

        '''
        另一种就是依赖于继承
        通过super关键字和mro列表
        '''
        # super(Student, self).__init__(self, name, age, gender) # 这种是python2中的写法
        super().__init__(name, age, gender)  # 这种是python3的写法
        '这种方式依赖于继承,如果去掉继承的父类就会报错'
        if course is None:
            self.course = []
        self.course = course

    def choose_course(self):
        print(f"{self.name}正在选课{self.course}")


class Teacher(People):  # 老师类
    def __init__(self, name, age, gender, level):
        # 通过知名道姓的方式直接调用
        # (因为是类调用绑定对象的方法所以得对应的传递参数)
        # People.__init__(self, name, age, gender)  # 指名道姓的方式
        '''另一种依靠继承的方式'''
        # super(Teacher, self).__init__(self, name, age, gender) # 这种是python2中的写法
        super().__init__(name, age, gender)  # 这是python3中的写法
        self.level = level

    def choose_course(self):
        print(f"{self.name}正在授课")


# 可以通过子类取出父类的数据和方法
print(Student.school)  # HF
print(Teacher.school)  # HF
stu = Student('jack', 18, 'male', 'python')
teacher1 = Teacher('chen', 25, 'male', 10)
print(stu.__dict__) #{'name': 'jack', 'age': 18, 'gender': 'male', 'course': 'python'}
print(teacher1.__dict__) #{'name': 'chen', 'age': 25, 'gender': 'male', 'level': 10}
print(stu.name)  # jack
print(teacher1.name)  # chen
stu.choose_course()  # jack正在选课python
teacher1.choose_course()  # chen正在授课
'''通过类的方法mro()或者类的属性__mro__可以输出这个类的继承层次结构'''
print(Student.mro())
print(Teacher.mro())
4.1、super和mro的使用
'''super和mro的使用'''
class A:
    __name = 'hello'
    def test(self):
        super().test()

class B:
    name = 'world'
    def test(self):
        print('from B')

class C(A,B):
    pass

c = C()
c.test()  # from B
'''
mro列表是通过一个C3算法得出来的,我们无需明白底层原理,只需要知道每一个类的mro列表到底是什么,
然后按照这个列表去查找就行了
'''
'''在打印mro列表的时候,一定是从起始类开始'''
print(C.mro())  # 返回的列表形式 
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
print(C.__mro__)  # 返回的元组形式
'''每个类都会有自己的mro列表'''
print(A.mro()) #[<class '__main__.A'>, <class 'object'>]
'''如果以后继承类中出现了super关键字,不清楚顺序的,可以先用mro列表打印起始类的所有的'''

5.单继承下的属性查找

'''单继承与多继承下的属性查找'''
	class Foo():
    def f1(self):
        print('from Foo.f1')

    def f2(self):
        print('from Foo.f2')

class Bar(Foo):
    def f1(self):
        print('from Bar.f1')

'''
单继承下的属性查找:先从对象自己名称空间中查找,如果没找到则到产生这个对象的类中查找
如果还没有,才会去继承的父类中查找
>>>>::对象本身->产生对象的类->继承的父类
'''
b = Bar()
b.f1()  # from Bar.f1
b.f2()  # from Foo.f2


'''父类如果不想让子类覆盖自己的方法,可以采用双下滑线开头的方式设置为私有化'''
class Foo():
    def __f1(self):  # 变形为_Foo__f1
        print('from Foo.f1')

    def f2(self):
        print('from Foo.f2')
        self.__f1()  # 变形为self._Foo__f1.因而只会调用自己所在的类中的方法

class Bar(Foo):
    def __f1(self):
        print('from Bar.f1')

b = Bar()
# print(Bar.__dict__)
# print(Bar.mro())
b.f2()

6.多继承下的属性查找

大多数面向对象语言都不支持多继承,而在Python中,一个子类可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的Diamond Problem菱形问题(或称钻石问题,有时候也被称为‘死亡钻石’),菱形起始就是对下面这种继承结构的形象比喻

在这里插入图片描述

	'''多继承下的属性查找'''
'''
多继承的查找,涉及到菱形查找和非菱形查找
    非菱形查找:就是一条一条分支的查找
        # 可以通过mro列表查看到多继承的查找顺序
    
    菱形查找:
        新式类 广度优先查找
        经典类 深度优先查找
    
    # 经典类是在python2中的,所以多继承下的属性查找,如果按照属性找不到就按照深度优先
    # 在python3当中都是新式类,所以多继承下的属性查找,如果按照属性找不到就按照广度优先 
'''
6.1非菱形查找

多继承结构为非菱形结构,此时,会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性

在这里插入图片描述

	'''非菱形查找'''
class E:
    # def test(self):
    #     print('from E')
    pass

class F:
    # def test(self):
    #     print('from F')
    pass

class B(E):
    # def test(self):
    #     print('from B')
    pass

class C(F):
    # def test(self):
    #     print('from C')
    pass

class D:
    def test(self):
        print('from D')


class A(B, C, D):
    # def test(self):
    #     print('from A')
    pass

obj=A()
'''非菱形查找从对象的名称空间中开始,没有就往产生对象的类中查找,没有就再往父类中查找,最后在object中查找'''
'''如果不知道怎么查找,可以直接用mro关键字,从起始类,就可以看到起始类的查找顺序'''
print(A.mro())
# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>,
# <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]
obj.test()
6.2菱形查找

如果继承关系为菱形结构,那么经典类与新式类会有不同MRO,分别对应属性的两种查找方式:深度优先和广度优先

当类是经典类时,在多继承情况下,在要查找的属性不存在时,会按照深度优先的方式查找下去

在这里插入图片描述

	'''经典类:深度查找'''
class G:  # 在python2中,未继承object的类及其子类,都是经典类
   def test(self):
       print('from G')

class E(G):
    def test(self):
        print('from E')

class F(G):
    def test(self):
        print('from F')

class B(E):
    def test(self):
        print('from B')

class C(F):
    def test(self):
        print('from C')

class D(G):
    def test(self):
        print('from D')

class A(B,C,D):
    # def test(self):
    #     print('from A')
    pass

obj = A()
obj.test()  # 如上图,查找顺序为:obj->A->B->E->G->C->F->D->object
# 可依次注释上述类中的方法test来进行验证,注意请在python2.x中进行测试

当类是新式类时,多继承情况下,在要查找属性不存在时,会按照广度优先的方式查找下去

在这里插入图片描述

	'''新式类:广度查找'''
class G(object):
   def test(self):
       print('from G')

class E(G):
    def test(self):
        print('from E')

class F(G):
    def test(self):
        print('from F')

class B(E):
    def test(self):
        print('from B')

class C(F):
    def test(self):
        print('from C')

class D(G):
    def test(self):
        print('from D')

class A(B,C,D):
    # def test(self):
    #     print('from A')
    pass

obj = A()
obj.test() # 如上图,查找顺序为:obj->A->B->E->C->F->D->G->object
# 可依次注释上述类中的方法test来进行验证

7.继承的实现原理

python到底是如何实现继承的呢? 对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表,如下

D.mro() # 新式类内置了mro方法可以查看线性列表的内容,经典类没有该内置该方法
[<class '__main__.D'>, <class '__main__.B'>, 
<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类

所以obj.test()的查找顺序是,先从对象obj本身的属性里找方法test,没有找到,则参照属性查找的发起者(即obj)所处类D的MRO列表来依次检索,首先在类D中未找到,然后再B中找到方法test

1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,
2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去,

三、面向对象三大特性之多态

什么是多态呢?
	多态就是一种事物的形态(:液态、气态、固态)
	一种事物有多中形态 但是相同的功能应该有相同的名字
"""之所以说人、狗、猪是动物,是因为他们都具备动物的特征,speak功能"""
"""多态带来的特性:在不考虑对象类型的情况下,直接调用对象的方法或者属性"""
import abc # abstract class 抽象类   具体的Specific

class Animal(metaclass=abc.ABCMeta): # 把animal类变成了抽象类
    """父类中得方法不是为了实现逻辑的,实现功能的,而是单纯的为了限制子类的行为"""
    @abc.abstractmethod # 把抽象类中得方法变成抽象方法, 它不实现具体的功能,就是单纯的为了限制子类中的方法
    def speak(self):
        pass
    @abc.abstractmethod
    def jiao(self):
        pass

"""抽象类和普通类有什么区别? 抽象类只能够被继承、不能够被实例化"""
# Animal() # Can't instantiate abstract class Animal with abstract methods speak
"""怎么限制子类People类必须有speak功能? 我们可以在父类中来限制子类的行为,其实就是限制子类中必须有某些方法"""


"""Python崇尚的是鸭子类型"""
"""鸭子类型就是更多的关注的是对象的行为,而不是对象的类型"""
class People(Animal):
    def speak(self):pass
        # print('from People.speak')
    def jiao(self):
        pass

class Dog(Animal):
    def speak(self):
        pass


class Pig(Animal):
    def speak(self):
        pass


def animal(obj):
    return obj.speak()

animal(obj)
animal(obj1)
animal(obj2)


"""面试题:请举出Python中使用多态的例子:len"""
len('hello')
len([1,2,3,4])
len((1,2,3,4))

def len(obj):
    return obj.__len__
len('helloworld')
len([1,2,3,4])
len((1,2,3,4))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值