2009-10-30 25 views
24

Jest to pierwszy przykład, jaki spotykamy, gdy spotykamy się z dekoratorami. Ale nie jestem w stanie zrozumieć, co dokładnie chciałbym.Rejestrowanie w Pythonie przy użyciu dekoratora

Prosty dekorator o nazwie LOG. Powinno to działać tak:

@LOG 
def f(a, b=2, *c, **d): 
    pass 

A wynik powinien być mniej więcej tak:

f(1, pippo=4, paperino='luca') 
===== Enter f ===== 
a = 1 
b = 2 
pippo = 4 
paperino = luca 
===== Exit f ===== 

Gdzie każdy argument przekazany jako parametr do funkcji jest pokazały swoją wartość.

Odkryłem, że problem jest trudniejszy, niż myślałem, głównie z powodu wielu różnych sposobów przekazywania argumentów funkcji (pomyśl o krotkach z * c lub słownikami z ** d).

Próbowałem rozwiązania, ale nie jestem pewien, czy to prawda. To somethink tak:

def LOG(fn): 
    import inspect 
    varList, _, _, default = inspect.getargspec(fn) 
    d = {} 
    if default is not None: 
     d = dict((varList[-len(default):][i], v) for i, v in enumerate(default)) 
    def f(*argt, **argd): 
     print ('Enter %s' % fn).center(100, '=') 
     d.update(dict((varList[i], v) for i, v in enumerate(argt))) 
     d.update(argd) 
     for c in d.iteritems(): 
      print '%s = %s' % c 
     ret = fn(*argt, **argd) 
     print 'return: %s' % ret 
     print ('Exit %s' % fn).center(100, '=') 
     return ret 
    return f 

myślę, że to nie jest tak proste, jak się spodziewałem, ale to dziwne, że nie znaleziono, co chciałem w Google.

Czy możesz mi powiedzieć, czy moje rozwiązanie jest w porządku? Czy możesz zaproponować lepsze rozwiązanie problemu, który zaproponowałem?

Dziękuję wszystkim.

+2

Czy to działa? Jeśli to działa, to jest w porządku. Jeśli nie, to masz problem. Zadaj nam konkretne pytanie dotyczące tego problemu. –

+0

Tak, masz rację. Właściwie to działa. Ale jak napisałem, istnieje wiele złożonych sposobów przekazywania argumentów do funkcji Pythona: używanie krotek, słowników, domyślnych argumentów, ... Nawet jeśli wydaje się, że działa, nie jestem pewien, czy to poprawne implementacje, które będzie działać w każdym przypadku. – Luca

+2

Sam będąc ofiarą padnięcia, czy ta funkcja, w absolutnie ogólnej formie, którą opisujesz, jest niezbędna do dalszego rozwijania aplikacji? Wygląda na to, że sam mechanizm rejestrowania przyciągnął twoją uwagę i wiem z doświadczenia, że ​​może to być okropny zabójca produktywności. –

Odpowiedz

5

Jedyne, co zauważyłem, to że konstrukcja dict((varList[i], v) for i, v in enumerate(argt)), z której korzystasz dwukrotnie, to w rzeczywistości dict(zip(varList,argt)).

Poza tym mam tylko meta-krytykę: Żadna z powyższych nie należy do pliku logu.

Zamiast iść koryta dzienniki

  • sprawdzić, czy funkcje są wywoływane z poprawnymi argumentami użyć twierdzi i debugger.
  • Sprawdź, czy funkcja zwraca poprawne wyniki, które zapisujesz.
+0

czy mógłbyś rozwinąć/podać linki na (1), proszę? –

+2

Dziękujemy za przydatną funkcję zip. Myślę, że masz rację dla meta-krytyki. Rozwijam się na zdalnym komputerze i nie mogę używać debuggera. Tak więc ten mechanizm logowania powinien być sposobem na łatwe debugowanie. – Luca

1

Wszystko jest w porządku w twojej funkcji. Wygląda na to, że jesteś zagubiony z argumentami słów kluczowych: pozycyjnymi i zmiennymi &.

Pozwól mi wyjaśnić: argumenty pozycyjne, a i b w twoim przypadku są obowiązkowe (i mogą mieć wartości domyślne). Inne argumenty są opcjonalne. Jeśli chcesz uczynić argument obowiązkowym lub mieć wartość domyślną, umieść go przed * args i ** kwargs. Ale należy pamiętać, że nie może dostarczyć argument dwukrotnie:

def x(a = 1, b = 2, *args, **kwargs): 
    print a, b, args, kwargs 

>>> x(3, 4, 5, b=6) 
TypeError: x() got multiple values for keyword argument 'b' 

Jest inny sposób, ale nie aż tak czytelne, mają wartości domyślne dla argumentów i nie mają pozycyjnych args:

def x(*args, **kwargs): 
    kwargs.updae({'a': 1, 'b': 2}) 

swoją funkcję analizuje argumenty, ale nie rozumiem, dlaczego piszesz varargs i keywords w _.przechodzi ona argumenty przejrzysty:

def x(a = 1, b = 2, *args, **kwargs): 
    print a, b, args, kwargs 

def y(*args, **kwargs): 
    x(*args, **kwargs) 

>>> y(3, 4, 5, 6) 
3 4 (5, 6) {} 

>>> y(3, 4, 5, b=6) 
TypeError: x() got multiple values for keyword argument 'b' 
0

Znalazłem twój miły rozwiązanie może być nieco poprawić, jeśli wziąć pod uwagę, że ogólna funkcja teoretycznie może zwrócić iterable, w takim przypadku zostanie zgłoszony błąd.

Oto rozwiązanie tego problemu:

Wrap print 'return: %s' % ret w instrukcji if:
if hasattr(ret, "__iter__"): print 'returned iterable' else: print 'return: %s' % ret

ten sposób nie użyć dużo czasu drukowania dużych iterables, ale można to oczywiście zmodyfikować zgodnie z potrzebami. (Również ciąg nie ma atrybutu __iter__, który jest przydatny)

Powiązane problemy