Python中给函数添加属性

2017-11-28  本文已影响338人  MontyOak

总所周知,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
侵删

上一篇 下一篇

猜你喜欢

热点阅读