python元类--求别再说orm了

python元类,

工作已经三年多了,python开发也进行了3年之久,也从一个小小开发者,转换成面试官(依然觉得自己很low,还需要继续努力学习)。 但每次问到别人python metaclass时,别人的回答几乎没有人让我满意的,无外乎千篇一律的 metaclass 多用在orm上。

我去,元类和orm有什么关系啊,就是网上抄来抄去,也许当年有一位牛人做了如此的解读后,让无数的不假思索者找到救命稻草一般。

python 元类只要你想用,你就可以用它。 我们都知道元类是生成类的类,一个类指定了metaclass,就意味着在定义class时,解释器运行此处后 即可检查此处的metaclass,通过你的metaclass来生成你的类对象,你的metaclass里面定义的

__new__函数,此时将作用到你的类中。 那么你可以做很多事情,就像是给你自己的类装上了装饰器一般,预装属性,预装函数。

我们自定义metaclass 即自定义了类的初始化过程。  比如我们想自定义一个 restful API, 或者我们想通过一个接口根据用户传递的不同参数响应不同的方法。

 

我们当然可以,通过传递的参数, 然后通过python系统函数 getattr 来完成此类工作。

import APIClass
from django.http import JsonResponse

def APIviews(request):
    
    request_data = request.POST.get("data", {})
    api_func = request_data.get("api_func", None)
    
    func = getattr(APIClass, api_func)

    return func(request)


class APIClass:

    def get_comment_list(self, request):
        return JsonResponse({"comment_list":[]})

    def get_comment_detail(self, request):
        return JsonResponse({"comment_detail":[]})
   

        

但如果我们想做很多复杂的操作呢,比如restful api,通常的框架某一个handler下的 create,对应post请求,read 对应get请求,如若我们更多的需求呢,难道我们依然在read函数里面根据参数的不同做if else 判断吗?

 

这时候直接一个getattr无法做到了,我们可以使用元类来实现它。 我们直接上maas的api源码,并做一些修改,这里给大家看看大佬们其实也是这么实现的,并没有大家想想的那么高大上,那么高不可攀。

class OperationsHandlerType(type):
    """除了curd 外的 我们使用属性将 新类的方法存储在定义的属性里,这样所有继承此metaclass的类
都会将自己的方法与相关属性关联起来
    """
    callmap = { 'GET': 'read', 'POST': 'create',
                'PUT': 'update', 'DELETE': 'delete' }

    def __new__(metaclass, name, bases, namespace):
        cls = super(type, metaclass).__new__(
            metaclass, name, bases, namespace)

        # Create a signature:function mapping for CRUD operations.
        crud = {
            (http_method, None): getattr(cls, method)
            for http_method, method in list(OperationsHandlerType.callmap.items())
            if getattr(cls, method, None) is not None
            }

        # Create a signature:function mapping for non-CRUD operations.
        operations = {
            attribute.export: attribute
            for attribute in list(vars(cls).values())
            if getattr(attribute, "export", None) is not None
            }

        # Create the exports mapping.
        exports = {}

        for base in bases:
            for key, value in vars(base).items():
                export = getattr(value, "export", None)
                if export is not None:
                    new_func = getattr(cls, key, None)
                    if new_func is not None:
                        exports[export] = new_func

        # Export custom operations.
        exports.update(operations)

        methods_exported = {method for http_method, method in exports}
        for http_method, method in OperationsResource.crudmap.items():
            if method in methods_exported:
                raise AssertionError(
                    "A CRUD operation (%s/%s) has been registered as an "
                    "operation on %s." % (http_method, method, name))

        # Export CRUD methods.
        exports.update(crud)

        # Update the class.
        cls.exports = exports
        cls.allowed_methods = frozenset(
            http_method for http_method, name in exports)

        # Flags used later.
        has_fields = cls.fields is not BaseHandler.fields
        has_resource_uri = hasattr(cls, "resource_uri")
        is_internal_only = cls.__module__ in {__name__, "metadataserver.api"}


        if has_fields and has_resource_uri:
            _, uri_params, *_ = cls.resource_uri()
            missing = set(uri_params).difference(cls.fields)
            if len(missing) != 0:
                raise OperationsHandlerMisconfigured(
                    "{handler.__module__}.{handler.__name__} does not render "
                    "all fields required to construct a self-referential URI. "
                    "Fields missing: {missing}.".format(
                        handler=cls, missing=" ".join(sorted(missing))))

        if (not has_resource_uri and not is_internal_only and
                not cls.is_anonymous):
            log.warn(
                "{handler.__module__}.{handler.__name__} does not have "
                "`resource_uri`. This means it may be omitted from generated "
                "documentation. Please investigate.", handler=cls)

        return cls

 

我们通过设置exports属性,将 cls 定义的函数,在初始化时,将函数引用存储在exports中,这样我们就可以使用 exports 来操作响应的对象方法了。

 

我们定义装饰器

def operation(idempotent, exported_as=None):
    """Decorator to make a method available on the API.

    :param idempotent: If this operation is idempotent. Idempotent operations
        are made available via HTTP GET, non-idempotent operations via HTTP
        POST.
    :param exported_as: Optional operation name; defaults to the name of the
        exported method.
    """
    method = "GET" if idempotent else "POST"

    def _decorator(func):
        if exported_as is None:
            func.export = method, func.__name__
        else:
            func.export = method, exported_as
        return func

    return _decorator

这装饰器的作用就是在定义 handler类时,api接口的函数(非 create read update delete)进行装饰,这样就给函数 赋予了 export,在__new__方法中即可进行。

 

在设定一个 mixin类,此类正是像我们一开始简单的设计api那里一样,不过现在更灵活,更强大。

class OperationsHandlerMixin:
    """Handler mixin for operations dispatch.

    This enabled dispatch to custom functions that piggyback on HTTP methods
    that ordinarily, in Piston, are used for CRUD operations.

    This must be used in cooperation with :class:`OperationsResource` and
    :class:`OperationsHandlerType`.
    """
    # CSRF protection is on by default.  Only pure 0-legged oauth API requests
    # don't go through the CSRF machinery (see
    # middleware.CSRFHelperMiddleware).
    # This is a field used by piston to decide whether or not CSRF protection
    # should be performed.
    csrf_exempt = False

    # Populated by OperationsHandlerType.
    exports = None

    # Specified by subclasses.
    anonymous = None

    def dispatch(self, request, *args, **kwargs):
        op = request.GET.get("op") or request.POST.get("op")
        signature = request.method.upper(), op
        function = self.exports.get(signature)
        if function is None:
            raise MAASAPIBadRequest(
                "Unrecognised signature: method=%s op=%s" % signature)
        else:
            return function(self, request, *args, **kwargs)

 

那么现在就可以定义我们的handler类的

class OperationsHandler(
        OperationsHandlerMixin,
        metaclass=OperationsHandlerType):


    def create(self, request):
        pass

    
    @operation(idempotent=False)
    def list_all_data(self, request):
        pass

 

以上我们就可以实现一个自定义的restful, 其实元类没那么可怕,同时还很可爱。每当我们查看django源码,piston源码,maas源码,cloud-init、openstack等优秀框架时,总被别人优秀设计叹为观止,但如果我们认真阅读,认真学习,深度思考,我们也能从中学到一些实战的经验,可以用在我们日后的工作中。

 

从小小的知识点开始,深度才有远度,希望大家今后在面试、在工作中,别条件反射一般说到metaclass 便是orm,他两完全没有任何关系,你可以从你自己设计向面试官表达你对知识理解,那样的你一定是做过实现的,做过实战的,一定比那些网上看看教程、看看别人嚼碎的知识要更吸引人的多。

我很low,所以才要更努力学习。加油加油!

 

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yinxingpan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值