zoukankan      html  css  js  c++  java
  • 29 元类 异常

    什么是元类

      一切皆对象 

      类也是对象,可以把一个类当成普通对象来使用,比如存储到列表中,或者作为参数传给函数等等...

      对象   通过类实例化产生的

      类对象 是由type实例化产生的

    例子

    class A :
    pass
    print(type(A))

    调用Ttye 实例化产生一个类

    一个类由三个部分产生
      类名字
      类的父类们

      名称空间

    type(类名,父类元组,名称空间字典) #返回一个新的类

    type(对象) #将会返回这个对象的类型

    当你定义一个class时,解释器会自动调用type来完成类的实例化

    动态语言
    可以在运行期间 动态生成类
    修改对象属性
    静态语言
    数据类型的检查是在运行前(如编译阶段)做的
    方便调试,代码相对规范
    eg:

    模拟解释器创建类对象
    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)

    1.

    exec 可以执行字符串形式的python代码 并且会把执行过程中产生的名字 放到局部名称空间中

    glob = {}
    local = {}

    code = """
    def test(a):
    print(a)

    """

    exec(code, glob, local)

    # print(glob)
    print(local)
    local['test'](10)

    2.

    eval 用于执行简单的表达式,不能有任何的特殊语法
    class_text = """
    class A:
    def test(self):
    print(self)
    """

    loca2 = {}
    exec(class_text, None, loca2)
    print(loca2)

    # eval(class_text) 报错 用于简单的表达式

    print(globals())
    a = 100


    def qq():
    name = 11

    元类

    用于产生类的类 

    定义元类时,尽量在类名后添加

    MetaClass   方便阅读

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

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

    def SAYHL(self):
    print(self.name)

    type类已经具备了创建类的能力

    ,但是现在不能满足我们的需求
    需要对已有的功能进行扩展或修改
    方式
    1.直接修改源代码 行不通
    2.自己定义新的元类

    class MyMetaClass(type):
    pass


    # 使用自定义元类
    class Person(metaclass=MyMetaClass):
    pass

    
    
    __init__方法

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

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

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



    def __init__方法(类对象本身 类名 父类们 名称空间):
      pass

    自动调用其元类中的__init__方法传入
    eg:
    class MyMetaClass(type):
    pass

    # 默认创建类时 是找的type来实例化的
    class People(metaclass=MyMetaClass):
    pass
    class Student:
    def __init__(self):
    pass

    print(type(People))
    print(type(Student))

    s = Student()
    # 实例化对象时 ,1.产生空对象 2.自动执行__init_
    People = MyMetaClass()
    # 创建类对象时也是一样的 会先创建空的类对象 再调用__init__()方法
    # 创建类时
    class MyMetaClass(type):

    # 类的实例化
    def __init__(self, class_name, bases, name_dict):
    """
    元类的应用
    self:表示的是类对象
    :param class_name:类名
    :param bases:父类们
    :param name_dict:名称空间
    """

    # 调用父类的初始化
    super().__init__(class_name, bases, name_dict)
    print(name_dict)
    # 类名必须首字母大写 否则抛出异常
    if not class_name.istitle():
    print('类名必须大写,')
    raise Exception

    # 控制类中方法名必须全小写
    for k in name_dict:
    if str(type(name_dict[k])) == "<class 'function'>":
    if not k.islower():
    print('方法名全部小写')
    raise Exception
    pass


    # 正确写入
    class Students(object, metaclass=MyMetaClass):
    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 MyClass(type):

    def __init__(self, class_name, bases, name_dict):
    # 调用父类的初始化
    # super().__init__(class_name, bases , name_dict)
    print('init')
    pass
    # __new__()方法
    # def __new__ 创建类对象
    # 该方法会在实例化类对象时自动调用并且在 init 之前调用
    # 其作用时用于创建新的类对象的
    # 注意这里必须调用type类中的__new__ 否则将无法产生类对象 并且返回其结果


    def __new__(cls, *args,**kwargs):
    """
    cls 表示元类自己 即MyMetaClass
    return 结果
    """
    return type.__new__(cls,*args,**kwargs)
    # 如果覆盖__new__ 一定要写上这行代码 并返回

    class Peopel(metaclass=MyClass):
    pass

    print(Peopel)

    # 就算__init__中什么都不写 这个类对象其实已经创建完成了 该有的属性都有了
    # 这是与普通类不同之处

    print(Peopel.__name__)

    练习
    # 需求: 要求每个类必须包含__doc__属性 自定义元类 覆盖
    __doc__ 用于访问一个对象的注释信息

    class A:
    """
    qwertyu
    asdfgjhk

    """
    pass
    print(A.__doc__)
    # 你要控制类的创建 那就自定义元类 覆盖__init__

    class DocMeatClass(type):
    def __init__(self,class_name,bases,name_dict):
    super().__init__(class_name,bases,name_dict)
    if not self.__doc__:
    raise Exception
    print(type(object))

    `__call__`方法(重点)

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

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

    class MyMate(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):
    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

    p = Person("rose","man")
    print(p.name)
    ```

    总结:当你要定制类时,就自定义元类并覆盖__init__方法

    异常

    异常是程序运行过程中发生的非正常情况,是一个错误发生时的信号

    异常如果没有被正确处理的话,将导致程序被终止,这对于用户体验是非常差的,可能导致严重的后果

    处理异常的目的就是提高程序的健壮性

    异常的分类

    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 是异常追踪信息 用于展示错误发生的具体位置 以及调用的过程
    其中 包括了 错误发生的模块 文件路径 行号 函数名称 具体的代码

    最后一行 前面是错误的类型

    后面 错误的详细信息 在查找错误时 主要参考的就是详细信息

    异常处理

    异常发生后 如果不正确处理将导致程序终止,我们必须应该尽量的避免这种情况发生

    必须掌握的语法

    语法:

    try:

    可能会出现异常的代码 放到try里面

    except 具体异常类型 as e:

    如果真的发生异常就执行except

    如何正确处理异常

    1. 当发生异常 不是立马加try 要先找出错误原因并解决它

    2. try 仅在 即使你知道为什么发生错误 ,但是你却无法避免 例如 你明确告诉用户 需要一个正确文件路径 然而用户依然传入了错误的路径

      如 socket 双方都要使用管道 ,但是如果一方有由于某些原因强行关闭了 ,即使你知道原因也无法避免出错 那就只能try 保证程序正常结束

      总结一句话:能不加try 就不加try

       

    自定义异常类

    当系统提供异常类不能准确描述错误原因时 就可以自定义异常类

    继承自Exception即可

    class  MyException(Exception):
       pass

     

    主动抛出异常:

    什么时候需要主动抛出异常

    当我们做功能的提供者,给外界提供一个功能接口

    但是使用者不按照相应的方式来使用,或者参数类型不正确等原因,导致功能无法正常执行时,就应该主动抛出异常

    主动抛出异常使用raise 关键字

    后面可以跟任何Exception的子类 或是 对象

    raise MyException
    raise MyException("错误具体原因!")

     

    断言assert

    断言 其实可以理解为断定的意思

    即非常肯定某个条件是成立的

    条件是否成立其实可以使用if来判断

    其存在的目的就是 为了简化if 判断而生的
























  • 相关阅读:
    linux中nc命令
    Centos6.5 安装zabbix3(收藏,非原创)
    紀念
    算法学习资源收集
    一道奇怪的求和题
    P5717 题解
    P1424 刷题记录
    P1888 题解
    P1422 刷题记录
    P1055 题解
  • 原文地址:https://www.cnblogs.com/komorebi/p/10920853.html
Copyright © 2011-2022 走看看