• 第八章 Python-面向对象编程


    第八章 面向对象编程

    8.1 面向对象概述

    • 面向对象是一种编程的思想,是一种解决问题的思路。

    • 在编程当中,有面向过程的编程思想和面向对象的编程思想。

      • 面向过程:

        • 面向过程主要专注在过程,主要关注在解决问题的步骤,将问题逐步分析去解决,但是代码重复内容较多
          • 函数可以帮助在功能上做一定的整合
        • 龟兔赛跑:
        # 龟兔赛跑
        import time
        
        DISTENCE = 500
        name_t = "turtle"
        speed_t = 100
        t1 = DISTENCE / speed_t
        
        name_r = "rabbit"
        speed_r = 10000
        t2 = DISTENCE / speed_r
        
      • 面向对象:

        • 对象本质上是一种容器,编写程序时:变量和函数;即数据和方法。
          • 函数可以针对方法进行进行整合,对象可以对某一类数据和方法的一个封装。
          • 例如:
            • 做饭的数据:食材,调味品 做饭的方法:厨具
              • 可以将做饭的数据跟做饭的方法做一个封装,称为做饭的对象。
            • 洗衣服的数据:脏衣服 洗衣服的方法:洗衣机
              • 可以将洗衣服的数据跟做饭的方法做一个封装,称为洗衣服的对象。

    8.2 面向对象基础

    8.2.1 类和对象的定义

    • 定义:类是对象的模板,对象是类的实例,类是创建对象用的,对象是实际实现功能的。

      • 定义规范要求:类的名称首字母必须大写,同时采用驼峰体的命名方式。
      • 定义类:
      class MammalAnimal:
          name = "Turtle"
          speed = 100
      
          def run(self):
              print("i can run")
      
          def jump(self):
              print("i can jump")
      
    • 类的命名空间

      • 类体代码在创建的时候就会产生,
      class MammalAnimal:
          name = "Turtle"
          speed = 100
      
          def run(self):
              print("i can run")
      
          def jump(self):
              print("i can jump")
      
      print(MammalAnimal.__dict__)
      

      {‘module’: ‘main’, ‘name’: ‘Turtle’, ‘speed’: 100, ‘run’: , ‘jump’: , ‘dict’: dict’ of ‘MammalAnimal’ objects>, ‘weakref’: weakref’ of ‘MammalAnimal’ objects>, ‘doc’: None}

    • 类的属性访问方式

      • 例如访问类的名称属性
      class MammalAnimal:
          name = "Turtle"
          speed = 100
      
          def run(self):
              print("i can run")
      
          def jump(self):
              print("i can jump")
      
      
      print(MammalAnimal.__dict__["name"])
      print(MammalAnimal.name)
      

      Turtle
      Turtle

      • 对象的定义
      class MammalAnimal:
          name = "Turtle"
          speed = 100
      
          def run(self):
              print("i can run")
      
          def jump(self):
              print("i can jump")
      
      
      cat = MammalAnimal()
      print(cat.dict)  # 打印对象,显示结果第一行
      cat.__dict__["name"] = "cat"
      cat.__dict__["speed"] = 10
      print(cat.__dict__)  # 打印对象的命名空间,显示结果第二行
      

      {}
      {‘name’: ‘cat’, ‘speed’: 10}

    8.2.2 对象的产生过程以及self参数

    • 对象产生的过程

      • 创建一个空对象,类MammalAnimal见上一小节
      cat = MammalAnimal()
      
      • 在对象的命名空间中添加数据

        • 方式一:
        cat.__dict__["name"] = "cat"
        cat.__dict__["speed"] = 10
        
        • 方式二:__init__方法
        class MammalAnimal:
            def __init__(self):
                self.name = "Turtle"
                self.speed = 100
        
            def run(self):
                print("i can run")
        
            def jump(self):
                print("i can jump")
        
        
        obj = MammalAnimal()  # 此处已经将obj当成参数传入到MammalAnimal()中,如果再写一个字符串进去,会显示已经传递了两个位置参数
        print(obj.__dict__)
        

        {‘name’: ‘Turtle’, ‘speed’: 100}

        • 方式三:定制化对象的参数
        class MammalAnimal:
            def __init__(self, *args):  # 采用可变长位置参数传递对象的属性
                self.name = args[0]
                self.speed = args[1]
        
            def run(self):
                print("i can run")
        
            def jump(self):
                print("i can jump")
        
        
        obj = MammalAnimal("cat", 5)
        print(obj.__dict__)
        dog = MammalAnimal("dog", 9)
        print(dog.__dict__)
        

        {‘name’: ‘cat’, ‘speed’: 18}
        {‘name’: ‘dog’, ‘speed’: 9}

      • 将实例化好的对象返回出来

        • 最终对象的命名空间里只包含实例属性,而具体的函数方法,时保存在类的命名空间当中,那么对象调用这些方法的时候,先在自己的命名空间中查找,如果没有,则再到类的命名空间查找。
        class MammalAnimal:
            fly = ""  # 类属性
        
            def __init__(self, *args):
                self.name = args[0]  # 实例属性
                self.speed = args[1]
        
            def fly_cap(self):
                MammalAnimal.fly = "yes"  
        
            def run(self):  
                print("i can run")
        
            def jump(self):
                print("i can jump")
        
        
        cat = MammalAnimal("cat", 5)
        dog = MammalAnimal("dog", 9)
        print(cat.__dict__)
        cat.fly_cap()  # 通过对象调用方法
        print(cat.fly)
        print(cat.__dict__)
        MammalAnimal.run(cat)  # 通过类执行cat对象的run方法
        

        {‘name’: ‘cat’, ‘speed’: 5}
        yes
        {‘name’: ‘cat’, ‘speed’: 5}

        i can run

    8.2.3 常见成员

    • 属性:变量类型
      • 实例属性:属性前面加了self.则为实例方法
      • 类属性:属性前面没有加self.是类方法
    • 方法:函数类型
      • 绑定方法:
        • 实例方法:函数上存在(self)参数则为实例方法,专门给对象使用
        • 类方法:
      • 非绑定方法:
        • 静态方法:

    8.2.4 应用场景

    • 通过对象封装数据

      • 方法一:非对象调用数据
      switch_info = {"CE1": {"ip": "192.168.1.1", "username": "admin", "password": "Huawei@121"},
                     "CE2": {"ip": "192.168.1.2", "username": "admin", "password": "Huawei@122"},
                     "CE3": {"ip": "192.168.1.3", "username": "admin", "password": "Huawei@123"},
                     "CE4": {"ip": "192.168.1.4", "username": "admin", "password": "Huawei@124"}, }
      print(switch_info["CE2"]["ip"])
      

      192.168.1.2

      • 方式二:通过对象调用数据
      class SwitchInfo:
          def __init__(self, ip, username, password):
              self.ip = ip
              self.username = username
              self.password = password
      CE1 = SwitchInfo("192.168.100.1","python","Huawei@123")
      CE2 = SwitchInfo("192.168.100.2","python","Huawei@123")
      CE3 = SwitchInfo("192.168.100.3","python","Huawei@123")
      CE4 = SwitchInfo("192.168.100.4","python","Huawei@123")
      print(CE2.ip)  # 通过对象调用
      

      192.168.100.2

    • 在实例化时可以完成必要操作:

    import os
    
    
    class Files:
        def __init__(self, path):
            self.path = path
            if not os.path.exists(self.path):
                os.makedirs(self.path)
    
        def open_file(self):
            pass
    

    8.2.5 数据类型回顾

    L1 = list([1, 2, 3, 4, 5])  # list是类;l1是对象;[1,2,3,4,5]是初始化的参数
    L1.append(3)  # 将自己作为第一参数加入方法append计算
    print(L1)
    list.append(L1, 5)  # 直接通过类调用两个参数L1列表,添加值5
    print(L1)
    

    [1, 2, 3, 4, 5, 3]
    [1, 2, 3, 4, 5, 3, 5]

    8.3 三大特性

    8.3.1 封装

    • 封装是面向对象编程中最重要的特性
    • 封装体现在两个方面:
      • 封装数据:对象在实例化的过程当中,调用类中的__init__完成初始化,并将初始化好的数据存放在对象的内存空间中,方便之后进行调用。
      • 封装方法:将某一个类的方法封装在类中,做一个整合,方便以后进行调用。

    8.3.2 继承

    1. 基础
    • 定义:在python中子类可以继承父类当中的类属性和方法(父类当中的类属性和方法依然属于父类,子类只是继承了而已)
      • 父类和子类
      • 基类和派生类
    • 作用:为了方便在原有的类基础上进行扩展,不需要重复造轮子
    • 经典类和形式类:
      • 经典类就是默认没有继承object的类
      • 形式类就是默认继承了object的类
      • 目前来说,在Python 3.0中,我们使用的都是新式类,默认都继承了object
    2. 一般继承
    class Animal:
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def run(self):
            print("我会跑")
        def jump(self):
            print("我会跳")
        def swimming(self):
            print("我会游泳")
    
    class Humanbeing(Animal):
        def swimming(self):  # 父类中存在该方法,此时重写该方法,叫做方法的重写
            print("我不会游泳")
    
    obj = Humanbeing("张三",18)
    print(obj.name)
    print(obj.age)
    obj.run()
    obj.swimming()
    
    • 方法在父类和子类中都存在,会优先调用子类当中的方法,如果说我们想要调用父类当中的方法,需要执行以下操作:

      • 方式一:
      class Humanbeing(Animal):
          def swimming(self):  
              Animal.swimming(self)  # 指明父类中的方法
      
      • 方式二:
      class Humanbeing(Animal):
          def swimming(self):  
              super(Humanbeing, self).swimming()使用super函数调用父类中的函数swimming()
      
    3. 多层继承
    • 多层继承,父类Animal,子类Intelligence,子类的子类Humanbeing,代码如下:
    class Animal:  
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def run(self):
            print("我会跑")
    
        def jump(self):
            print("我会跳")
    
        def swimming(self):
            print("我会游泳")
    
    
    class Intelligence(Animal):  # 
        def study(self):
            print("我能学习")
    
        def control(self):
            print("我能控制自己")
    
    
    class Humanbeing(Intelligence):
        def swimming(self):
            super(Humanbeing, self).swimming()
    
    
    obj = Humanbeing("张三", 18)
    print(obj.name)
    print(obj.age)
    obj.run()
    obj.swimming()
    obj.study()
    

    张三
    18
    我会跑
    我会游泳
    我能学习

    4. 多重继承
    class Animal:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def run(self):
            print("我会跑")
    
        def jump(self):
            print("我会跳")
    
        def swimming(self):
            print("我会游泳")
    
        def control(self):
            print("我不能控制自己")
    
    
    class Intelligence():
        def study(self):
            print("我能学习")
    
        def control(self):
            print("我能控制自己")
    
    
    class Humanbeing(Animal, Intelligence):  # 如果两个父类中都存在相同的方法,则采用最左侧的Animal的方法作为方法使用
        def swimming(self):
            super(Humanbeing, self).swimming()
    
    
    obj = Humanbeing("张三", 18)
    print(obj.name)
    print(obj.age)
    obj.run()
    obj.swimming()
    obj.study()
    obj.control()  # 该方法在两个父类中都存在,选择最左侧的类中方法执行
    

    张三
    18
    我会跑
    我会游泳
    我能学习
    我不能控制自己

    • 继承顺序问题
      • 当使用obj.成员时,首先在obj对象关联的类当中进行查找,没有则向父类中查找
      • 当多重继承时,先继承左边的,在继承右边的。
      • 如果说要打破继承规则,就要进行方法的重写,其中可以直接指定调用类中的方法,也可以使用super表示要调用哪个父类中的方法。
      • 注意:需要关注当前的self是属于哪个类的对象
    5. 钻石继承
    • 场景:D继承B,C,B和C都继承A,此时构成一个钻石继承
    class A:
        def f1(self):
            print("A")
    
        def f2(self):
            print("AA")
    
        def f3(self):
            print("AAA")
    
    
    class B(A):
        def f1(self):
            print("B")
    
        def f2(self):
            print("BB")
    
    
    class C(A):
        def f1(self):
            print("C")
    
        def f3(self): print("CC")
    
    
    class D(B, C):  # 先继承B再继承C方法
        def f1(self):
            print("D")
    
    
    obj = D()
    obj.f1()
    obj.f2()
    obj.f3()
    

    D
    BB
    CC

    • C3算法和MRO列表
      • 关于继承查找的顺序问题,通过C3算法计算得到的,在Python当中 提供了一个叫做mro的方法,通过类.mro可以得到一个mro列表,里面存放的时继承的关系。
    class A:
        def f1(self):
            print("A")
    
        def f2(self):
            print("AA")
    
        def f3(self):
            print("AAA")
    
    
    class B(A):
        def f1(self):
            print("B")
    
        def f2(self):
            print("BB")
    
    
    class C(A):
        def f1(self):
            print("C")
    
        def f3(self): print("CC")
    
    
    class D(B, C):  # 先继承B再继承C方法
        def f1(self):
            print("D")
    
    
    print(D.mro())  # 查看该类的继承顺序
    

    [main.D’>, main.B’>, main.C’>, main.A’>, ]

    6. Mixint机制

    Mixint是一种规范,一个类继承了多个类,那么通过Minxint机制能够区分出哪个是主类,那个是附加性的。

    class Animal:
        pass
    
    
    class EggMinIn:
        speak = "我有翅膀"
    
        def egg_func(self):
            print("我会下蛋")
    
    
    class Duck(EggMinIn, Animal):
        pass
    
    
    class Chicken(EggMinIn, Animal):
        def Egg(self):
            print("我会下蛋")
    
        speak = "我有翅膀"
        pass
    
    
    class Cat(Animal):
        pass
    

    8.3.3 多态

    多态其实指的就是一种食物的多种形态;水: 气体:水蒸气;液体:水;固体:冰

    class Animal:
        def speak(self):
            print("我是动物,我会叫")
    
    
    class Humanbeing(Animal):
        def speak(self):
            super().speak()
            print("我是人,我的叫声是啊啊啊")
    
    
    class Dog(Animal):
        def speak(self):
            super().speak()
            print("我是狗,我的叫声是汪汪汪")
    
    
    class Duck(Animal):
        def speak(self):
            super().speak()
            print("我是鸭子,我的叫声是嘎嘎嘎")
    
    
    obj = Dog()
    obj.speak()
    
    • 定义了一个叫做Animal的类,Humanbeing,Dog,duck类,人、狗、鸭子继承了动物类,那么此时动物类里面定义了一个speak方法,就意味着他的所有子类都拥有了speak这个方法。

    • 这么讲的含义是什么呢,就是说假设我们有一个类,他存在某些特征,那么之后我们在遇到一些类,只要他属于挪个类,那么他就不需要思考,就能直接使用这些特征。

    • 此时的Animal,起到的作用,就是即便下面的子类当中没有定义这个方法,但是由于他们属于Animal这个类,因此也可以直接调用Speak方法。

    • 但是在python当中,其实并不推崇这个约束方式,而是按照规范来创建类,假如定义一个类需要具备某些特征,那么我们就应该自主的吧这个特征加到定义的类里面。比如在这个例子当中,不是应为继承了Animal才有speak这个方法,而是以为属于Animal的子类,而Animal拥有speak的方法,所以也要在自己的类中定义speak方法。

    • 鸭子类型:

      • 如果已知鸟走起路来像鸭子,叫起来像鸭子,游泳像鸭子,那么他就是鸭子。
    • 拓展:

    import abc
    
    
    class Animal(metaclass=abc.ABCMeta):
        @abc.abstractmethod
        def speak(self):
            print("I am an Animal")
    
    
    class HumanBeing(Animal):
        def speak(self):
            pass
    
    
    class Dog(Animal):
        def speak(self):
            pass
    
    
    class Duck(Animal):
        def speak(self):
            pass
    
    obj = HumanBeing()
    obj1 = Dog()
    

    8.4 隐藏属性

    8.4.1 定义

    class Foo:
        def __init__(self, name, age):
            self.__name = name  # 变量名前加上__,则该变量为隐藏属性,无法被对象调用。
            self.age = age
    
    
    obj = Foo('张三', 21)
    print(obj._Foo__name)  # 隐藏后的属性如果一定要调用,采用该方式调用
    

    通过对象.属性的方式来获取属性值,而如果定义类的时候属性名之前加上__,那么此时该属性成为隐藏属性,通过之前的方式调用则会失败,必须通过对象._类名__属性名的方式可以调用,但是不建议该隐藏后又再次被调用。

    8.4.2 作用

    • 开发者决定用户能够访问哪些属性,用什么样的方式能访问属性。影藏属性在类的内部可以直接调用,通过变形后的属性名类.__属性名
    class Foo:
        def __init__(self, name, age):
            self.__name = name  # 变量名前加上__,则该变量为隐藏属性,无法被对象调用。
            self.age = age
    
        def get_name(self):
            # print("看什么看,不准查看")
            # return
            print(self.__name)
    
        def set_name(self, val):
            if val == str(val):
                self.__name = val
                print(self.__name)
            else:
                print("更改的类型必须是字符串")
    
        def del_name(self):
            del self.__name
    
    
    obj = Foo("张三", 18)
    obj.set_name(123)
    

    更改的类型必须是字符串

    8.5 成员相关补充

    • 变量:
      • 实例变量:实例中定义
      • 类变量:类中定义
    • 方法:
      • 绑定方法:绑定给对象使用
        • 类方法:
        • 对象方法
      • 非绑定方法
        • 静态方法:

    8.5.1 类方法

    • 类的最大方法使用来创建对象,因此类方法的使用场景比较局限性,存在一个场景需要通过类来调用而不需要通过对象,就可以使用类方法。
    import config  # 引入另一个文件中的代码,代码如下:
    
    
    # Switch_info = {"CE1": {"IP": "192.168.1.1", "username": "admin", "password": "Huawei@123"},
    #                "CE2": {"IP": "192.168.1.2", "username": "admin", "password": "Huawei@123"},
    #                "CE3": {"IP": "192.168.1.3", "username": "admin", "password": "Huawei@123"},
    #                "CE4": {"IP": "192.168.1.4", "username": "admin", "password": "Huawei@123"}, }
    
    
    class SwitchMonitor:
        def __init__(self, ip, username, password):
            self.ip = ip
            self.username = username
            self.password = password
            print(self.ip, self.username, self.password)
    
        @classmethod  # 加了该命令后,不在将对象当成第一个参数传函数中,而是将类当成第一个参数传递进来
        def config_info(cls, arg):  # cls相当于类名,是一个类
            return cls(SwitchMonitor(config.Switch_info[arg]["IP"], config.Switch_info[arg]["username"],
                                     config.Switch_info[arg]["password"]))
    
    
    obj = SwitchMonitor.config_info("CE1")
    

    192.168.1.1 admin Huawei@123

    8.5.2 静态方法

    class Foo:
        def __init__(self, x):
            self.x = x
    
        def f1(self):
            print(self.x ** 2)
    
        def f2(self):
            print(self.x ** 3)
    
    
    obj = Foo(10)
    obj.f1()
    obj.f2()
    

    8.5.2 property

    • 使用装饰器:
    class Foo:
        def __init__(self, x):
            self.x = x
    
        def f1(self):
            print(self.x ** 2)
        @property   # 装饰器实现的功能,即可以通过调用属性的方式调用方法。
        def f2(self):
            print(self.x ** 3)
    
    
    obj = Foo(10)
    obj.f2  # 调用属性的方法,如果是调用方法则obj.f2()
    
    • 配合匿名属性使用
    class Foo:
        def __init__(self, x):
            self.__x = x
    
        def get_x(self):
            print(self.__x)
    
        def set_x(self,val):
            self.__x = val
    
        def del_x(self):
            del self.__x
    
    
    obj = Foo(10)
    obj.get_x()
    obj.set_x(22)
    obj.get_x()
    

    装饰器配合property

    class Foo:
        def __init__(self, x):
            self.__x = x
    
        @property
        def x(self):
            print(self.__x)
    
        @x.setter
        def x(self, val):
            self.__x = val
            print(self.__x)
    
        @x.deleter
        def x(self):
            del self.__x
    
    
    obj = Foo(10)
    obj.x = 20  # 像对对象属性一样的进行赋值,格式为x = val,则会自动匹配x.setter这个装饰器进行执行
    del obj.x  # 删除参数只要前面加了del,则匹配x.deleter删除参数
    obj.x  # 参数已经被删除,执行该语句报错
    

    8.6 反射

    8.6.1 反射的基本原理

    • 动态语言

      • 定义变量时候,不需要指明变量的类型,那么就可以称为动态语言,动态语言在程序执行时,才会知道数据的类型。
    • 反射的定义:

      • 反射是指在程序执行过程中,能够动态的获取对象的属性以及方法,执行之前不知道对象中具体有哪些内容。需要有一种灵活或者自由的方式能够判断属性是否存在,以及怎样获取对象的属性以及方法。
      • 在Python当中的体现就是能够通过字符串来获取属性和方法。
    • 作用:

      • 实例一:获取类的属性
        • name这个属性是否存在与这个对象,如果不存在会报错。
        • 需要一个更便捷的方式来获取对象中的属性和方法的信息。
        • 以及判断如果属性存在如何调用以及如果属性不存在怎么处理。
      class Foo:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def run(self):
              pass
      
      
      obj = Foo("张三", 21)
      print(obj.__dict__)  # 查询对象的属性,返回字典
      print(dir(obj))  # 查询对象的属性和方法
      print(obj.name)  # 执行该属性,如果该属性不存在则会报错。
      
      • 反射的方法
        • 相比于通过对象.属性的形式,或者对象.方法的形式来进行调用,也可以通过反射功能,采用字符串的类型事项相应多功能
    • 反射的方法:

      • hasattr:判断一个属性是否在对象中存在
      lass Foo:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def run(self):
              pass
      
      
      obj = Foo("张三", 21)
      res = hasattr(obj, "name")  # obj是对象,"name"属性对应的字符串,判断,返回结果是True或者False
      print(res)
      
      • gettattr:判断属性是否存在,如果存在返回一个值,不存在返回另一个值
      class Foo:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def run(self):
              pass
      
      
      obj = Foo("张三", 21)
      res = getattr(obj, "name",None)  # obj是对象,"name"属性对应的字符串,存在则返回name值,不存在则返回None
      print(res)
      
      • setattr:为属性赋值
      class Foo:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def run(self):
              pass
      
      
      obj = Foo("张三", 21)
      setattr(obj, "name", "李四")
      print(getattr(obj, "name"))
      

      李四

      • delattr删除属性
      class Foo:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def run(self):
              pass
      
      
      obj = Foo("张三", 21)
      delattr(obj, "name")
      res = getattr(obj, "name", "None")
      print(res)
      
      • 应用实例
      class Foo:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def run(self):
              print("123")
      
          def choose_function(self):
              option = input("please input a function's name:")  # 输入run,结果返回到res,res()就是run()。
              if hasattr(self, option):
                  res = getattr(obj, option)
                  res()
              else:
                  print("Invalid function")
      
      
      obj = Foo("张三", 21)
      obj.choose_function()
      

    8.6.2 import_module+反射

    • import_module:可以以导入字符串的形式导入一个模块
    from importlib import import_module
    
    r = import_module('random')
    res = r.randint(1, 10)
    print(res)
    
    • 注意:通过import_mudule最多只能导入到模块的级别,不能单独导入模块下的类。
    from importlib import import_module
    
    r = import_module('telnetlib')
    r1 = import_module('telnetlib.Telnet')  # 错误方式
    

    8.7 内置方法

    8.7.1 内置方法

    • 满足一定情况自动执行的方法,不需要手动调用,也叫做魔法方法

    8.7.2 使用内置方法

    1. __init__
    • 在对象执行实例化操作是后,自动触发执行__init__的方法
    2. __dict__
    • 在生成命名空间的时候,会自动触发__dict__的方法。
    3. __str__
    • 在执行print(对象)的时候会自动触发__str__,必须为该方法顶一个字符串的返回值,否则会报错。
    class Animal:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def run(self):
            pass
    
        def jump(self):
            pass
    
        def __str__(self):
            return "这是一个动物的对象"  # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用
    
    
    obj = Animal('Bob', 18)
    print(obj)
    
    4. __del__
    • 当执行删除对象的操作时,会自动的触发__del__执行

    • 默认在程序执行完成之后,会回收程序代码资源,等于说这个时候会将对象回收,也就意味着触发了方法__del__的执行。

    class Animal:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def run(self):
            pass
    
        def jump(self):
            pass
    
        def __str__(self):
            return "这是一个动物的对象"  # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用
    
        def __del__(self):
            print("程序终止了")
    
    
    obj = Animal('Bob', 18)  
    
    • 但是对于系统的资源是不会被回收,如果执行代码的过程中打开了一个文件,那么可以在__del__的方法下关闭一个文件,防止文件一直占用系统资源。
    class Animal:
        def __init__(self, name, age):
            self.name = name
            self.age = age
            self.f = open("config.py", "r")  # 打开文件
            print("文件打开完毕")
    
        def run(self):
            pass
    
        def jump(self):
            pass
    
        def __str__(self):
            return 
          
        def __del__(self):
            self.f.close()
            print("文件关闭了")  # 关闭文件
    
    
    obj = Animal('Bob', 18)
    
    5. __new__
    • 创建对象的时候会用到的魔法方法

    • 创建对象的过程:

      • 创建一个空对象,通过__new__方法创建出来的。
      • 通过__init__方法初始化对象,需要注意,一定要有一个空对象才会触发__init__
      • 返回一个初始化好的对象
      obj = Animal('Bob', 18)  # 值传递给__init__方法
      
    • 创造空对象是自动触发__new__方法,并且返回好一个创建好的空对象

      • 对象
      class Animal:
          def __init__(self, name, age):
              self.name = name
              self.age = age
              print("haha")
      
          def run(self):
              pass
      
          def jump(self):
              pass
      
          def __str__(self):
              return "这是一个动物的对象"  # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用
      
          def __new__(cls, *args, **kwargs):
              return super().__new__(cls)
      
      
      obj = Animal('Bob', 18)  # 实例化对象
      print(obj)  # 显示结果为空
      print(dir(Animal))
      
      
    6. __call__
    • 在执行对象()的操作时,会自动触发__call__方法的执行,并且可以将__call__return的结果当成返回值。
    • Animal()触发的应该是Animal父类中的call方法,可以推断Animal()触发的应该是Animal父类中的__call__方法,也就说明了,我们在做对象实例的时候,默认触发的就是Animal父类中的__call__方法。
    • 创建对象的过程:
      • 触发Animal父类当中的call方法的执行。
      • 创建一个空对象。
      • 通过__init__方法初始化对象。
      • 返回一个初始化好的对象。
    class Animal:
        def __init__(self, name, age):
            self.name = name
            self.age = age
            print("haha")
    
        def run(self):
            pass
    
        def jump(self):
            pass
    
        def __str__(self):
            return "这是一个动物的对象"  # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用
    
        def __new__(cls, *args, **kwargs):
           	return super().__new__(cls)
    
        def __call__(self, *args, **kwargs):
            return 123
    
    
    obj = Animal('Bob', 18)
    res = obj()
    print(res)
    
    7. __getitem____setitem____delitem__
    class Animal:
        def __init__(self, name, age):
            self.name = name
            self.age = age
            print("看到我这句,表示打印了__init__中的属性")
    
        def run(self):
            pass
    
        def jump(self):
            pass
    
        def __str__(self):
            return "这是一个动物的对象"  # 该函数调用一个字符串,否则会报错,在打印对象的时候会调用
    
        def __new__(cls, *args, **kwargs):
            return super().__new__(cls)
    
        def __call__(self, *args, **kwargs):
            return 123
    
        def __getitem__(self, item):
            print(item)
            pass
    
        def __setitem__(self, key, value):
            print(key)
            print(value)
    
        def __delitem__(self, key):
            print(key)
            pass
    
    
    obj = Animal('Bob', 18)  # Animal()触发的应该是Animal类中的call方法。
    obj["gender"] = "male"  # 该语句自动执行setitem的方法
    obj["name"]  # 自动触发geitem方法的执行
    del obj["age"]  # 自动触发delitem执行
    
    8. __add__
    class Animal:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def run(self):
            pass
    
        def jump(self):
            pass
    
        def __str__(self):
            return "这是一个动物的对象"  # 该函数返回一个字符串,否则会报错,在打印对象的时候会调用
    
        def __new__(cls, *args, **kwargs):
            return super().__new__(cls)
    
        def __call__(self, *args, **kwargs):
            return 123
    
        def __add__(self, other):
            print(other)
    
    
    obj1 = Animal('Bob', 18)  # Animal()触发的应该是Animal类中的call方法。
    obj2 = Animal('Alice', 19)
    obj3 = obj1 + 3  # 触发__add__方法
    print(obj3)
    
    9. __enter____exit__
    • 打开文件时采用的方法(回顾):

      • 方法一:open
      f = open("config.py", "r")  # 打开后,需要手工关闭。
      print(f.read())
      f.close()
      
      • 方法二:With xxx as 语句
      with open("config.py", "r") as f:  # 打开后当退出with语句块的时候,关闭文件。
          print(f.read())
      
    • __enter____exit__方法

      • 首先使用with对象 as语句会自动线触发__enter__方法,然后获得一个返回值,赋值给f,接着执行with 对象as下对应的代码块,执行完毕之后触发__exit__方法。
      • 使用该方法文件的过程类似
    class Foo:
        def __init__(self, name):
            self.name = name
    
        def __enter__(self):
            return 5
            pass
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("-------")
    
    
    obj = Foo("张三")
    with obj as f:
        print(f)
        print(123)
    
    10. __inter____next__
    • 生成器定义:
      • 对象中存在__iter____next__方法
      • __iter__方法返回自身(对象)
      • 通过__next__提取,如果没有数据可以提取,那么抛出一个StopIteration的错误
    • 示例一:创建一个生成器函数
    class Foo:
        def __init__(self):
            self.counter = 0
    
        def __iter__(self):
            return self
    
        def __next__(self):
            self.counter += 1
            if self.counter == 5:
                raise StopIteration
            else:
                return self.counter
    
    
    obj = Foo()
    print(next(obj))
    print(next(obj))
    print(next(obj))
    print(next(obj))
    
    • 示例二:自定义一个range方法:
    class Foo:
        def __init__(self, num):
            self.counter = -1
            self.num = num
    
        def __iter__(self):
            return self
    
        def __next__(self):
            self.counter += 1
            if self.counter == self.num:
                raise StopIteration
            else:
                return self.counter
    
    
    obj = Foo(5)
    print(next(obj))
    print(next(obj))
    print(next(obj))
    print(next(obj))
    
    • 生成器对象
      • 是由generator类实例化而来的,本质上生成器需要满足的条件跟迭代器一样。
    • 可迭代对象,需要满足两个条件。
      • 内部拥有__iter__方法
      • 通过__iter__方法返回一个迭代器对象
    • 示例:只要是可迭代的对象都能使用for循环进行遍历,而只有迭代器对象和生成器对象才能使用next方法来提取数据。
    class Foo:
        def __init__(self, num):
            self.counter = -1
            self.num = num
    
        def __iter__(self):
            return self
    
        def __next__(self):
            self.counter += 1
            if self.counter == self.num:
                raise StopIteration
            else:
                return self.counter
    
    
    class Xoo:
        def __iter__(self):
            return Foo(100)
    
    obj = Xoo()
    for i in obj:
        print(i)
    

    8.8元 类

    8.8.1 元类定义

    • 元类就是类所属的类,python中一切皆对象,那么Animal这个类本质上也是对象,Animal这个对象是由哪个类实例化得到的呢。
    class Animal:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def run(self):
            print("我会跑")
    
        def jump(self):
            print("我会跳")
    
    
    obj = Animal("cat", 8)
    print(type(obj))  # 显示结果obj类的父类是Animal
    print(type(Animal))  # 显示结果Animal的父类是type,时一个元类。
    

    main.Animal’>

    8.8.2 自定义类

    cls_name = "Animal"
    cls_base = (object,)
    cls_content = """
    class Animal:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def run(self):
            print("我会跑")
    
        def jump(self):
            print("我会跳")
    """
    cls_namespace = {}
    exec(cls_content, {}, cls_namespace)
    Animal = type(cls_name, cls_base, cls_namespace)  # 通过元类创造对象Animal,第一个参数是字符串,第二个参数是元组类型,第三个参数是字典类型
    obj = Animal("张三", 18)
    print(obj.__dict__)
    

    8.8.3 自定义元类来对类的定义进行约束

    class MyMeta(type):
        def __init__(self, a1, a2, a3):  # a1代表类名,a2代表继承的父类,a3代表命名空间。
            if a1.isupper():
                self.a1 = a1
            else:
                print("类名必须全大写")
    
    
    class Animal(object, metaclass=MyMeta):  # 将该类的父类设置为MyMeta类
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def run(self):
            print("我会跑")
    
        def jump(self):
            print("我会跳")
    
    
    obj = Animal("cat", 8)
    print(obj)
    

    8.8.4 在看__new____call__

    class MyMeta(type):
        def __call__(self, *args, **kwargs):
            print(args, kwargs)
            obj = self.__new__(self)  # 创建空对象,对于MyMeta而言,self是Animal,调用Animal中的__new__方法,如果Animal中不存在__new__方法,则查找父类中的__new__方法。
            self.__init__(obj, *args, **kwargs)  # 初始化参数
            return obj  # 返回初始化好的对象
    
    
    class Animal(object, metaclass=MyMeta):  # 将该类的父类设置为MyMeta类
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def run(self):
            print("我会跑")
    
        def jump(self):
            print("我会跳")
    
        def __new__(cls, *args, **kwargs):  # 因此Animal中该方法也可以删除,删除后,将调用父类中的__new__函数,父类中没有,将调用到元类。
            return super().__new__(cls)
    
    
    obj = Animal("cat", 8)  # 相当于执行父类中的__call__类型,进而调用
    print(obj)
    
  • 相关阅读:
    道一云与畅捷通T+对接集成获取报销信息列表连通凭证创建(报销差旅费(天水))
    MySQL介绍
    使用android 提取小米手机日志
    半个朋友圈都在露营|露营行业消费者洞察及新业态发展前景分析
    EMQX 入门教程①——EMQX 5.0 for CentOS 8 安装
    C++-封装unordered
    JAVA应用导致 CPU 100% 占用问题排查
    java基于Springboot+vue 旅游管理系统 element 前后端分离
    电力系统直流潮流分析【N-1】(Matlab代码实现)
    【数学与算法】跟踪、预测、单目标、多目标、匈牙利匹配之间的关系
  • 原文地址:https://blog.csdn.net/m0_60875392/article/details/139626876