前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >functools之update_wrapper的使用

functools之update_wrapper的使用

作者头像
the5fire
发布2019-03-01 14:10:13
1.5K0
发布2019-03-01 14:10:13
举报

由来

xadmin通过实现自己的BaseAdminView(继承自Django的View)来完成xadmin后台界面的处理。在解决一个csrf的问题时,翻了下xadmin BaseAdminView和Django的View部分的代码,关键点少了一条 update_wrapper 使用。导致我的小伙伴调试了半天。

还是看代码

可以对比看下:

代码语言:javascript
复制
## xadmin代码
@classonlymethod
def as_view(cls):
    def view(request, *args, **kwargs):
        self = cls(request, *args, **kwargs)

        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get

        if self.request_method in self.http_method_names:
            handler = getattr(
                self, self.request_method, self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed

        return handler(request, *args, **kwargs)

    # take name and docstring from class
    update_wrapper(view, cls, updated=())
    view.need_site_permission = cls.need_site_permission

    return view

### django/views/generic/base.py:class View中的代码
@classonlymethod
def as_view(cls, **initkwargs):
    """Main entry point for a request-response process."""
    for key in initkwargs:
        if key in cls.http_method_names:
            raise TypeError("You tried to pass in the %s method name as a "
                            "keyword argument to %s(). Don't do that."
                            % (key, cls.__name__))
        if not hasattr(cls, key):
            raise TypeError("%s() received an invalid keyword %r. as_view "
                            "only accepts arguments that are already "
                            "attributes of the class." % (cls.__name__, key))

    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
        return self.dispatch(request, *args, **kwargs)
    view.view_class = cls
    view.view_initkwargs = initkwargs

    # take name and docstring from class
    update_wrapper(view, cls, updated=())

    # and possible attributes set by decorators
    # like csrf_exempt from dispatch
    update_wrapper(view, cls.dispatch, assigned=())    ## 差了这个啊!同学们
    return view

xadmin是直接把dispatch的代码放到内部的view中了,这样看起来直观,但是缺少了对外可配置(通过重写增加装饰器)的dispatch函数。导致无法对view进行csrf_exempt装饰(其实是dispatch上装饰@csrf_exempt时,装饰器返回的inner上会设置csrf_exempt = True的属性)。

update_wrapper的用法

上述代码应该挺明显了,update_wrapper的作用就是把cls.dispatch上的所有属性全部赋值到装饰函数上,也就是代码中的 view

在Python中有几个库是“居家旅行”必备的,functools就是之一,其中的partial也十分有用,用法参考这里python中functools宝库下的partial

再举个例子吧

上面的代码,如果不熟悉对应的内容,可能不太懂。

关于保持函数签名,functools提供了两个api,一个是update_wrapper,一个是wrap装饰器函数,但是wrap装饰器函数也是调用了update_wrapper。所以就看update_wrapper就行。

有一个面试题是这样的,写一个函数装饰器,用来缓存函数的值。

函数是这样的:

代码语言:javascript
复制
def exec(sql):
    """ 从执行数据库查询 """
    return conn.execute(sql)

为了避免同样的语句执行多次数据库查询,我们需要做一层缓存,在不改变原函数的情况下。

于是有了这样的代码:

代码语言:javascript
复制
def cache(func):
    cached_dict = {}
    def inner(*args, **kwargs):
        key = repr(args, kwargs)
        try:
            return cached_dict[key]
        except KeyError:
            cached_dict[key] = func(*args, **kwargs)
            return cached_dict[key]
    inner.csrf_exempt = True
    return inner

@cache
def execute_query(sql):
    """ 从执行数据库查询 """
    print 'hit db'  # 插播一条,刚才有人在群里问如何判断是否缓存了,看这个就行了
    return  'result'  # conn.execute(sql)  # 假设拿到了结果

通过装饰器中的cached_dict来缓存同一个sql的结果。

我们print出来函数名称以及执行几个语句

代码语言:javascript
复制
print execute_query
# 输出 <function inner at 0x1025de6e0>
print execute_query('select * from test')
# 输出 hit db
# 输出 result
print execute_query('select * from test')
# 输出 result

第一个print出来的结果是存在问题的,我们调用的是execute_query,然而输出的函数名确实inner。所以这就需要update_wrapper或者wrap这样的函数来把被装饰的函数的属性(包括名称,doc等)放到装饰的函数上。也就是让inner伪装为execute_query。

要怎么做呢?一开始的那段代码就是例子了。两个方法一个是直接在inner上增加@functools.wrap(func)装饰器,另外一个方法是在return inner之前,增加一行: functools.update_wrapper(inner, func),然后重新执行上面的代码,结果是什么?

参考

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-05-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 由来
  • 还是看代码
  • update_wrapper的用法
  • 再举个例子吧
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档