Skip to content

Instantly share code, notes, and snippets.

@c1ay
Last active August 7, 2020 07:07
Show Gist options
  • Save c1ay/ec5eb0199b1f98910c6d32c8db4fc44a to your computer and use it in GitHub Desktop.
Save c1ay/ec5eb0199b1f98910c6d32c8db4fc44a to your computer and use it in GitHub Desktop.
使用python 类型标注的解析字段,适用于rpc调用,字段懒加载
import typing
from abc import ABCMeta
INSTANCE_ATTR = 'instance_attr'
class UndefinedType:
def __repr__(self):
return 'UndefinedType'
Undefined = UndefinedType()
class ModelField:
def __init__(self, name: str, type_: typing.Type, default: typing.Any = None):
self.name = name
self.type_ = type_
self.default = default
@classmethod
def infer(cls, name, value, type_):
if value is Undefined:
default = None
else:
default = value
return cls(name=name, type_=type_, default=default)
class DynamicMetaclass(ABCMeta):
__fields__ = typing.Dict[str, typing.Any]
def __new__(mcs, name, bases, namespace, **kwargs):
fields: typing.Dict[str, ModelField] = {}
annotations = namespace.get('__annotations__', {})
for ann_name, ann_type in annotations.items():
value = namespace.get(ann_name, Undefined)
fields[ann_name] = ModelField.infer(name=ann_name, value=value, type_=ann_type)
namespace.update({'__fields__': fields})
fields_properties = {}
for field in fields.values():
origin_get_func = namespace.get(f'resolve_{field.name}', Undefined)
fields_properties[field.name] = property(make_get_func(field, origin_get_func),
make_set_func(field))
namespace.update(**fields_properties)
namespace.update({'fields': fields})
return super().__new__(mcs, name, bases, namespace, **kwargs)
class BaseModel(metaclass=DynamicMetaclass):
def dict(self, include: typing.List = None):
fields = self.fields
if include:
fields = set(fields) & set(include)
return {k: getattr(self, k) for k in fields}
def make_get_func(field, origin_get_func):
def get_func(self):
attr_name = field.name
if attr_name in self.__dict__:
return self.__dict__[attr_name]
if origin_get_func is not Undefined:
return origin_get_func(self)
# try to get from instance
instance_attr = getattr(self, INSTANCE_ATTR, 'instance')
instance = getattr(self, instance_attr, None)
if instance:
return getattr(instance, attr_name, field.default)
return field.default
return get_func
def make_set_func(field):
def set_func(self, value):
attr_name = field.name
self.__dict__[attr_name] = value
return set_func
@c1ay
Copy link
Author

c1ay commented Apr 1, 2020

使用方法:

class User(BaseModel):
    id: int
    name: str

    def __init__(self, user_id):
        self.user_id = user_id

    def resolve_id(self):
        return self.user_id

    def resolve_name(self):
        # get user name from rpc or other
        # 从rpc或者数据库 获取用户名称
        return ''

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment