简体   繁体   中英

how do I create a meta-class such that instances of the class are wrapped in `functools.wraps`?

I want to wrap a function name funky_the_function with an object which has a __call__ method defined.

Without using functools.wraps the name of the wrapped function will be lost and so will the docstring.

How do I create a meta-class such that instances of the class are wrapped in functools.wraps ?

import functools

class MetaDecorator(type):
    def __call__(self, *args):
        super().__call__(*args)
        # SOMEWHERE INSIDE OF `__call__` WE HAVE:
        #     obj = functools.wraps(obj)

class Decorator(metaclass=MetaDecorator):
    def __init__(f):
        assert(callable(f))
        self._f = f
    def __call__(self, *args, **kwargs):
        return self._f(*args, **kwargs)

@Decorator
def funky_the_function(*args, **kwargs):
    """Documentation string"""
    print('Called example function')

print(funky_the_function.__name__)
print(funky_the_function.__doc__)

1. without metaclass, without wraps:

If you're looking for a way to fix the name and docstring, it can be easily fix by dynamically adding the __name__ and __doc__ to the instance. There is no need for meta-classes.

class Decorator:
    def __init__(self, f):
        assert callable(f)
        self.__name__ = f.__name__
        self.__doc__ = f.__doc__
        self._f = f

    def __call__(self, *args, **kwargs):
        return self._f(*args, **kwargs)

2. with metaclass, without wraps:

Of course you can do this in metaclass as well:

class MetaDecorator(type):
    def __call__(self, f):
        assert callable(f)
        instance = super().__call__(f)
        instance.__name__ = f.__name__
        instance.__doc__ = f.__doc__
        return instance


class Decorator(metaclass=MetaDecorator):
    def __init__(self, f):
        self._f = f

    def __call__(self, *args, **kwargs):
        return self._f(*args, **kwargs)

3. with metaclass, with wraps:

from functools import wraps

class MetaDecorator(type):
    def __call__(self, f):
        assert callable(f)
        instance = super().__call__(f)
        instance = wraps(f)(instance)
        return instance


class Decorator(metaclass=MetaDecorator):
    def __init__(self, f):
        self._f = f

    def __call__(self, *args, **kwargs):
        return self._f(*args, **kwargs)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM