面向对象之继承

一、动静态方法

动态方法

1. 绑定给对象的方法

class Student:
    school_name = '摆烂大学'
def func1(self):
    print('看谁最能摆烂 真的好棒棒!!!')

类中直接定义的函数,都是默认给对象使用的,类名加括号,产生一个对象:

obj = Student()  # 产生一个对象

对象名.函数名,即可调用该函数,对象调用函数,默认把对象名作为第一个参数传给函数。上述类中的函数有一个参数,所以对象调用该函数不用传参。

obj.func1()   # 看谁最能摆烂 真的好棒棒!!!

如果是类调用的话,就是有几个参数传几个参数:

Student.func1(a)   # 看谁最能摆烂 真的好棒棒!!!

 

2. 绑定给类的方法:   @classmethod

class Student:
    school_name = '摆烂大学'

  @classmethod
    def func2(cls):
        print('嘿嘿嘿 猜猜我是干嘛滴',cls)

被@classmethod修饰的函数,默认绑定给类。类调用则第一个参数就是类自身,对象也可以调用并且会自动将产生该对象的类当做第一个参数传入

Student.func2()     # func2(Student)  嘿嘿嘿 猜猜我是干嘛滴  <class '__main__.Student'>
obj.func2() # func2(Student)   嘿嘿嘿 猜猜我是干嘛滴  <class '__main__.Student'>

 

静态方法:

 普普通通的函数

   @staticmethod
    def func3(a):
        print('哈哈哈 猜猜我又是什么', a)

不管谁来调用,必须传参,有几个参数传几个参数。

Student.func3(123)   # 哈哈哈 猜猜我又是什么 123

obj.func3(321)  # 哈哈哈 猜猜我又是什么 321

 

二、面向对象之继承的概念

面向对象三大特性

封装、继承、多态

  • 三者中继承最为核心(实操最多,体验最强)
  • 封装和多态略微抽象

继承的含义:

   在现实生活中继承表示人与人之间资源的从属关系
    eg:儿子继承父亲 干女儿继承干爹
   在编程世界中继承表示类与类之间资源的从属关系
    eg:类A继承类B

继承的目的:

  在现实生活中儿子继承父亲,儿子就拥有了父亲所有资源的支配权限。
  在编程世界中,类A继承类B,类A就拥有了类B中所有的数据和方法使用权限。

继承的实操:

1. 在定义类的时候类名后面可以加括号填写其他类名,意味着继承其他类

class Son(Father):
        pass

2. 在python支持多继承,括号内填写多个类名,彼此逗号隔开即可

class Son(F1, F2, F3):
         pass

上述中继承其他类的类——Son,我们称之为子类、派生类。

被继承的类——Father、F1、F2、F3,我们称之为父类、基类、超类。

我们最常用的就是子类和父类。

 

继承的本质:

继承本质应该分为两部分:
抽象: 将多个类相同的东西抽出去形成一个新的类
继承: 将多个类继承刚刚抽取出来的新的类

要找出类与类之间的继承关系,需要先抽象,再继承。抽象即总结相似之处,总结对象之间的相似之处得到类,总结类与类之间的相似之处就可以得到父类,如下图所示:

基于抽象的结果,我们就找到了继承关系:

 

例:有2个类——学生类和老师类,学生类和老师类中都要获取姓名、年龄和性别:

class Student:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def choice_course(self):
        print(f'{self.name}正在选课')


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

    def teach_course(self):
        print(f'{self.name}正在教课')

上述写法会造成代码冗余,为了节省代码的考虑,可以将相同的部分抽取出来形成一个新的类,然后让学生类和老师类都继承这个新的类。

class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender


class Student(Person):
   
    def choice_course(self):
        print(f'{self.name}正在选课')


class Teacher(Person):
  
    def teach_course(self):
        print(f'{self.name}正在教课')


stu1 = Student('jason', 18, 'male')
tea1 = Teacher('tony', 29, 'others')
  • 对象:数据与功能的结合体
  • 类(子类):多个对象相同数据和功能的结合体
  • 父类:多个类(子类)相同数据和功能结合体

    ps: 类与父类本质都是为了节省代码

 

三、名字的查找顺序

不继承情况下名字的查找顺序

        对象自身 >>> 产生对象的类

1.先从自己的名称空间中查找

2.自己没有再去产生该对象的类中查找

3.如果类中也没有 那么直接报错

class C1:
    name = 'jason'

    def func(self):
        print('from func')


obj = C1()
print(C1.name)   # jason

obj.name = 'kevin'    # 由于对象原本没有name属性,该语法会在对象名称空间中创建一个新的'键值对'

print(obj.name)    # kevin  先从自己的名称空间中查找
print(C1.name)   # jason 

 

单继承情况下名字的查找顺序

       对象自身   >>>   产生对象的类     >>>    父类

案例1:

先从自身查找:

class F1:
    name = 'jason'

class S1(F1):
    name = 'kevin'

obj = S1()
obj.name = 'oscar'

print(obj.name)    # oscar

自身没有,则从产生对象的类中查找:

class F1:
    name = 'jason'
class S1(F1):
    name = 'kevin'

obj = S1()

print(obj.name)    # kevin

自身和产生对象的类中都没有,则往上从产生对象的类的父类中查找:

class F1:
    name = 'jason'

class S1(F1):
    pwd = 123

obj = S1()

print(obj.name)    # jason

案例2:

class F3:
    name = 'jerry'

class F2(F3):
    name = 'tony'


class F1(F2):
    name = 'jason'
   

class S1(F1):
    name = 'kevin'


obj1 = S1()

obj1.name = '嘿嘿嘿'

print(obj1.name)

查找顺序:obj1 >>> S1 >>> F1 >>> F2 >>> F3

案例3:

class A1:
    def func1(self):
        print('from A1 func1')

    def func2(self):
        print('from A1 func2')
        self.func1()

class B1(A1):
    def func1(self):
        print('from B1 func1')

obj = B1()
obj.func2()

第一步:先从产生对象的类B1中查找,B1中没有func2,然后从B1的父类A1中查找,A1中有func2,所以先打印'from A1 func2';


第二步:之后self.func1(),再重新回到对象自身,先从对象自身查找,对象中没有,所以从产生对象的类B1中查找,打印'from B1 func1'

强调: 对象点名字,永远从对象自身开始一步步查找
以后在看到self.名字的时候,一定要搞清楚self指代的是哪个对象

 

多继承情况下名字的查找顺序:

1. 非菱形继承

(1)多个父类,第一个父类不再有其他父类:按照括号里的父类从左往右

class F1:
    name = 'jason' 

class F2:
    name = 'oscar'   

class F3:
    name = 'jerry'
 
class S1(F1, F2, F3):
    name = '嘿嘿嘿'    

obj = S1()

obj.name = '想干饭'

print(obj.name)

对象自身   >>>   产生对象的类     >>>    父类(从左往右)
obj >>> S1 >>> F1 >>> F2 >>> F3

 

(2) 多个父类,每个父类还有父类

    深度优先(从左往右每条道走完为止)

class A:
    # name = 'from A'
    pass
class B:
    # name = 'from B'
    pass
class C:
    # name = 'from C'
    pass
class D(A):
    # name = 'from D'
    pass
class E(B):
    # name = 'from E'
    pass
class F(C):
    # name = 'from F'
    pass

class S1(D,E,F):
    pass
obj = S1()

print(obj.name)

每条线走完再进行下一条:
obj >>> S1 >>> D >>> A>>> E >>> B >>> F >>> C

 

2. 菱形继承: 

多个父类,每个父类还有父类,且最后形成一个闭环

广度优先(最后才会找闭环的定点)

class G:
    name = 'from G'
    
class A:
    name = 'from A'
   
class B:
    name = 'from B'
   
class C:
    name = 'from C'
   
class D(A):
    name = 'from D'
   
class E(B):
    name = 'from E'
    
class F(C):
    name = 'from F'   

class S1(D,E,F):
    pass

obj = S1()

print(obj.name)

先把每条线和广度铺开,最后一条回归到闭环:
obj >>> S1 >>> D >>> A>>> E >>> B >>> F >>> C >>> G

如果实在捋不清名字的查找顺序,可以通过mro方法来判断:

print(S1.mro())

# 可以得到一个类的名字列表,列表里面类的先后顺序就是查找的先后顺序:
# [<class '__main__.S1'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.C'>, <class 'object'>]

 

四、 经典类与新式类

经典类: 不继承object或者其子类的类,要使用object或其子类,必须自己添加object。经典类已成过去式,现在基本不用

class Student(object):pass

新式类: 继承object或者其子类的类,可以默认使用object里的所有类

在python2中有经典类和新式类
在python3中只有新式类(所有类默认都继承object)

ps:以后我们在定义类的时候,如果没有其他明确的父类,也可能习惯写object兼容

 

posted @ 2022-11-07 15:40  莫~慌  阅读(299)  评论(0编辑  收藏  举报