class Meta(type):
def __new__(mcs, name, bases, attrs):
print(locals())
return super(Meta, mcs).__new__(mcs, name, bases, attrs)
def __init__(cls, *args, **kwargs):
# 获得类原来的__init__方法
_init = _init = getattr(cls, "__init__", None)
def cls_init(self, *args, **kwargs):
if _init: # 如果子类定义了__init__方法,执行它
_init(self, *args, **kwargs)
# 接下来做自己想做的操作
print("inner init")
print(locals())
super(Meta, cls).__init__(*args, **kwargs)
# 修改创建类的__init__方法
setattr(cls, "__init__", cls_init)
class AClass(metaclass=Meta):
status = 1
def __init__(self, name):
print(f"a init and name is {name}")
class AnotherClass(metaclass=Meta):
status = 2
def __init__(self, num):
print(f"another init and num is {num}")
a = AClass("小红")
b = AClass("大红")
c = AnotherClass(1)
d = AnotherClass(100)
输出
{'mcs': , 'name': 'AClass', 'bases': (), 'attrs': {'__module__': '__main__', '__qualname__': 'AClass', 'status': 1, '__init__': }, '__class__': }
{'cls': , 'args': ('AClass', (), {'__module__': '__main__', '__qualname__': 'AClass', 'status': 1, '__init__': }), 'kwargs': {}, 'cls_init': .cls_init at 0x10895b520>, '_init': , '__class__': }
{'mcs': , 'name': 'AnotherClass', 'bases': (), 'attrs': {'__module__': '__main__', '__qualname__': 'AnotherClass', 'status': 2, '__init__': }, '__class__': }
{'cls': , 'args': ('AnotherClass', (), {'__module__': '__main__', '__qualname__': 'AnotherClass', 'status': 2, '__init__': }), 'kwargs': {}, 'cls_init': .cls_init at 0x10895b640>, '_init': , '__class__': }
a init and name is 小红
inner init
a init and name is 大红
inner init
another init and num is 1
inner init
another init and num is 100
inner init
当一个类继承了单独type后,这个类就成了可以创建其他类对象的类
如:
class Meta(type):
def __new__(mcs, name, bases, attrs):
return super(Meta, mcs).__new__(mcs, name, bases, attrs)
def __init__(cls, *args, **kwargs):
super(Meta, cls).__init__(*args, **kwargs)
对于类来说,其创建也是会经过__new__和__init__的,和类实例一样。
元类就可以在其中就行某些操作。元类的__new__接收四个参数,都是不具名参,分别是mcs:本元类类对象,name:类的名字、bases:类的父类们、attrs:类的属性。我们可以在这对类的属性进行一些操作,下面有一个通过此方法设计response类的实例
init方法是在执行完new之后,已经把类对象创建出后对其执行某些操作。
此方法有也是四个参数,第一个参数是cls:__new__函数创建出的类,其余三个参数和__new__是相同的
比如上边示例代码的元类的__init__方法
类比类实例的创建过程,类的创建过程中的__new__函数的第一个参数实际上是元类(类实例的话是类),__init__函数的第一个参数是创建出的类本身(类实例的话是其本身)。
import functools
import json
def gen_response_tuple(data=None, status_code=200, code=0, msg=""):
"""
根据传入参数生成类似
({'data': {}, 'msg': 'invalid response', 'code': 10001}, 500)
形式的元组用于接口返回。
使用此方法以定义错误码、错误信息和http状态码
"""
if data is None:
data = {}
return {"data": data, "code": code, "msg": msg, }, status_code
class ResponseMeta(type):
"""
此元类的目的是构造
class HttpResponse(ResponseMeta):
__module_prefix__ = 1
Success = 200, 0, "ok"
Failed = 200, 1, "failed"
此类中虽然类属性是元组,但是通过元类构造成了一个只需要传递data的偏函数
通过此类创造的类构造的实例可以用于接口返回
如: return HttpResponse.Success(data={})
这样接口返回 {'data': {}, 'msg': 'ok', 'code': 10000}, 200
当需要抛出错误时,可以使用raise HttpResponse.Success.Exception(data={})
这样在中间件中就可以捕获到错误值是错误提示的错误,避免raise的非友好提示
"""
prefix_codes = dict()
def __new__(mcs, name, bases, attrs):
module_prefix = attrs.get("__module_prefix__", None)
# 保证在使用错误类之前定义了错误码前缀,否则项目无法启动
assert module_prefix is not None, f"class <{name}> must define __module_prefix__"
# 保证错误码前缀不重复,否则项目无法启动
assert module_prefix not in mcs.prefix_codes, f"module_prefix of class <{mcs.prefix_codes[module_prefix]}> " \
f"and class <{name}> is conflicted"
mcs.prefix_codes[module_prefix] = name
# 将类属性(格式不为'__a__'的)都转化为偏函数,这个函数固定了status_code和code和msg,只需要填入data
for k in filter(lambda x: not (x.startswith("__") and x.endswith("__")), attrs.keys()):
assert isinstance(attrs[k], tuple) and len(attrs[k]) == 3, f"attribution <{k}> of class " \
f"<{name}> is not a tuple like (200, 1, 'ok')"
msg = attrs[k][2]
code = module_prefix * 10000 + attrs[k][1]
status_code = attrs[k][0]
attrs[k] = functools.partial(
gen_response_tuple,
status_code=status_code,
code=code,
msg=msg,
)
def gen_exception(data_in=None, msg_in=msg, code_in=code, status_code_in=status_code):
return Exception(
json.dumps(dict(
msg=msg_in, code=code_in, status_code=status_code_in, data=data_in if data_in is not None else {}
))
)
# 添加Exception属性使此属性能被raise
setattr(attrs[k], 'Exception', functools.partial(
gen_exception,
status_code_in=status_code,
code_in=code,
msg_in=msg,
))
return super(ResponseMeta, mcs).__new__(mcs, name, bases, attrs)
class BasicResponse(metaclass=ResponseMeta):
__module_prefix__ = 1
Success = 200, 0, 'success'
NotFound = 200, 1, 'resource not found'
UnknownError = 200, 9999, 'unknown error'