zoukankan      html  css  js  c++  java
  • Python Day 27 元类、__inti__方法、__new__方法、__call__方法、单例模式、exec与eval区别、异常处理语法

      阅读目录

        元类

        __inti__方法

        __new__方法

        __call__方法

        单例模式

        exec与eval区别

        异常处理语法

      ##元类

    #什么是元类?
    一切皆对象 
    
    ​    类也是对象,可以把一个类当成普通对象来使用,比如存储到列表中,或者作为参数传给函数等等...
    
    ​    对象是如何产生的?  通过类实例化产生的
    
    ​    类对象 是由type实例化产生的 
    class AClass:
        pass
    print(type(AClass)) 
    
    #我们可以手动调用type来实例化产生一个类
    
    #一个类由三个部分组成
    1.类的名称         我是谁
    
    ​2.类的父类们    我从哪里来
    
    ​3.类的名称空间     我有什么
    
    
    #type源码中
    type(类名,父类元组,名称空间字典)  #返回一个新的类
    
    type(对象)  #将会返回这个对象的类型
    
    #所以:我们可以总结出 当你定义一个class时,解释器会自动调用type来完成类的实例化
    
    #示例一:模拟解释器创建类对象
    def test1(a):
        print(a)
    
    def test2(self,b):
        print(self,b)
    
    class_name = "C"
    bases = (object,)
    name_dict = {"name":"jack","test1":test1,"test2":test2}
    
    C = type(class_name,bases,name_dict)
    # print(C)
    c1 = C()
    # print(c1)
    c1.test2(100)


    #用来干啥
    “”“

    当我们需要高度定制类时,如限制类名必须大写开头等等...

    就需要使用元类,但是元类type中的代码 无法被修改 ,只能创建新的元类(继承自type) 通过覆盖__init__来完成对类的限制 

    ”“”

    #使用元类
    #1、如何自定义元类
      
    class MyMetaClass(type):
        pass
    # 使用自定义元类
    class Person(metaclass=MyMetaClass):
        pass
     
     #2、__init__`方法 (重点)
    “”“

    实例化对象时会自动执行类中的__init__方法, 类也是对象 ,在实例化类对象时会自动执元类中的__init__方法

    并且传入类的三个必要参数,类的名字,父类们,名称空间

    当然会自动传入类对象本身作为第一个参数

    ”“”
    #示例:限制类名必须首字母大写   控制类中方法名必须全部小写
    class MyMetaClass(type):
        def __init__(self,class_name,bases,name_dict):
            super().__init__(class_name,bases,name_dict)
            # 类名必须首字母大写  否则直接抛出异常
            if not class_name.istitle():
                print("类名必须大写 傻x!")
                raise Exception
            # 控制类中方法名必须全部小写
            for k in name_dict:
                if str(type(name_dict[k])) == "<class 'function'>":
                    if not k.islower():
                        raise Exception
        pass
    # 会自动调用其元类中的 __init__ 方法传入 类对象本身 类名称 父类们  名称空间
    class Student(object,metaclass=MyMetaClass): # MyMetaClass("Student",(object,),{})
        NAME = 10
        def say(self):
            print("SAY")
        pass
     
     
     #__new__方法
    """

    元类中的new方法会在创建类对象时执行,并且先于init方法 

    作用是创建一个类对象  

    class A(metaclass=MyMetaClass):

    pass

    #1.执行MyMetaClass的__new__方法   拿到一个类对象

    #2.执行MyMetaClass的__init__ 方法  传入类对象以及其他的属性 ,进行初始化

     

    注意:如果覆盖了__new__ 一定也要调用type中的__new__并返回执行结果  

     

    #使用new方法也可以完成定制类的工作 和init有什么区别?

    在调用init方法前类对象已经创建完成了  

    所以如果对性能要求高的话 可以选在在new中完成定制   如果发现有问题,就不用创建类对象了

    """
    #示例:
     
    class MyMetaClass(type):
        def __init__(self,class_name,bases,name_dict):
            # super().__init__(class_name,bases,name_dict)
            print("init")
            pass
        # 该方法会在实例化类对象时自动调用并且在 init 之前调用
        # 其作用时用于创建新的类对象的
        # 注意这里必须调用type类中的__new__ 否则将无法产生类对象   并且返回其结果
        def __new__(cls, *args, **kwargs):
            # cls 表示元类自己 即MyMetaClass
            # print("new")
            # print(args,kwargs)
            return type.__new__(cls,*args,**kwargs)  # 如果覆盖__new__ 一定要写上这行代码

    class Person(metaclass=MyMetaClass):
        pass
    print(Person)
    # 就算__init__中什么都不写 这个类对象其实已经创建完成了 该有的属性都有了
    # 这是与普通类不同之处
    # print(Person.__name__)

    # class Student:
    #     def __init__(self,name):
    #         pass
    #
    # s = Student("张三")
    #
    # s.name
     
     
    #示例二:需求: 要求每个类必须包含__doc__属性
    class DocMeatClass(type):
        def __init__(self,class_name,bases,name_dict):
            super().__init__(class_name,bases,name_dict)
            # if not("__doc__" in name_dict and name_dict["__doc__"]):
            #     raise  Exception
           
            # 或者如下
            if not self.__doc__:
                raise Exception
        #或者如下
         # if not name_dict.get("__doc"):
            #     raise Exception
    class Person(metaclass=DocMeatClass):
        """"""
        pass
     
    #3、__call__`方法(重点)
    “”“

    元类中的 call方法会在调用类时执行,

    可以用于控制对象的创建过程

    ”“”
    class MyMeta(type):
        # 获得某个类的实例
        def __call__(self, *args, **kwargs):
            print("call")
            # return super().__call__(*args,**kwargs)
            new_args = []
            for i in args:
                if isinstance(i,str):
                    new_args.append(i.upper())
                else:
                    new_args.append(i)
            return super().__call__(*new_args,**kwargs)
     
    # 注意注意注意:  __new__  __init__ 是创建类对象时还会执行
    # __call__ 类对象要产生实例时执行
    class Student(metaclass=MyMeta):
        def __init__(self,name,gender,age):
            print(self)#类的对象
            self.name = name
            self.gender = gender
            self.age = age
    s = Student("jack","woman",18)
    print(s.age)
    print(s.gender)

    class Person(metaclass=MyMeta):
        def __init__(self,name,gender):
            self.name = name
            self.gender = gender
    print(Person)#类的对象
    p = Person("rose","man")
    print(p.name)
     
     
     

      #元类的单例模式

    #什么是单例:
    
    ​    某个类如果只有一个实例对象,那么该类成为单例类 
    
    单例的好处:
    
    ​    当某个类的所有对象特征和行为完全一样时,避免重复创建对象,浪费资源
    
    #示例:
    class SingletonMetaClass(type):
        #创建类时会执init 在这为每个类设置一个obj属性 默认为None
        def __init__(self,class_name,bases,name_dic):
            super().__init__(class_name,bases,name_dic)
            self.obj = None
        
        # 当类要创建对象时会执行 该方法
        def __call__(self, *args, **kwargs):
             # 判断这个类 如果已经有实例了就直接返回 从而实现单例
            if self.obj:
                return self.obj
    
            # 没有则创建新的实例并保存到类中
            obj = type.__call__(self,*args,**kwargs)
            self.obj = obj
            return obj
    class Person(metaclass=SingletonMetaClass): # Person = SingletonMetaClass("Person",(object,),{})
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
    
        def say(self):
            print("my name is %s  my 姑姑 is 龙妈" % self.name)
    
    class Student(metaclass=SingletonMetaClass):
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
    
        def say(self):
            print("my name is %s  my 姑姑 is 龙妈" % self.name)
    
    
    
    p1 = Person("jon snow",18,"man")
    # p1.say()
    p2 = Person("jon snow",18,"man")
    
    
    # # p1.say()
    # p3 = Person("jon snow",18,"man")
    # p1.say()
    # print(p1 is p2) #True
    
    
    stu = Student("布兰",16,"man")
    print(p1 , stu)
    
    stu1 = Student("布兰",16,"man")
    
    stu2 = Student("布兰",16,"man")
    stu3 = Student("布兰",16,"man")
    
    print(stu1,stu2,stu3)
    #总结
    Person类创建多个对象时,因为self.obj在创建了第一次以后被赋值,后续在创建对象就不会再重新创建了,又来了Student类也要创建对象,初始化时self.obj被赋予默认值None,又可以重新创建另外一个对象了,然而又想在创建另一个Student类,又创建不了多个了,只使用同一个

      ##exec与eval

    “”“
    exec用于执行字符串形式的python代码 只要符合python都能执行 ,并且可以指定将执行产生的名字放入某个名称空间 
    
    eval 用于把一个字符串当成一个表达式来执行,且只能执行简单的表达式,不能有任何的特殊语法,返回表达式执行结果
    
    ”“”
    #示例一:exec
    class_text = """
    class A:
        def test(self):
            print(self)
    """
    loca2 = {}
    exec(class_text,None,loca2)
    print(loca2)
    #eval(class_text)  #报错
    
    #示例二:eval
    x=100
    y=200
    s="x+y"
    print(eval(s))#200
    ls={"x":1,"y":2}
    print(eval(s,None,ls))#3
    gs={"x":10,"y":20}
    print(eval(s,gs,ls))#3   执行locals中的先---ls

      ##异常

    # 什么是异常
    
    异常是程序运行过程中发生的非正常情况,是一个错误发生时的信号
    
    异常如果没有被正确处理的话,将导致程序被终止,这对于用户体验是非常差的,可能导致严重的后果  
    
    处理异常的目的就是提高程序的健壮性 
    
    
    
    # 异常的分类
    
    python解释器在执行代码前会先检查语法,语法检查通过才会开始执行代码
    
    1.语法检测异常  作为一个合格的程序员 是不应该出现这种低级错误 
    
    2.运行时异常   
    
    ​    已经通过语法检测,开始执行代码,执行过程中发生异常 称之为运行时异常
    
    TypeError: 'int' object is not subscriptable     对象不能被切片  
    TypeError: 'list' object is not callable        对象不能被调用
    IndexError: list index out of range                索引超出范围
    TypeError: 'builtin_function_or_method' object is not iterable     对象不能被迭代
    KeyError: 'xxx'      不存在这个key
    FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx'  文件找不到
    
    #异常的组成
    Traceback (most recent call last):
      File "F:/python8期/课堂内容/day29/11.常见异常.py", line 22, in <module>
        with open("xxxxx") as f:
    FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx'
    
            
            
            
    Traceback    是异常追踪信息   用于展示错误发生的具体位置 以及调用的过程
    其中 包括了 错误发生的模块  文件路径   行号  函数名称  具体的代码
    
    最后一行  前面是错误的类型  
             后面 错误的详细信息   在查找错误时 主要参考的就是详细信息
    
    #异常的处理
    异常发生后 如果不正确处理将导致程序终止,我们必须应该尽量的避免这种情况发生yufa 
    
    #异常语法
    语法:
    
    try:
    
    ​    可能会出现异常的代码 放到try里面
    
    except 具体异常类型 as e:
    
    ​    如果真的发生异常就执行except
    
    #如何正确处理异常
    1. 当发生异常  不是立马加try   要先找出错误原因并解决它  
    
    2. try 仅在 即使你知道为什么发生错误 ,但是你却无法避免  
       例如   你明确告诉用户 需要一个正确文件路径 然而用户依然传入了错误的路径   
    
       如  socket    双方都要使用管道  ,但是如果一方有由于某些原因强行关闭了 ,即使你知道原因也无法避免出错   那就只能try 保证程序正常结束      
    
       总结一句话:能不加try 就不加try
    
    #自定义异常类
    当系统提供异常类不能准确描述错误原因时   就可以自定义异常类  
    
    继承自Exception即可 
    class  MyException(Exception):
        pass
    
    #主动抛异常
    “”“
    什么时候需要主动抛出异常  
    
    ​    当我们做功能的提供者,给外界提供一个功能接口
    
    ​    但是使用者不按照相应的方式来使用,或者参数类型不正确等原因,导致功能无法正常执行时,就应该主动抛出异常
    
    主动抛出异常使用raise  关键字 
    
    后面可以跟任何Exception的子类   或是 对象
    ”“”
    
    #断言assert
    “”“
    断言  其实可以理解为断定的意思  
    
    即非常肯定某个条件是成立的
    
    条件是否成立其实可以使用if来判断
    
    其存在的目的就是 为了简化if 判断而生的
    ”“”
    #示例
    class FileTypeException(Exception):
        pass
    
    class Player:
        def play(self,path):
            # if not path.endswith("mp3"):
            #     # print("文件类错误!")
            #     # z主动抛出异常
            #     raise FileTypeException("仅能播放mp3格式.....")
            # print("播放%s" % path)
    
            assert path.endswith("mp3")
            print("播放%s" % path)
    
    
    p = Player()
    p.play("xxx.mp4")
  • 相关阅读:
    Autotools使用流程【转】
    Linux下GCC生成和使用静态库和动态库【转】
    Linux驱动模块的Makefile分析【转】
    GNU C 中零长度的数组【转】
    Linux设备驱动中的IO模型---阻塞和非阻塞IO【转】
    程序(进程)内存分布解析【转】
    【转】用户空间使用i2c_dev--不错
    开源课堂网址
    【转】用Device tree overlay掌控Beaglebone Black的硬件资源
    【转】Device Tree(三):代码分析
  • 原文地址:https://www.cnblogs.com/liangzhenghong/p/10920453.html
Copyright © 2011-2022 走看看