2013-09-07 4 views
2

В принципе, я хочу, чтобы декоратор имел список аргументов, который содержит больше, чем просто функцию, которая может быть вызвана как из @ -, так и из обычных форм. Я «разработал» быстрое обходное решение, но он уродлив и немедленно выполняет функцию в @ -form, что является нежелательным побочным эффектом (это связано с возвратом body_two(), конечно).Более элегантный способ для этого декоратора python

def status_display_with_comment(comment, closure = None): 
    def body_one(function = None): 
     def body_two(): 
      print(comment) 
      #an ugly workaround to be able to run both the @- and regular forms 
      if function != None: 
       print("Entering", function.__name__) 
       function() 
       print("Exited", function.__name__) 
      elif closure != None: 
       print("Entering", closure.__name__) 
       closure() 
       print("Exited", closure.__name__) 
     return body_two() 
    return body_one 

def a_function(): 
    print('a_function executes') 

@status_display_with_comment(comment = 'some comment') 
def a_function_with_comment(): 
    print('a_function_with_comment executes') 

a_function_status_display_with_comment = status_display_with_comment(closure = a_function, comment = 'a comment') 

a_function_status_display_with_comment() 

Заранее спасибо.

P.S .: Я должен обернуть голову вокруг этой цели. Это интересно, учитывая, что это можно сделать рекурсивно, как в Scheme (давно для меня).

+0

Будьте осторожны, заходите слишком далеко с аналогами Схемы. Вы можете писать что-нибудь рекурсивно в Python, как и в Scheme ... но у Python нет оптимизации хвостового вызова, поэтому вы часто этого не хотите. Для полдюжины уровней рекурсии это не проблема, но когда вы начинаете думать о рекурсивном складывании, чтобы сопоставить список произвольной длины, вы, вероятно, ошибаетесь. – abarnert

+0

@abarnert, так как же вы имеете дело с древовидными структурами? Список, содержащийся в списке, который сам по себе является встроенным списком и т. Д., С произвольной глубиной? Мое решение на данный момент: Защита tree_printer (a_tree): \t для элемента a_tree: \t \t если типа (пункт) является список или типа (элемента) является кортеж: \t \t \t tree_printer (пункт) \t \t еще: \t \t \t \t print (item, end = '') –

+0

На схеме, если ваши деревья, как известно, не очень глубоки, вы можете использовать наивную рекурсию, но если они могут быть сколь угодно глубокими, вы должны преобразовать это в рекурсию хвоста. В Python преобразование в хвостовую рекурсию не помогает, поэтому вам вместо этого нужно преобразовать в итеративный код. (Это самое важное отличие, но не единственное. Например, у Python есть понятия, итеративная карта/фильтр/сокращение, все материалы в itertools и т. Д., Чтобы упростить запись итеративного кода, так часто, даже когда рекурсия будет работать, это не самый простой способ сделать это.) – abarnert

ответ

6

Вы хотите функцию, которая возвращает декоратора:

def status_display_with_comment(comment): 
    def decorator(function): 
     def wrapper(): 
      print(comment) 
      print("Entering", function.__name__) 
      result = function() 
      print("Exited", function.__name__) 
      return result 
     return wrapper 

    return decorator 


def a_function(): 
    print('a_function executes') 



a_function_SD_WC = status_display_with_comment('a comment')(a_function) 
a_function_SD_WC() 

работает также:

@status_display_with_comment('a comment') 
def a_function(): 
    print('a_function executes') 


a_function() 

Регулярное прямое декоратор уже возвращает замыкание:

def a_normal_decorator(function): 
    def wrapper(): 
     return function() 
    return wrapper 

wrapper здесь закрытие, так как оно должно содержать function вокруг даже после завершения a_normal_decorator.


Для справки, это как идиоматическое декоратор обычно пишется:

import functools 

def decorator(function): 
    @functools.wraps(function) 
    def wrapper(*a, **kw): 
     return function(*a, **kw) 

    return wrapper 

То есть, он передает аргументы обернутой функции и не выбрасывайте ее возвращаемое значение.

functools.wraps копии из функции завернутые в функцию обертки __name__, __module__, __annotations__ и __doc__ строка документации.

+0

status_display_with_comment ('a comment') (a_function) - ничего себе, я не знал, что это возможно для цепочки вызовов таким образом ... Я думаю, что Python намного больше похож на LISP, чем я себе представлял. Так что это своего рода любовный ребенок C и LISP. Спасибо, человек, уже за 2 часа здесь - надо ложиться спать. Python - такой замечательный язык - у меня не было много интересного программирования годами! –

+0

'Специальные случаи не являются достаточно сложными, чтобы нарушать правила. Вы можете позвонить что угодно, потому что почему бы и нет? :) Очень рад, что тебе это нравится! –

+0

Это не очень lisp-like: мой круглые скобки не сломаны. Я бы сказал, что единственный язык, который я использовал, когда синтаксис правильно ограничивает вас, - это PHP (я думаю о '$ value = get_array_of_some_sort() [0]'). –