python3-面对对象-三大特征(继承、多态、封装)

本文深入探讨了Python3中面向对象编程的三大核心概念:继承,允许子类继承父类的属性和方法;多态,允许多个类定义相同的方法,实现不同的行为;封装,通过隐藏内部实现细节,提供简洁的公共接口。通过实例解析,帮助读者理解并掌握这些概念。
摘要由CSDN通过智能技术生成
'''
继承:
    一种创造新类的方法,新建的类可以继承一个或多个父类的属性
    父类又可以称为 基类或超类;子类又称为派生类
    
继承的目的: 为了减少类与类之间的代码冗余

python中继承的特点:
    1.可以遗传、重用父类的属性
    2.一个子类可以继承多个父类
    3.继承背景下,python中的类分为两种:新式类、经典类
        新式类:但凡继承了object的类,以及该类的子类、子子类
            在python3中一个类即便是没有显式的继承任何类,则默认继承object类。
            即,python3中所有的类都是新式类
        经典类:没有继承object
            在python2中取分新式、经典类
'''


class Parent1:
    pass


# 在python3中写入object继承,为了能在Python2中兼容
class Parent2(object):
    pass


class Sub1(Parent1):
    pass


class Sub2(Parent1, Parent2):
    pass


# 查看类的父类(基类),访问属性__bases__
print(Parent1.__bases__)
print(Parent2.__bases__)  # 默认继承 object类
print(Sub1.__bases__)
print(Sub2.__bases__)
# 判断是否是另一个类的子类,使用内置方法 issubclass()
print(issubclass(Sub1,Parent1))

# 判断对象是否是特定类的实例,使用方法isinstance()
s = Sub1()
# 对象是所属类的实例
print(isinstance(s,Sub1))
# 对象是所属类的父类的实例
print(isinstance(s,Parent1))
# 对象是object类的实例
print(isinstance(s,object))

# 获悉对象属于哪个类,使用属性__class__
print(s.__class__)


'''
子类如何重用父类?
    方式一、指名道姓的应用某一个类中的函数
        (1、与继承无关 2、且访问的函数,没有自动传值的效果)
    方式二、使用内置方法 super()
            返回一个特殊的对象,该对象用来专门访问父类中的属性 **完全参照mro列表**
        python2中:类中使用super(当前类名,self)
        python3中:super()
        (1、严格依赖mro列表 2、访问的是绑定方法,有自动传值的效果)
        
在继承背景下,属性的查找优先级:
   1.单继承背景: 对象内部 --》 对象的类 --》 父类 --》 父父类 …… --》object
   2.多继承背景:
    1)若一个子类继承多个分支,非菱形结构(多个分支没有共同继承经典类、新式类(非object的类))
        对象内部 --》 对象的类 --》父类(从左往右,各分支查找) --》 object
    2)菱形继承问题
        -1 py3,py2 多父类最终指向同一个类,此类为新式类:
            广度优先查找:各父类分支查找,在最后一个父类查找完毕,再去顶级类查找
        -2 py2 多父类最终指向同一个类,此类为经典类:
            深度优先查找:在第一个父类分支查找,第一次就去找顶级类查找
    
python查找原理:
    使用C3线性算法,计算出一个方法解析顺序(MRO),此列表是一个简单的所有积累的线性顺序列表。
    如:>>> f.mro()
        [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, 
        <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
        
    所有父类的MRO列表并遵循如下三条准则:
        1.子类会先于父类被检查
        2.多个父类会根据它们在列表中的顺序被检查
        3.如果对下一个类存在两个合法的选择,选择第一个父类

'''

# 利用继承解决代码冗余
class CollegePeople:
    school = 'college'

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


class Stu(CollegePeople):
    def __init__(self, name, age, sex, score):
        # 指名道姓的应用父类中的函数
       CollegePeople.__init__(self, name, age, sex)
         self.score = score

    def choose_course(self, y):
        print('%s choose course' % self.name, y)

class Teacher(CollegePeople):

    def __init__(self, name, age, sex, level):
        # CollegePeople.__init__(self, name, age, sex)
        # 使用内置方法 super()重用父类
        super().__init__( name, age, sex)
        self.level = level

    def score(self, stu, num):
        stu.score = num
s1 = Stu('name',18,'male',99)
print(s1.__dict__)
t1 = Teacher('teacher',28,'female',10)
print(t1.__dict__)


方式一、指名道姓的应用某一个类中的函数
 (1、与继承无关 2、且访问的函数,没有自动传值的效果)

class A:
    def __init__(self, a):
        self.a = a


class B:
    def __init__(self,b):
        self.b = b


class C(A):
    def __init__(self, a,b,c):
        A.__init__(self,a)
        B.__init__(self,b)
        self.c = c


c1 = C('a', 'b','c')
print(c1.__dict__)
# -------------------------------

# 单继承下的查找顺序

class Pra:
    def f1(self):
        print('P --> f1')
    def f2(self):
        print('P ---->  f2')
        self.f1()
class Fo(Pra):
    def f1(self):
        print('F ---> f1')
a = Fo()
a.f2()

# -----------------------------------------------
'''
组合:某一个对象,拥有一个属性,其值来自于另一个类的对象
    class Foo:
        xxx = 222
    class Bar:
        yyy = 111
    obj = Foo()
    b = Bar()
    
    obj.attr = Bar()
    obj.a = b
    
    obj.xxxx #调用Foo内属性
    obj.attr.yyyy #调用Bar内属性

使用组合的目的:
    通过为某一个对象添加属性的方式,间接将两个类进行关联,减少类与类代码冗余
'''

# 使用组合减少代码冗余

class CollegePeople():
    school = 'college'
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex


class CollegeStudent(CollegePeople):
    def __init__(self, name, age, sex, score=0):
        super().__init__(name, age, sex)
        self.score = score
        self.courses = []

    def tell_all_course(self):
        print(('学生[%s]选修的课程如下' % self.name).center(50, '='))
        for obj in self.courses:
            obj.tell_course()
        print('=' * 60)


class CollegeTeacher(CollegePeople):
    def __init__(self, name, age, sex, level):
        super().__init__(name, age, sex)
        self.level = level
        self.courses = []

    def tell_all_course(self):
        print(('老师[%s]教授的课程如下' % self.name).center(50, '*'))
        for obj in self.courses:
            obj.tell_course()
        print('*' * 70)

# 创建课程
class CollegeCourse:
    def __init__(self, c_name, c_price, c_period):
        self.c_name = c_name
        self.c_price = c_price
        self.c_period = c_period
    def tell_course(self):
        print('course:' + self.c_name, self.c_price, self.c_period)


python = CollegeCourse('python全栈开发', 1900, '5mons')
linux = CollegeCourse('linux架构师', 900, '3mons')

# 学生添加课程
stu1 = CollegeStudent('STU1', 38, 'male')
stu1.courses.append(python)
stu1.courses.append(linux)
stu1.tell_all_course()

# 教师添加课程
tea1 = CollegeTeacher('TEA1',18,'male',10)
tea1.courses.append(linux)
tea1.tell_all_course()


# -------------------------------------------
'''
多态:一类事物存在多种形态
    例:动物存在多种形态<人,狗,猫>

多态性:在多态的背景下(统一),不考虑实例类型的情况下使用实例
            即不同的实例,调用相同的方法,实现不同的结果
    例:不同种类的动物,都可以调用吃喝拉撒的方法,但是结果不同。
    
多态性分为静态多态性和动态多态性

抽象类:一个特殊的类,只能被继承,不能被实例化。
      若类是一堆对象中抽取相同的内容而来,抽象类则是从一堆类中抽取相同的内容而来,内容包括了属性和方法。

抽象类和接口:抽象类的本质还是累,指的是一组类的相似性,包括属性和方法,而接口只强调函数属性的相似性。

abc模块:使子类强制遵循父类的方法,即子类必须重写父类方法
    注:父类只能用来建立规范,不能用来实例化,无需实现内部方法
例:
class Animal(metaclass = abc.ABCMeta):

    @abc.abstractmethod
    def speak(self):
        pass

鸭子类型:如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子
python程序员通常根据这种行为来编写程序。
例如,linux对硬盘的操作看起来像文件操作,那么就用操作文件的方法操作硬盘
'''

'''
封装:明确的取分内外,封装的属性可以直接在内部调用,而不能被外界使用。
隐藏方式:定义的属性以__开头。例:__name
    注意:__xxx__为内置属性而不是影藏
    
隐藏存储的__dict__总结:
    1.__开头的属性,实现的隐藏仅仅只是一种语法意义上的变形,并不会真正限制类外部的访问
    2.改变形操作只在类定义阶段检测语法时发生一次,类定义阶段结束后(对象、类的添加修改等操作)定义的属性不会变形
    3.属性添加__开头可以防止子类覆盖父类的方法。且父类内只会查找到自身的隐藏属性


封装数据属性目的:将数据属性隐藏,类外部无法直接操作属性,通过类内部接口间接操作
                接口实现各种逻辑,控制外部使用接口对属性的操作
封装方法的目的:隔离复杂度
'''


class Foo:
    __x = 111  # _Foo__x
    __y = 222  # _Foo__y

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

    def __func(self):  # _Foo__func
        pass

    def get_info(self):
        print(self.__name, self.__age, self.__x)
        # print(self._Foo__name,self._Foo__age,self._Foo__x)


# 外界无法调用隐藏属性
print(Foo.__x)
print(Foo.__func)
# 查看类名称空间,发现属性名称在定义检测阶段修改
print(Foo.__dict__)
# 仍然可以调用修改后名字的属性,不建议使用
print(Foo._Foo__x)
print(Foo._Foo__y)

# 在类定义检测阶段之后定义的属性,不被修改就放入空间
Foo.__z=333
print(Foo.__dict__)
print(Foo.__z)

# 对象中也会修改对应的隐藏属性
obj=Foo('123',18)
print(obj.__dict__)
print(obj.__name)
print(obj.__age)
obj.get_info()
# 对象创建之后,新定义的属性,不会被修改
obj.__sex='male'
print(obj.__dict__)
print(obj.__sex)
# -------------------------------------------
# 封装数据属性的目的:
#   通过接口,实现控制再间接修改私有类型

class People:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def get_info(self):
        print(self.__name, self.__age)

    def set_info(self, name, age):
        if type(name) is not str:
            print('name type error!')
            return
        if type(age) is not int:
            print('age type error')
            return
        self.__name = name
        self.__age = age


# 创建对象
obj = People('lin',12)
obj.get_info()

# 使用接口作为控制用户输入的判断,达到不影响隐藏属性的作用
obj.set_info(18,'LIN')
obj.set_info('LIN','18')

# 成功修改对象
obj.set_info('LIN',18)
obj.get_info()

# --------------------
'''
特性:通过存取方法存取的属性称之为特性
property装饰器:使类内的函数属性伪装成数据属性
           被property装是的函数可以使用@xxx.setter和@xxx.deleter进行set、del操作
注:仅可使用与新式类
'''


class People1:
    def __init__(self, name, weight, height):
        self.__name = name
        self.__weight = weight
        self.__height = height

    @property
    def bmi(self):
        return self.__weight / (self.__height ** 2)

    # 凡是被property装饰过的函数,则可以使用函数名.setter作为装饰器,让私有属性可以被修改
    @bmi.setter
    def bmi(self, obj):
        self.__name = obj

    @bmi.deleter
    def bmi(self):
        del self.__name


p1 = People1('p1', 75, 1.85)
print(p1.bmi)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值