用元类和__getattribute__改变类属性的读取方式

首先,需要知道两点:

  1. 类本身是type类的实例
  2. __getattribute__ 可以改变类实例的属性的读取方式(http://www.cnblogs.com/blackmatrix/p/5681480.html)

对于__getattribute__,大部分例子都是类似这样的,通过重写类的__getattribute__方法,来改变这个类的实例的属性读取行为

class ClassA:
    x = 'a'
    def __init__(self):
        self.y = 'b'
    def __getattribute__(self, item):
        return '__getattribute__'


if __name__ == '__main__':
    a = ClassA()
    # 使用实例直接访问存在的类属性时,会调用__getattribute__方法
    # 输出结果 __getattribute__
    print(a.x)
    # 使用实例直接访问实例存在的实例属性时,会调用__getattribute__方法
    # 输出结果 __getattribute__
    print(a.y)
    # 使用实例直接访问实例不存在的实例属性时,也会调用__getattribute__方法
    # 输出结果 __getattribute__
    print(a.z)

但是换个角度想想,类本身也是type类的实例,如果重写type类的子类,也就是元类的__getattribute__的方法,不就可以改变类自身属性的读取行为了吗?

 

有这个想法是因为之前写了一个整合自定义异常的类,每个异常是这个类的类属性,每次要抛出异常时,直接raise这个类的类属性就好。

但是,当某些情况,需要对抛出异常的信息进行修改时,因为这个类是全局的,类属性的修改,会影响到程序其他地方抛出的异常,这明显不合理。

 

所以才有了通过重写元类的__getattribute__方法,来改变这个异常类的属性读取过程。

部分代码:

class ApiException(Exception):

    def __init__(self, err_code, message, status_code=500):
        self.err_code = err_code
        self.status_code = status_code
        self.message = message

    def __str__(self):
        return str('异常编号:{code}; Http Code:{status_code}; 异常信息:{message}'.format(
            code=self.err_code,
            status_code=self.status_code,
            message=self.message))


class MetaApiExceptions(type):

    def __getattribute__(self, item):
        api_ex = super().__getattribute__(item)
        new_api_ex = ApiException(err_code=api_ex['api_code'],
                                  status_code=api_ex['http_code'],
                                  message=api_ex['api_msg'])
        return new_api_ex


class ApiBaseExceptions(metaclass=MetaApiExceptions):

    def __init__(self):
        pass


# API 系统层面异常信息,以1000开始
class ApiSysExceptions(ApiBaseExceptions):
    # code 1000 为保留编码,代表执行成功
    # 服务不可用
    missing_system_error = {'api_code': 1001, 'http_code': 403, 'api_msg': '服务不可用'}

通过在type类的子类MetaApiExceptions中,重写__getattribute__方法,每次读取类属性时,会根据原先的类属性(一个dict),实例化出一个异常对象,便于程序中对异常信息进行修改及抛出。

这样,程序在raise这个类中的某个异常时,获取的都是新实例化的对象,做任何修改都不会影响到其他地方的使用。

同时,不用在类创建的时候,一次实例化N个异常对象,而是每次使用时在实例化,用完自动回收,更加合理。

 

posted @ 2017-05-23 21:07  BlackMatrix  阅读(496)  评论(0编辑  收藏  举报