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