zoukankan      html  css  js  c++  java
  • 面向对象-进阶篇

    一、类的继承

    ​ 什么是继承?
    ​ -继承是一种新建类的方式,新建的类称为子类,被继承的类称为父类
    ​ -继承的特性是: 子类会遗传父类的属性
    ​ -继承是类与类之间的关系

    ​ 为什么要用继承?
    ​ -可以减少代码的冗余

    ​ 对象的继承:
    ​ -Python中支持一个类同时继承多个类

    class Parentl:
        pass
    
    
    class Parent2:
        pass
    
    
    class Sub1(Parentl,Parent2):   # 类Sub1 继承了Parentl,Parent2两个类
        pass
    
    # 使用__bases__方法可以获取对象 继承的类
    print(Sub1.__bases__)        # (<class '__main__.Parentl'>, <class '__main__.Parent2'>)
    
    # 在python3中如果没有继承任何类,则默认继承object类
    print(Parentl.__bases__)         #  (<class 'object'>,)
    

    ​ 继承的应用:
    ​ -牢记对象是特征与功能的集合体,拿选课系统举例:

    class OldboyPeople:
        '''由于学生和老师都是人,因此都有姓名、年龄、性别'''
        school = 'oldboy'
    
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
    
    
    class OldboyStudent(OldboyPeople):
        def choose_course(self):
            print('%s is choosing course' %self.name)
    
    
    class OldboyTeacher(OldboyPeople):
        def score(self,stu_obj,num):
            print('%s is scoring' %self.name)
            stu_obj.score = num
    
    stu1 = OldboyStudent('cxk',98,'male')
    tea1 = OldboyTeacher('zhao',98,'male')
    print(stu1.school)
    print(tea1.school)
    print(stu1.__dict__)
    print(tea1.__dict__)
    stu1.choose_course()
    tea1.score(stu1,99)
    print(stu1.score)
    

    ​ 属性查找顺序:

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

    菱形继承问题

    新式类:继承object的类,python3中全是新式类

    经典类:没有继承object的类,只有python2中有

    在菱形继承的时候,新式类是广度优先(老祖宗最后找);经典类深度优先(一路找到底,再找旁边的)

    经典类:不找多各类最后继承的同一个类,直接去找下一个父类,广度优先92-菱形继承问题-经典类.png?x-oss-process=style/watermark

    新式类:不找多各类最后继承的同一个类,直接去找下一个父类,广度优先

    92-菱形继承问题-新式类.png?x-oss-process=style/watermark

    class G(object):
        # def test(self):
        #     print('from G')
        pass
    
    
    print(G.__bases__)
    
    
    class E(G):
        # def test(self):
        #     print('from E')
        pass
    
    
    class B(E):
        # def test(self):
        #     print('from B')
        pass
    
    
    class F(G):
        # def test(self):
        #     print('from F')
        pass
    
    
    class C(F):
        # def test(self):
        #     print('from C')
        pass
    
    
    class D(G):
        # def test(self):
        #     print('from D')
        pass
    
    
    class A(B, C, D):
        def test(self):
            print('from A')
    
    
    obj = A()
    obj.test()  # 查找顺序 A->B->E-C-F-D->G-object
    

    也可以通过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列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

    二、类的派生

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

    派生方法一:(类调用)
    指名道姓的使用 跟继承没有关系

    class OldboyPeople:
        """由于学生和老师都是人,因此人都有姓名、年龄、性别"""
        school = 'oldboy'
    
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
    
    
    class OldboyStudent(OldboyPeople):
        '''由于学生类没有独自的__init__()方法,因此不需要声明继承父类的__init__()方法,会自动继承'''
    
        def choose_course(self):
            print('%s is choose course' %self.name)
    
    
    class OldboyTeacher(OldboyPeople):
        '''由于老师类有独自的__init__()方法,因此需要声明继承父类的__init__()'''
    
        def __init__(self,name,age,gender,level):
            OldboyPeople.__init__(self,name,age,gender)
            self.level = level   # 派生
    
        def score(self,stu_obj,num):
            print("%s is scoring" % self.name)
            stu_obj.score = num
    
    
    stu1 = OldboyStudent('cxk',98,'famale')
    tea1 = OldboyTeacher('nike',88,'famale',10)
    
    print(stu1.__dict__)
    print(tea1.__dict__)
    
    

    派生方法二:通过super关键字
    -跟继承有关系
    -super()是一个特殊的对象

    class OldboyPeople:
        school = 'oldboy'
    
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    
    class OldboyStudent(OldboyPeople):
    
        def __init__(self,name,age,sex,stu_id):
            # super()会按照mro列表拿到父类对象
            # 对象来调用绑定方法,不需要传递第一个参数(self)
            super().__init__(name,age,sex)
            # 经典类和新式类
            # 经典类中必须这么写(py3中没有经典类),都用上面的方法写
            # super(OldboyStudent,OldboyPeople).__init(name,age,sex)
    
            self.stu_id = stu_id
    
        def choose_couse(self):
            print('%s is choosing course' % self.name)
    
    
    stu1 = OldboyStudent('cxk',98,'famale',1)
    print(stu1.__dict__)
    

    三、super()方法的使用

    回顾一下绑定方法:

    class Student:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def study(self):
            print(self.name)
            print('study...')
    
        def chang_name(self,new_name):
            print('原来的名字是%s'% self.name)
            self.name = new_name
            print('修改的名字是%s' % self.name)
    
    

    类来调用对象的绑定方法(写在类中的函数,没加装饰器),有几个参数就传几个参数
    Student.__init__(125,'cxk',78)
    类实例化产生对象,会自动调用__init__完成初始化操作
    stu = Student('cxk',78)
    对象的绑定方法的特殊之处,会把对象本身当做第一个参数传入
    stu.study()
    stu2 = Student('tank',28)
    stu2.study()

    修改学生姓名
    stu = Student('nick',48)
    方式一

    print(stu.name)
    stu.name = 'tank'
    print(stu.name)
    

    方式二

    stu.chang_name('张全蛋')
    print(stu.name)
    

    方式三

    Student.chang_name(stu,'蔡徐坤')
    print(stu.name)
    

    方式四
    定义一个函数

    def chang_name(obj,name):
       # 修改Obj对象的name属性
        print('原来的名字是%s' %obj.name)
        obj.name = name
        print('修改的名字是%s' % obj.name)
    
    chang_name(stu,'老王')
    print(stu.name)
    

    ​ -object写与不写,在py3中没有区别
    ​ -有的人在py3中这么写,为了向下兼容
    ​ -调用父类方法的第一种方式:指名道姓的方式,跟继承关系无关

    super()的方式:

    class Person(object):
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
    class Student(Person):
        school = 'yyyy'
        def __init__(self,name,age,course):
            # super()相当于得到了一个特殊对象,第一个参数(self)不需要传值,调用绑定方法,会把自己传过去
    
            # super().__init__(name,age)
            # 看到别人这么写:super(类名,对象)  py3中为了兼容py2
            # 在py3中这么写和省略写法完全一样
            # 在py2 中必须super(Student,self)写
            super(Student,self).__init__(name,age)
            self.course = course
    
    stu= Student('cxk',78,'python')
    print(stu.name)
    print(stu.age)
    print(stu.course)
    

    总结:
    -有继承关系的时候通常用super
    -指名道姓的方式在什么情况下用呢?
    1.没有继承关系
    2.如果继承了多个父类,super是按照mro列表找,现在想知名道姓的用某个方法,就需要指名道姓的使用。

    super练习:
    super是按照mro列表找的

    class A:
        def f1(self):
            print('A.f1')
    
    class B:
        def f1(self):
            print('B.f1')
        def f2(self):
            print('B.f2')
            super().f1()
    
    class C(B,A):
        def f1(self):
            print('C.f1')
    
    c= C()
    c.f2()
    print(c.f2())
    print(C.mro())
    

    四、组合

    什么是组合?

    对象的某个属性是另一个类的对象

    组合的概念:

    class Foo:
        def __init__(self,bar):
            self.bar=bar
    
    class Bar:
        pass
    
    
    bar=Bar()
    f=Foo(Bar())
    f=Foo(bar)
    print(f.bar)
    

    使用组合可以减少代码冗余:

    如何使用组合

    例1:

    class Person:
        school = 'oldboy'
    
    class Teacher(Person):
        def __init__(self,name,age,level,course):
            self.name=name
            self.age=age
            self.level=level
            #course是课程对象,表示老师教授的课程
            self.course=course
    
    class Student(Person):
        def __init__(self,name,age,course):
            self.name=name
            self.age=age
            # course是课程对象,表示学生选的课程
            self.course = course
    
    class Course:
        def __init__(self,course_name,course_price,course_period):
            self.name=course_name
            self.price=course_price
            self.period=course_period
    
    course=Course('Python',20180,7)
    stu=Student('nick',19,course)
    teacher=Teacher('nick',19,'高级',course)
    #查看老师教授的课程名
    print(teacher.course.name)
    

    例2:

    class Person:
        school = 'oldboy'
    class Teacher(Person):
        def __init__(self,name,age,level,course):
            self.name=name
            self.age=age
            self.level=level
            #course是课程对象,表示老师教授的课程
            self.course=course
    
    class Student(Person):
        # course=[]  #错误
        def __init__(self,name,age):
            self.name=name
            self.age=age
            # course是课程对象,表示学生选的课程
            self.course_list = []  # 定义一个空列表存放未来要添加的课程属性
        def choose_course(self,course):
            # self.course=[]  #错误
            #把课程对象追加到学生选课的列表中
            self.course_list.append(course)
    
        def tell_all_course(self):
            #循环学生选课列表,每次拿出一个课程对象
            for course in self.course_list:
                #课程对象.name  取到课程名字
                print(course.name)
    
    class Course:
        def __init__(self,course_name,course_price,course_period):
            self.name=course_name
            self.price=course_price
            self.period=course_period
    
    
    
    course=Course('Python',20199,7)  # 生成课程对象
    stu1=Student('nick',19)
    stu1.choose_course(course)  # 添加课程属性到stu1对象  # 对象的组合
    stu2=Student('王二丫',19) 
    stu2.choose_course(course)  
    stu2.choose_course(Course('linux',19999,5))  # 再添加一个课程属性
    

    查看stu1选的所有课程名称:
    方法一(通过普通函数):

    def tell_all_course(student):
        for course in student.course_list:
            print(course.name)
    
    tell_all_course(stu1)
    tell_all_course(stu2)
    

    方法二(通过对象的绑定)

    stu1.tell_all_course()
    stu2.tell_all_course()
    

    五、多态与多态性

    什么是多态?
    一类事物的多种形态 比如: 动物类: 猪,狗,人

    多态性:
    一种调用方式,不同的执行效果(多态性)
    多态性是指在不考虑实例类型的情况下使用实例

    好处:
    1.增加了程序的灵活性: 以不变应万变,不论对象千变万化,使用者都是同一种形式去调用
    2.增加了程序额可扩展性 :

    多态基础:

    
    class Animal:
        def speak(self):
            pass
    
    class Pig(Animal):
        def speak(self):
            print('哼哼哼')
    
    class Dog(Animal):
        def speak(self):
            print('汪汪')
    
    class People(Animal):
        def speak(self):
            print('say hello')
    
    pig=Pig()
    dog=Dog()
    people=People()
    pig.speak()
    dog.speak()
    people.speak()
    
    

    由上面可以看出动物下的各类都有同一个方法:
    我们可以直接定义一个函数来执行类里面的方法

    def animal_speak(obj):  # obj就是对象
        obj.speak()
    animal_speak(pig)
    animal_speak(people)
    
    

    多态性的使用方法

    第一种方式:用abc实现接口统一化,约束代码(用的比较少)

    注意:如果使用了约束,但是没有在子类中使用约束的方法,在实例化产生对象的时候就会报错。

    TypeError: Can't instantiate abstract class Func with abstract methods speak

    import abc
    #第一在括号中写metaclass=abc.ABCMeta
    class Animal(metaclass=abc.ABCMeta):
        #第二在要约束的方法上,写abc.abstractmethod装饰器
        @abc.abstractmethod
        def speak(self):
            pass
    
    class Pig(Animal):
        def speak(self):
            print('哼哼哼')
    class Dog(Animal):
        def speak(self):
            print('汪汪')
    
    class People(Animal):
        def speak(self):
            print('say hello')
    
    
    people = People()
    pig=Pig()
    def animal_speak(obj):
         obj.speak()
    animal_speak(people)
    

    第二种方式,用异常处理来实现(常用)

    class Animal():
        def speak(self):
            #主动抛出异常
            raise Exception('你得给我重写它啊')
    class Pig(Animal):
        def speak(self):
            print('哼哼哼')
    class People(Animal):
        def speak(self):
            print('say hello')
    pig=Pig()
    pe=People()
    def animal_speak(obj):
        obj.speak()
    
    animal_speak(pig)
    animal_speak(pe)
    

    鸭子类型:只要走路像鸭子(对象与其他中有某个相同绑定方法),那你就是鸭子

    class Pig:
        def speak(self):
            print('哼哼哼')
    class People:
        def speak(self):
            print('say hello')
    
    pig=Pig()
    pe=People()
    def animal_speak(obj):
        obj.speak()
    animal_speak(pig)
    animal_speak(pe)
    

    传统写法:

    class File:
        def read(self):
            pass
        def write(self):
            pass
    #内存类
    class Memory(File):
        def read(self):
            print('Memory...read')
        def write(self):
            print('Memory...write')
    
    class Network(File):
        def read(self):
            print('Network...read')
        def write(self):
            print('Network...write')
    

    鸭子类型的写法:

    # 内存类
    class Memory:
        def read(self):
            print('Memory...read')
    
        def write(self):
            print('Memory...write')
    
    class Network:
        def read(self):
            print('Network...read')
    
        def write(self):
            print('Network...write')
    
    def read(obj):
        obj.read()
    m=Memory()
    n=Network()
    read(m)
    read(n)
    

    六、封装

    封装是什么意思?
    从封装本身的意思去理解,
    封装就好像是拿来一个麻袋,把小猫,小狗,小王八,一起装进麻袋,然后把麻袋封上口子如何隐藏,把东西包装进去之后,隐藏起来,外部访问不到

    如何用代码实现隐藏?
    隐藏属性/隐藏方法 隐藏之后,外部访问不到,只有内部能够访问
    隐藏属性:通过 __变量名来隐藏
    隐藏方法:通过 __方法名来隐藏

    隐藏属性是为了安全

    把name 隐藏起来

    class Person:
        def __init__(self,name,age):
            self.__name=name
            self.__age=age
        def get_name(self):  # 自定义一个方法,让外部只能通过这种方来访问
            # print(self.__name)
            return '[----%s-----]'%self.__name
    
    

    访问name.

    p=Person('nick',89)
    print(p.get_name())
    

    隐藏的属性访问不到?实际上有方法能访问到

    通过变形隐藏了属性

    print(p._Person__name)
    
    print(p.__dict__)
    

    也可以隐藏方法,为了隔离复杂度

    class Person:
        def __init__(self,name,age):
            self.__name=name
            self.__age=age
        def __speak(self):
            print('6666')
    
    p=Person('nick',89)
    p.__speak()   # 这样访问不到
    print(Person.__dict__)
    p._Person__speak()
    

    什么时候属性变形,只要再类内部,以__变量名 命名的变量,都会被隐藏,会发生的变形,在外部放入的 __变量名 属性是不隐藏的:

    class Person:
        def __init__(self,name,age):
            self.__name=name
            self.__age=age
        def set_xx(self,xx):
            self.__xx=xx
    
    p=Person('nick',18)
    
    p.set_xx('6688')
    print(p.__dict__)
    

    类的property特性

    计算人的bmi指数
    property装饰器:把方法包装成数据属性,在使用时可以不用加括号而直接使用

    class Person:
        def __init__(self,name,height,weight):
            self.name=name
            self.height=height
            self.weight=weight
        @property
        def bmi(self):
            return self.weight/(self.height**2)
            # return self.weight/(self.height*self.height)
    p=Person('lqz',1.82,70)
    # print(p.bmi())   # 如果不用property装饰器,就加要括号调用
    print(p.bmi)
    # p.name='ppp'
    # p.bmi=90
    

    property之setter和deleter

    class Person:
        def __init__(self,name,height,weight):
            self.__name=name
            self.__height=height
            self.__weight=weight
        @property
        def name(self):
            return '[我的名字是:%s]'%self.__name
        #用property装饰的方法名.setter
        @name.setter
        def name(self,new_name):
            # if not isinstance(new_name,str):
            if type(new_name) is not str:
                raise Exception('改不了')
            if new_name.startswith('sb'):
                raise Exception('不能以sb开头')
            self.__name=new_name
            print(f'成功修改为{new_name}')
    
        # 用property装饰的方法名.deleter
        @name.deleter
        def name(self):
            # raise Exception('不能删')
            print('删除成功')
            # del self.__name
    
    p=Person('lqz',1.82,70)
    # print(p.name)
    # p.name='pppp'
    # p.name='xxx'
    #改不了,直接抛一异常
    # p.name=999
    # p.name='sb_nick'
    
    # print(p.name)
    
    del p.name
    print(p.name)
    

    七、绑定方法与非绑定方法:

    绑定方法分为 对象的绑定方法 和 类的绑定方法

    绑定方法的特殊之处: 绑定给谁就是谁来调用,并且会把自身传过来

    类的绑定方法: 绑定给类,类来调用,会把类自身传进来

    类的绑定方法用在什么地方?

    不需要通过对象,只需要通过类就能获取到一些东西的时候,用类的绑定方法

    class Person:
        def __init__(self,name,age):
            print(self)
            self.name=name
            self.age=age
        # @classmethod
        # def test(cls):
        #     print(cls)
        #     print('类的绑定方法')
        #     #类实例化产生对象,返回
        #     return cls('lqz',19)
        @classmethod
        def get_obj_by_name(cls,name):
            #1 去文件中查找名字为 name 的pickle文件
            #2 反序列化成对象
            #3 return 对象
            pass
        def get_obj_by_name(self,name):
            # 1 去文件中查找名字为 name 的pickle文件
            # 2 反序列化成对象
            # 3 return 对象
            pass
    
    
    
    per1=Person.test()
    
    per2=Person('nick',18)
    
    
    admin=Person.get_obj_by_name('lqz')
    admin.create()
    

    类的绑定方法,可以由对象来调

    class Person:
    
        def __init__(self,name,age):
            # print(self)
            self.name=name
            self.age=age
        @classmethod
        def test(cls):
            print(cls)
            print('类的绑定方法')
    
    # Person.test()
    p=Person('nick',18)
    #对象可以调用类的绑定方法,也是把该对象的类传入
    p.test()
    

    总结:

    • classmethod 是个装饰器,放在类中函数的上面,该函数就变成了类的绑定方法
    • 类的绑定方法由类来调用,自动把类传过去(对象也可以调,一般不用)
    • 类的绑定方法用在什么地方?
    • 不需要通过对象,只需要通过类就能完成某些事的时候,就把该方法绑定到类。
    1. 非绑定方法:
      staticmethod 也是一个装饰器,装饰在类中函数上面,相当于普通方法,谁也不绑定

      对象、类都可以调用,但是不会自动传值。

      class Person:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def object_method(self):
              print('我是对象绑定方法,对象来调用我')
      
          @classmethod
          def class_method(cls):
              print('我是类的绑定方法,类来调用我')
      
          #当成一个普通函数,只不过是写在类内部的
          @staticmethod
          def static_method():
              print('我是静态方法,谁都不绑定')
      #静态方法(非绑定方法)
      #类来调用
      # Person.static_method()
      # 对象来调用
      # p=Person('nick',19)
      # p.static_method()
      

      生成一个唯一的id号。

      import uuid
      print(uuid.uuid4())
      

    静态方法(非绑定方法)的使用:

    跟类和对象都没有关系的时候,可以定义成静态方法,一般在类内部使用,类外部也可以使用

    它就是一个普通函数,想把它拿到类中管理,就可以定义成静态方法

    class Person:
        def __init__(self,name, age):
            self.id=self.get_uuid()
            self.name = name
            self.age = age
    
        #当成一个普通函数,只不过是写在类内部的
        @staticmethod
        def static_method():
            print('我是静态方法,谁都不绑定')
        @staticmethod
        def get_uuid():
            import uuid
            return uuid.uuid4()
    
    # import uuid
    # def get_uuid():
    #     return uuid.uuid4()
    # a=uuid.uuid4()
    # p=Person(uuid.uuid4(),'nick',18)
    # p=Person(get_uuid(),'nick',18)
    p=Person('nick',19)
    # print(p.id)
    # print(p.get_uuid())
    print(Person.get_uuid())
    #面向对象高级:Person类也是一个特殊的对象
    
  • 相关阅读:
    Evanyou Blog 彩带
    Evanyou Blog 彩带
    Evanyou Blog 彩带
    Evanyou Blog 彩带
    Evanyou Blog 彩带
    Evanyou Blog 彩带
    Evanyou Blog 彩带
    Evanyou Blog 彩带
    Evanyou Blog 彩带
    Evanyou Blog 彩带
  • 原文地址:https://www.cnblogs.com/guapitomjoy/p/11520826.html
Copyright © 2011-2022 走看看