面向对象进阶1(代码片段)

hades123 hades123     2022-12-21     386

关键词:

类的继承

什么是继承

继承就是新建一个类的方式,新建的类我们叫子类,被继承的类称为父类

在现实生活中,子女继承了父母的基因,遗产,所以子女会拥有父母的部分特征

而在python中,子类继承父类就是子类完全拥有父类所具备的属性及技能

为什么要继承

比如我现在定义一个动物类,这个动物类拥有名字,身高,体重

然后我再定义一个人类,这个人也有名字,身高,体重再加其他的属性技能

我再定义一个狗类,也有名字,身高,体重再加其他的属性技能

class Animal:
    
    def __init__(self,name,height,weight):
        self.name = name
        self.height = height
        self.weight = weight
        
        
class People:
    
    def __init__(self,name,height,weight,age):
        self.name = name
        self.height = height
        self.weight = weight
        self.age = age
        
        
class Dog:
    
    def __init__(self,name,height,weight,color):
        self.name = name
        self.height = height
        self.weight = weight
        self.color = color
  • 现在我们看上面的代码,会发现我们一直在写重复的代码,像人和狗都是属于动物类,他们在某些方面是具有动物共同特征的,所以可以继承动物类的特征
class Animal:
    
    def __init__(self,name,height,weight):
        self.name = name
        self.height = height
        self.weight = weight
        
        
class People(Animal):
    
    def __init__(self,name,height,weight,age):
        Animal.__init__(self,name,height,weight)
        self.age = age
        
        
class Dog(Animal):
    
    def __init__(self,name,height,weight,color):
        Animal.__init__(self,name,height,weight)
        self.color = color
  • 这样看来我们的代码就精简的了汗多

所以类的继承可以减少代码的冗余

对象的继承

  • 使用__bases__可以获得对象继承的类
print(People.__bases__)
print(Dog.__bases__)
(<class '__main__.Animal'>,)
(<class '__main__.Animal'>,)
  • 在Python3中如果一个类没有继承任何类,则默认继承object类

  • 在Python2中如果一个类没有继承任何类,不会继承object类

print(Animal.__bases__)
(<class 'object'>,)

类的查找顺序

  • 对象查找属性的顺序:对象自己--》对象的类--》父类--》父类
class Foo:
    def f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.f1()   # 此时的self.f1()代表了obj.f1()


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


# 对象查找属性的顺序:对象自己-》对象的类-》父类-》父类。。。
obj = Bar()  # self是obj本身,即找到Bar的f1()
obj.f2()
Foo.f2
Bar.f1

类的派生

派生:子类中新定义的属性的这个过程叫做派生,并且需要记住子类在使用派生的属性时始终以自己的为准

派生方法一

直接访问某一个函数,这种方式与继承无关

class Animal:
    
    def __init__(self,name,height,weight):
        self.name = name
        self.height = height
        self.weight = weight
        
        
class People:
    
    def __init__(self,name,height,weight,age):
        Animal.__init__(self,name,height,weight)
        self.age = age   # 派生
        
        
class Dog(Animal):
    
    def __init__(self,name,height,weight,color):
        super().__init__(name,height,weight)
        self.color = color # 派生

派生方法二

  • 严格以来继承属性查找关系

  • super()会得到一个特殊的对象,该对象就是专门用来访问父类中的属性的(按照继承的关系)

  • super().__init__(不用为self传值)

  • super的完整用法是super(自己的类名,self),在python2中需要写完整,而python3中可以简写为super()

上面的Dog就是使用了方法二来进行的

类的组合

类的组合就是某一个类的对象具有一个属性,该属性的值指向另一个类的对象

优点:用来解决类与类之间代码的冗余

# 定义课程类
class Course:
    def __init__(self,name,period,price):
        self.name = name
        self.period = period
        self.price = price
        
    def tell_info(self):
        print("""
        课程名:%s
        课程周期:%s
        课程价钱:%s"""%(self.name,self.period,self.price))
        
# 定义成员共有属性        
class OldBoyPeople:
    school = 'oldboy'
    
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
        
# 定义老师类        
class OldBoyTeacher(OldBoyPeople):
    def __init__(self,name,age,gender,level):
        super().__init__(name,age,gender)
        self.level = level

    def score(self,student,num):
        print(f'老师self.name为student.name的<student.course.name>课程打了num分')
        
# 定义学生类        
class OldBoyStudent(OldBoyPeople):
    
    def __init__(self,name,age,gender,stu_id):
        super().__init__(name,age,gender)
        self.stu_id = stu_id
        
    def choose_course(self,course):
        self.course = course
        print(f'self.name选择了<course.name>课程')
# 创建课程
python = Course('Python全栈开发','6个月',25000)
python.tell_info()
        课程名:Python全栈开发
        课程周期:6个月
        课程价钱:25000
linux = Course('Linux运维','6个月',20000)
linux.tell_info()
        课程名:Linux运维
        课程周期:6个月
        课程价钱:20000
# 创建老师
tec = OldBoyTeacher('hades',27,'male',1)
print(tec.__dict__)
'name': 'hades', 'age': 27, 'gender': 'male', 'level': 1
# 创建学生
stu = OldBoyStudent('Bonnie',16,'female','00001')
print(stu.__dict__)
'name': 'Bonnie', 'age': 16, 'gender': 'female', 'stu_id': '00001'
  • 组合
stu.choose_course(python)
stu.course.tell_info()
Bonnie选择了<Python全栈开发>课程

        课程名:Python全栈开发
        课程周期:6个月
        课程价钱:25000
tec.score(stu,98)
老师hades为Bonnie的<Python全栈开发>课程打了98分

菱形继承问题

类的分类

新式类

  • 继承了object类以及该类的子类,都是新式类
  • Python3中所有的类都是新式类

经典类

  • 没有继承了object类以及该类的子类,都是经典类
  • 只有Python2有经典类,因为Python2中的object类要手动进行添加

菱形继承问题

在python中子类可以继承多个父类,如A(B,C,D)

如果继承关系为非菱形结构,则会按照(B,C,D)的顺序依次寻找,如果找不到就会报错

菱形继承就是子类同时继承多个父类,而多个父类又同时继承了同一个父类

这样就会有一个查找顺序的问题,属性查找顺序有两种:

  • 经典类下:深度优先,也就是一条线找到底
  • 新式类:广度优先,不找多各类最后继承的同一个类,直接去找下一个父类

技术图片

技术图片

class G:
    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 D(G):
    def test(self):
        print('from D')
        
class C(F):
    def test(self):
        print('from C')
        
class B(E):
    def test(self):
        print('from B')
        
class A(B,C,D):
    def test(self):
        print('from A')
        
obj = A()

我们可以通过__mro__算法进行打印查找顺序

print(A.__mro__)
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>)
for i in A.__mro__:
    print(i)
<class '__main__.A'>
<class '__main__.B'>
<class '__main__.E'>
<class '__main__.C'>
<class '__main__.F'>
<class '__main__.D'>
<class '__main__.G'>
<class 'object'>

为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

  1. 子类会先于父类被检查

  2. 多个父类会根据它们在列表中的顺序被检查

  3. 如果对下一个类存在两个合法的选择,选择第一个父类

类的多态与多态性

多态

多态指的是一类事物具有多种形态,即一个抽象的类具有多个子类,因而多态的概念依赖于继承

  1. 数据类型有多种形态:字符串,列表,整型
  2. 动物有多种形态:人,猪,狗
class Animal:
    def communicate(self):   # 子类约定俗称的必须实现这个方法
        print('动物类会交流')
        
class People(Animal):
    def communicate(self):
        print('人类通过语言交流')
        
class Dog(Animal):
    def communicate(self):
        print('狗通过"汪汪汪"进行交流')
        
class Pig(Animal):
    def communicate(self):
        print('猪通过"哼哼哼"进行交流')
peo1 = People()
dog1 = Dog()
pig1 = Pig()

peo1.communicate()
dog1.communicate()
pig1.communicate()
人类通过语言交流
狗通过"汪汪汪"进行交流
猪通过"哼哼哼"进行交流

但是总有人不遵守规则,想搞例外,所以我们可以通过@abc.abstractmethod方法进行强制要求,如果子类没有这个功能则会报错

import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def communicate(self):   # 子类约定俗称的必须实现这个方法
        print('动物类会交流')
        
class People(Animal):
    def communicate(self):
        print('人类通过语言交流')
        
class Dog(Animal):
    def communicate(self):
        print('狗通过"汪汪汪"进行交流')
        
class Pig(Animal):
#     def communicate(self):
#         print('猪通过"哼哼哼"进行交流')
    pass 

peo2 = People()
peo2.communicate()
dog2 = Dog()
dog2.communicate()
pig2 = Pig() # 报错
人类通过语言交流
狗通过"汪汪汪"进行交流



---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-76-4c019fadd2e5> in <module>
     23 dog2 = Dog()
     24 dog2.communicate()
---> 25 pig2 = Pig() # 报错


TypeError: Can't instantiate abstract class Pig with abstract methods communicate

多态性

多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

def func(obj):
    obj.communicate()
    
func(peo1)
func(dog1)
func(pig1)
人类通过语言交流
狗通过"汪汪汪"进行交流
猪通过"哼哼哼"进行交流
func(peo2)
func(dog2)
人类通过语言交流
狗通过"汪汪汪"进行交流

综上可以说,多态性是一个接口(函数func)的多种实现(如obj.communicate())

多态性的好处

  1. 增加了程序的灵活性:以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)

  2. 增加了程序额可扩展性:通过继承Animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

python中面向对象初识到进阶(代码片段)

面向对象初识到进阶#面向对象结构:#class类名:#def__init__(self,参数1,参数2):#self.对象的属性1=参数1#self.对象的属性2=参数2##def方法名(self):pass##def方法名2(self):pass##对象名=类名(1,2)#对象就是实例,代表一个具体的东西##类名():类名+... 查看详情

面向对象——面向对象进阶(代码片段)

一、isinstance与issubclass方法1、isinstance是用来判断对象是否是某个类  isinstance(obj,class)2、issubclass是用来判断一个类是否为另一个类的子类  issubclass(Bar,Foo)1classFoo:2pass345classBar(Foo):6pass789obj=Foo()10print(isinstance(obj,Foo) 查看详情

面向对象进阶(代码片段)

...的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。 2python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物 查看详情

面向对象进阶(代码片段)

元类1.类也是对象在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立:>>>classObjectCreator(object):…pass…>>>my_object=ObjectCreator()>>>printmy_object<__main__.ObjectCreatorobjecta... 查看详情

面向对象进阶(代码片段)

   面向对象进阶(1)#设计模式#23种#单例模式#限制一个类在程序开始到结束始终只有一个对象实例#当你第一次实例化这个类的时候,就创建一个实例化的对象,但是当你之后再实例化的时候还是就用之前创建的对象#实现... 查看详情

面向对象进阶(代码片段)

...的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。2python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可 查看详情

面向对象进阶1(代码片段)

目录类的继承什么是继承为什么要继承对象的继承类的查找顺序类的派生派生方法一派生方法二类的组合菱形继承问题类的分类新式类经典类菱形继承问题类的多态与多态性多态多态性多态性的好处类的继承什么是继承继承就是... 查看详情

面向对象进阶2(代码片段)

1,反射反射用字符串的名字去操作变量#反射用字符串的名字去操作变量#hasattrgetattrdelattrsetattr#classA:#price=20#def__init__(self,name,age):#self.name=name#self.age=age#deffunc(self):#print(‘infunc‘)#@classmethod#deffunc2(cls):#print 查看详情

9.面向对象进阶(代码片段)

一.isinstance(obj,cls)和issubclass(sub,super)(1)isinstance(obj,cls)检查一个对象obj是否是一个类cls的实例(2)issubclass(sub,super)检查sub类是否是super类的派生类classFoo:#定义一个类FoopassclassBar(Foo):#定义一个类Bar继承了Foopassf1=Foo()#实例化得到f1 查看详情

面向对象进阶(代码片段)

1,组合组合在一个类中以另外一个类的对象作为数据属性,称为类的组合---->‘有‘的关系#组合在一个类中以另外一个类的对象作为数据属性,称为类的组合#圆形与圆环类#frommathimportpi#classCircle:#def__init__(self,r):#self.r=r#defperime... 查看详情

面向对象进阶(代码片段)

...的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。&n 查看详情

一个视屏的时间教你学会java面向对象进阶(代码片段)

...lo!我又来了,废话不多说,今天我们学习Java面向对象进阶!面向对象进阶分为:1.成员变量                 2.this关键字                 3.隐藏和封装                 4.static关键字... 查看详情

第14课进阶面向对象(下)(代码片段)

类之间的基本关系: 类之间的基本关系--继承: 类之间的基本关系--组合:类的表示法:  类的表示法--简化  类的表示法--改进1  类的表示法--改进2  类的表示法--改进3 类的表示法--改进4... 查看详情

面向对象进阶:反射以及内置方法(代码片段)

一、反射反射:使用字符串数据类型的变量名来获取这个变量的值  input:用户输入的如果是a,那么打印1。如果输入的是b那么就打印2.如果输入name,那么打印alex  文件:从文件中读出的字符串,想转换成变量的名字  网... 查看详情

面向对象进阶(代码片段)

面向对象进阶一、递归1.概念:方法自己调用自己,但是会在某一个时机进行第次返回。注意:递归的行为,通常要放在递归的条件之后publicclassTest publicvoidfun(inti) i--; if(i>=0) fun(i); 二、构造方法1.什么是构造方法用于初始... 查看详情

面向对象进阶--拷贝构造函数(代码片段)

在了解前可以先看下《C++有关拷贝构造函数(默认/浅/深拷贝构造函数)》拷贝构造函数1.初始化(调用构造函数初始化) 2.在使用的s1(s2)(s1,s2都是对象),这种方式都是用拷贝构造函数初始化。 3.拷贝构造函数用student(... 查看详情

面向对象进阶小结(代码片段)

面向对象进阶小结一、面向对象进阶小结面向对象最本质解决的问题就是:提供可扩展性类与对象:程序中必须现有类,再有对象类中有属性,有方法绑定方法:定义在类内部,没有装饰器装饰的方法都是对象的绑定方法,需要... 查看详情

面向对象进阶(代码片段)

isinstance和issubclassisinstance(obj,cls)检查是否obj是否是类cls的对象classFoo(object):passobj=Foo()isinstance(obj,Foo)  issubclass(sub,super)检查sub类是否是super类的派生类 classFoo(object):passclassBar(Foo):passissubcl 查看详情