美文网首页
Python中给函数添加属性

Python中给函数添加属性

作者: MontyOak | 来源:发表于2017-11-28 22:52 被阅读338次

    总所周知,Python里有一个非常出名的魔术方法__str__,它常用于对于一个Python对象的字符转化,比如:

    class Foo(object):
        def __init__(self):
            pass
        def __str__(self):
            return 'Foo Class'
    
    foo = Foo()
    str(foo)
    # 'Foo Class'
    

    那么,如果要对Python中的一个函数实现类似的效果,应该怎么做呢?
    按照魔术方法本身定位而言,dunder方法一般只是针对于对象,而不是函数。但好在Python是动态语言,支持duck type特性,从这个思路出发,可以找到解决的办法。

    def with_str (str_func):
        def wrapper (f):
            class FuncType:
                def __call__ (self, *args, **kwargs):
                    return f(*args, **kwargs)
                def __str__ (self):
                    return str_func()
            return functools.wraps(f)(FuncType())
        return wrapper
    
    def foo_str():
        return 'This is __str__ for foo!'
    @with_str(foo_str)
    def foo():
        print('Hello world!')
    
    foo
    # 'Hello world'
    str(foo)
    # 'This is __str__ for foo!'
    

    简单来说,实现的思路是将原有函数转化成一个对象,当调用这个对象的时候,触发__call__直接调用原有函数,为这个对象绑定__str__的方法。从实现效果来看,就好像是函数具备了__str__的魔术方法。这也就是动态语言的优越性,名副其实的duck type。原文地址

    另外再提一个实际开发当中经常遇到的问题。在面对web开发的过程当中。经常需要将数据dumps成json返回给response。不巧的是,Python中的对象与json中的对象并非一一对应的关系,即便把范围缩小到Python中的内置对象,也有常见的decimal,datetime等对象无法正确的dumps到json中。
    这时的主要思路是在json.dumps是检测特殊类型并单独处理。
    一个简单的实现是:

    import json
    import decimal
    import datetime
    
    class MyEncoder(json.JSONEncoder):
        def default(self, obj):
            if isinstance(obj, decimal.Decimal):
                return float(obj)
            if isinstance(obj, datetime.datetime):
                return obj.strftime( '%Y-%m-%d %H:%M:%S' )
            return super(MyEncoder, self).default(obj)
    
    json.dumps(data, cls=MyEncoder)
    

    上面重写了json.dumps中的encoder部分,也可以重写一个to_json的方法来对特殊类型进行特殊处理。
    前段时间在一个微信公众号的一篇文章中偶然看见了一种实现方法:

    import json
    import decimal
    import datetime
    from functools import singledispatch
    
    @singledispatch
    def myEncoder(obj):
        raise TypeError('Must be impelement!')
    @myEncoder.register(datetime.datetime)
    def _(obj):
        return obj.strftime( '%Y-%m-%d %H:%M:%S' )
    @myEncoder.register(decimal.Decimal)
    def _(obj):
        return float(obj)
    
    class MyEncoder(json.JSONEncoder):
        def default(self, obj):
            try:
                myEncoder(obj)
            except TypeError:
                return super(MyEncoder, self).default(obj)
    
    json.dumps(data, cls=MyEncoder)
    

    这里使用了Python3.4中新增的singledispatch特性来解决这种单泛型的问题,当有新的特殊类型时,仅需要新增注册方法即可。关于singledispatch的更对信息:点我

    参考链接:
    https://stackoverflow.com/questions/47452513/how-do-you-implement-str-for-a-function
    https://mp.weixin.qq.com/s/icNu9hO80opzq4CxuF4unQ
    侵删

    相关文章

      网友评论

          本文标题:Python中给函数添加属性

          本文链接:https://www.haomeiwen.com/subject/dbcybxtx.html