面向对象:动静态方法、面向对象之继承的概念、继承的本质、名字的查找顺序、经典类与新式类、派生方法

一、动静态方法

昨天我们学习了类中可以存放函数并且公共功能和一些函数独有的功能其实是放在一起的,我们在给对象调用独有方法的时候其实就是把这个方法给他传一份过去,变成独有的。

那说了这么多,其实类中的函数是可以设置调用条件的——也就是我们所说的动静态方法。

动态方法

分成两种方法,第一种就是我们昨天学习的,单纯的函数,第二种是使用@classmethod语法糖装饰的函数。

单纯的函数我们发现对象调用可以少写一个参数(调用者的参数可以不写),类来自己调用的时候需要给他写上参数。

第二种方式就是无论对象还是类,你来用都可以少写一个参数。

class Student:
    school_name = '摆烂大学'
    # 1.类中直接定义函数 默认绑定给对象 类调用有几个参数传几个 对象调用第一个参数就是对象自身
    def func1(self):
        print('看谁最能摆烂 真的好棒棒!!!')

    # 2.被@classmethod修饰的函数 默认绑定给类 类调用第一个参数就是类自身 对象也可以调用并且会自动将产生该对象的类当做第一个参数传入
    @classmethod
    def func2(cls):
        print('嘿嘿嘿 猜猜我是干嘛滴', cls)

    # 3.普普通通的函数 无论是类还是对象调用 都必须自己手动传参
    @staticmethod
    def func3(a):
        print('哈哈哈 猜猜我又是什么', a)


obj = Student()
# 1.绑定给对象的方法
obj.func1()
Student.func1(123)
Student.func1(Student)
# 其实就是必须得有个参数,也可以随便给
# 2.绑定给类的方法
Student.func2()  # fun2(Student)
obj.func2()  # func2(Student)
print(Student)

静态方法

使用时代码接上面

静态方法就相当于python不帮我们做任何偷懒操作了,所有的参数都需要传。

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

面向对象三大特性

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

1.继承的含义

  • 继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,新建的类可称为子类或派生类,父类又可称为基类或超类。
  • 根据继承的数量可以分为单继承和多继承。
'''单继承和多继承简单定义'''
class Parent1:
    pass
class Parent2:
    pass
class Sub1(Parent1): #单继承
    pass
print(Sub1.__bases__)  # 查看自己的父类---->(<class '__main__.Parent1'>,)

class Sub2(Parent1,Parent2): # 多继承
    pass
print(Sub2.__bases__)    # 查看自己的父类---->(<class '__main__.Parent1'>, <class '__main__.Parent2'>)

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

2.继承的目的

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

3.继承解决了什么问题

  • 类解决对象与对象之间代码冗余的问题,子类可以遗传父类的属性
  • 继承解决的是类与类之间代码冗余的问题
  • object类丰富了代码的功能

4.多继承的优缺点

  • 优点:子类可以同时遗传多个父类的属性,最大限度的重用代码
  • 缺点:违反人的思维习惯,一个人有两个爹,代码的可读性会变差,不建议使用多继承.
  • 继承表达的是一种“是”什么的关系

5.继承的实操

	class Son(Father):
        pass
 	1.在定义类的时候类名后面可以加括号填写其他类名 意味着继承其他类
 	2.在python支持多继承 括号内填写多个类名彼此逗号隔开即可
    class Son(F1, F2, F3):
        pass

1.继承其他类的类 Son
​ 我们称之为子类、派生类
​ 2.被继承的类 Father F1 F2 F3
​ 我们称之为父类、基类、超类
​ ps:我们最常用的就是子类和父类

三、继承的本质

继承中类与对象的作用

对象:数据与功能的结合体
类(子类):多个对象相同数据和功能的结合体
父类:多个类(子类)相同数据和功能结合体
ps:类与父类本质都是为了节省代码

继承本质应该分为两部分

抽象:

将多个类相同的东西抽出去形成一个新的类

img

继承:

将多个类继承刚刚抽取出来的新的类

img

抽象即总结相似之处,总结对象之间的相似之处得到类,总结类与类之间的相似之处就可以得到父类,如下图所示:

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

基于上图我们可以看出类与类之间的继承指的是什么’是’什么的关系(比如人类,猪类,猴类都是动物类)。子类可以继承/遗传父类所有的属性,因而继承可以用来解决类与类之间的代码重用性问题。比如我们按照定义Student类的方式再定义一个Teacher类:

class Teacher:
    school='清华大学'
    
    def __init__(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age
    
    def teach(self):
        print('%s is teaching' %self.name)

类Teacher与Student之间存在重复的代码,老师与学生都是人类,所以我们可以得出如下继承关系,实现代码重用:

class People:
    school='清华大学'
    
    def __init__(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age
    
class Student(People):
    def choose(self):
        print('%s is choosing a course' %self.name)
 
class Teacher(People):
    def teach(self):
        print('%s is teaching' %self.name)

Teacher类内并没有定义__init__方法,但是会从父类中找到__init__,因而仍然可以正常实例化,如下:

>>> teacher1=Teacher('lili','male',18)
>>> teacher1.school,teacher1.name,teacher1.sex,teacher1.age
('清华大学', 'lili', 'male', 18)

四、名字的查找顺序

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

1.不继承情况下名字的查找顺序
	class C1:
    name = 'jason'

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

    obj = C1()
    # print(C1.name)  # 类肯定找的自己的
    obj.name = '你迷了吗'  # 由于对象原本没有name属性 该语法会在对象名称空间中创建一个新的'键值对'
    print(obj.__dict__)
    print(obj.name)  # 你迷了吗
    print(C1.name)
    """
    对象查找名字的顺序
        1.先从自己的名称空间中查找
        2.自己没有再去产生该对象的类中查找
        3.如果类中也没有 那么直接报错
    对象自身 >>>    产生对象的类
    """

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

2.单继承情况下名字的查找顺序
	 # class F1:
    #     name = 'jason'
    # class S1(F1):
    #     name = 'kevin'
    # obj = S1()
    # obj.name = 'oscar'
    # print(obj.name)
    '''
    对象自身   >>>   产生对象的类     >>>    父类
    '''
    # class F3:
    #     # name = 'jerry'
    #     pass
    #
    # class F2(F3):
    #     # name = 'tony'
    #     pass
    #
    # class F1(F2):
    #     # name = 'jason'
    #     pass
    #
    # class S1(F1):
    #     # name = 'kevin'
    #     pass
    # obj1 = S1()
    # # obj1.name = '嘿嘿嘿'
    # print(obj1.name)

例题:
    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()
  • 查找顺序讲解

先在obj的名称空间中查找,找不到就去B1中查找,因为B1的父系是A1,所以最后会在A1中找到func2。
func2中又出现了一格self.func1(),这时候我们应该把他拿出来看,这行代码就相当于在外面另外写了一个obj.func1(),因此这里调用的是B1中的func1.

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

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

广度优先:

img

深度优先:

img

特殊情况

  • 菱形继承

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

当我们继承的顺序如下图形成闭环的时候,会先根据广度优先顺序查找,最后才回去最上面的闭环处查找。

img

  • 非菱形继承

    深度优先(从左往右每条道走完为止,也就是普通的查找顺序)

img

ps:mro()方法可以直接获取名字的查找顺序

对象自身 >>> 产生对象的类 >>> 父类(从左往右)

 	# class F1:
    #     # name = 'jason'
    #     pass
    #
    #
    # class F2:
    #     # name = 'oscar'
    #     pass
    #
    # class F3:
    #     # name = 'jerry'
    #     pass
    #
    # class S1(F1, F2, F3):
    #     # name = '嘿嘿嘿'
    #     pass
    # obj = S1()
    # # obj.name = '想干饭'
    # print(obj.name)
    '''
        对象自身   >>>   产生对象的类     >>>    父类(从左往右)
    '''
    class G:
        name = 'from G'
        pass
    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)

    print(S1.mro())

五、经典类与新式类

在Python 2及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于“新式类”,都会获得所有“新式类”的特性;反之,即不由任意内置类型派生出的类,则称之为“经典类”。

“新式类”和“经典类”的区分在Python 3之后就已经不存在,在Python 3.x之后的版本,因为所有的类都派生自内置类型object(即使没有显示的继承object类型),即所有的类都是“新式类”。

概括

经典类:不继承object或者其子类的类
新式类:继承object或者其子类的类
在python2中有经典类和新式类
在python3中只有新式类(所有类默认都继承object)

ps:这里的object类,可以看成一个虚的东西,在名称查找的时候虽然出现了闭环,但是因为他是虚的所以不影响查找顺序。

class Student(object):pass

ps:以后我们在定义类的时候 如果没有其他明确的父类,也可能习惯写object兼容python2(虽然遇到python2的可能性很小了)

六、派生方法

这里主要用到一个super()方法,所谓派生方法就是子类基于父类的某个方法做了拓展或是增加了一些条件。

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


class Student(Person):
    def __init__(self, name, age, gender, sid):
        super().__init__(name, age, gender)  # 子类调用父类的方法
        self.sid = sid


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


stu1 = Student('jason', 18, 'male', 666)
print(stu1.__dict__)
学生类可以在创建对象的时候调用父类的方法,并且还可以添加自己独有的数据
tea1 = Teacher('tony', 28, 'female', 99)
print(tea1.__dict__)
老师类也跟学生类一样,可以添加独有的数据


class MyList(list):
    def append(self, values):
        if values == 'jason':
            print('jason不能尾部追加')
            return
        super().append(values)

        
当我们想要做一些限制条件的时候也可以在类中添加。
obj = MyList()
print(obj, type(obj))
obj.append(111)
obj.append(222)
obj.append(333)
obj.append('jason')
print(obj)

七、作业

利用面向对象的继承派生重写列表、字典、字符串等常见方法操作,自己随便改改

# 模拟列表,在执行append的添加功能的时候,如果小康康在添加的值中出现的就不让他加(模仿老师的功能)
class my_list(list):
    def append(self, values):
        if '小康康' in values:
            print('小康康不能出现在添加的数据值中!')
            return
        super().append(values)

m_l1 = my_list()
m_l1.append('小康康不穿内裤')
# 小康康不能出现在添加的数据值中!
m_l1.append('小猪猪')
print(m_l1)
# ['小猪猪']



# 定义一个类,模拟字典,在取值的时候如果没有关键字(小猪猪),并且要获取的键的名称有关键字(小猪猪),就给他加上指定内容,然后返回结果
class my_dict(dict):
    def get(self, values):

        if '小猪猪' in values and '小猪猪' not in self:
            self[values] = '小猪猪来了,好生伺候!'
            answer = super(my_dict, self).get(values)
            return answer

m_d1 = my_dict()
m_d1.get('要吃小猪猪')
print(m_d1)
# {'要吃小猪猪': '小猪猪来了,好生伺候!'}
print(m_d1.get('要吃小猪猪'))
# 小猪猪来了,好生伺候!
posted @ 2022-11-03 18:57  wwwxxx123  阅读(16)  评论(0编辑  收藏  举报