2015-07-30 3 views
0

Я написал декоратор под названием apply_first, который устанавливает первый аргумент декорированной функции. К сожалению, у этого декоратора неправильная подпись. Как это обойти? Обычно я использую decorator, чтобы сохранить подпись, но на этот раз я бы хотел ее изменить.Как написать декоратор для установки первого аргумента?

def apply_first(x): 
    def decorate(f): 
     def g(*args): 
      return f(*((x,) + args)) 
     return g 
    return decorate 

@apply_first(5) 
def add(x,y): 
    return x+y 

print(add(3)) 
# prints 8 
+0

Что вы имеете в виду * «неправильную подпись» *? Что вы ожидали * результирующей подписи (если не '* args', почему бы и нет)? – jonrsharpe

+0

Я не ожидал, что он будет отличаться от '* args', но я бы хотел, чтобы он был' (y) '. Как я упоминаю в своем посте, модуль 'decorator' позаботится об этом для вас обычно. –

+2

Ах, боюсь, вы не сможете этого сделать. Обратите внимание, что то, что вы пишете, больше или меньше ['functools.partial'] (https://docs.python.org/3/library/functools.html#functools.partial) и' functools.wraps' сохраняет подписи с 3.4. – jonrsharpe

ответ

0

Лучшее решение:

Я закончил тем, что писал декоратор, который фиксирует декоратор, которые украшают прочь первого аргумент

import decorator 
import sys 

def wrapper_string(pre_decor): 
    argspec = decorator.getfullargspec(pre_decor) 
    if len(argspec.args) == 0: 
     raise TypeError("Couldn't find a first argument to decorate away.") 
    allargs = list(argspec.args[1:]) 
    allshortargs = list(argspec.args[1:]) 

    if argspec.varargs: 
     allargs.append('*' + argspec.varargs) 
     allshortargs.append('*' + argspec.varargs) 
    elif argspec.kwonlyargs: 
     allargs.append('*') # single star syntax 
    for a in argspec.kwonlyargs: 
     allargs.append('%s=None' % a) 
     allshortargs.append('%s=%s' % (a, a)) 
    if argspec.varkw: 
     allargs.append('**' + argspec.varkw) 
     allshortargs.append('**' + argspec.varkw) 

    head = "def " + pre_decor.__name__ + "(" + ', '.join(allargs) + "):" 
    body = " return _decorator_(_func_)("+ ', '.join(allshortargs) +")" 
    return head + "\n" + body 

def update_signature(pre_decor, post_decor, **kw): 
    "Update the signature of post_decor with the data in pre_decor" 
    post_decor.__name__ = pre_decor.__name__ 
    post_decor.__doc__ = getattr(pre_decor, '__doc__', None) 
    post_decor.__dict__ = getattr(pre_decor, '__dict__', {}) 
    argspec = decorator.getfullargspec(pre_decor) 
    if len(argspec.args) == len(argspec.defaults): 
     pos = 1 
    else: 
     pos = 0 
    post_decor.__defaults__ = getattr(pre_decor, '__defaults__',())[pos:] 
    post_decor.__kwdefaults__ = getattr(pre_decor, '__kwdefaults__', None) 
    #post_decor.__annotations__ = getattr(pre_decor, '__annotations__', None) 
    try: 
     frame = sys._getframe(3) 
    except AttributeError: # for IronPython and similar implementations 
     callermodule = '?' 
    else: 
     callermodule = frame.f_globals.get('__name__', '?') 
    post_decor.__module__ = getattr(pre_decor, '__module__', callermodule) 
    post_decor.__dict__.update(kw) 

@decorator.decorator 
def decorate_first_arg(dec, pre_decor): 
    evaldict = pre_decor.__globals__.copy() 
    evaldict['_decorator_'] = dec 
    evaldict['_func_'] = pre_decor 
    wrapper = compile(wrapper_string(pre_decor), "<string>", "single") 
    exec(wrapper, evaldict) 
    post_decor = evaldict[pre_decor.__name__] 
    update(pre_decor, post_decor) 
    return post_decor 

Почти весь код был скопирован и соответствующим образом модифицированного от Michele Симионато-х decorator модуль (GitHub).

С этим, в приведенном выше примере может быть установлена ​​как:

@decorate_first_arg 
def apply_first(x): 
    def decorate(f): 
     def g(*args): 
      return f(*((x,) + args)) 
     return g 
    return decorate 
Смежные вопросы