2014-04-29 9 views
6

Chciałbym wiedzieć, czy jest możliwe po introspekcja w CPython:Powiedz, jak argument został odebrany przez funkcję?

>>> def potato(x=69): 
...  if x == 69 and ???: 
...   print '69 was taken from argument defaults' 
...  if x == 69 and ???: 
...   print '69 was passed as positional arg' 
...  if x == 69 and ???: 
...   print '69 was passed as kwarg' 
... 
>>> potato() 
69 was taken from argument defaults 
>>> potato(69) 
69 was passed as positional arg 
>>> potato(x=69) 
69 was passed as kwarg 

Jestem zainteresowany zarówno python2 i python3 odpowiedzi, jeśli są one różne.

Wszelkiego rodzaju Blackmagic udziałem inspect, traceback, pdb, sys._getframe itp dopuszczalna jest tutaj. Oczywiście modyfikowanie argspec funkcji jest niedozwolone.

+1

You” d musi przeanalizować kod źródłowy ramki wywołującej. Które mogłyby być używane 'potato (* args)' lub 'potato (** kwargs)', w którym to momencie musisz przeanalizować, co to jest * in * 'args' lub' kwargs', itd. To staje się skomplikowane * szybko *. –

+0

Ramka wywołująca może również przechowywać 'potato' pod * inną * nazwą lub na liście. Może być zaangażowany dekorator lub 'exec' lub' eval() '. Lub funkcja została wywołana jako wywołanie zwrotne przez rozszerzenie C. *dreszcz*. Możliwości są tutaj nieograniczone. –

+1

Po prostu naciśnij 'u' w' pdb', aby zwiększyć ramkę w stosie i sprawdzić, jaki kod został tam wykonany. Ty jako człowiek możesz znacznie łatwiej sprawdzić, co się tam wydarzyło i co zostało przekazane "ziemniakom". –

Odpowiedz

1

To nie wygląda skontrolować może dostarczyć te informacje bezpośrednio chociaż ramek ciąg nazwie code_context co daje linię źródłowy w których funkcja została wywołana. Problem polega na tym, że trzeba by przerobić mały parser, żeby to zrozumiał.

Oto prostsze rozwiązanie oparte na opakowaniu wokół funkcji, którą chcesz zbadać. Nie zmienia to spec Arg i walidacja arg nie jest zmieniany albo:

import inspect 

def track_args(func): 
    def tracker(*args, **kwargs): 
     r = func(*args, **kwargs) 
     for arg_num,arg_name in enumerate(inspect.getargspec(func)[0]): 
      if arg_name in kwargs: 
       print "%s was provided as keyword arg" % arg_name 
      elif arg_num < len(args): 
       print "%s was provided as positional arg" % arg_name 
      else: 
       print "%s was provided by default value" % arg_name 
     return r 
    return tracker 

@track_args 
def f(a, b, c=30): 
    print "I'm f()" 

f(20, b=10) 
f(20) 

Wynik z ważnych argumentów:

I'm f() 
a was provided as positional arg 
b was provided as keyword arg 
c was provided by default value 

Rezultatów z nieprawidłowymi argumentami:

Traceback (most recent call last): 
    File "test.py", line 21, in <module> 
    f(20) 
    File "test.py", line 5, in tracker 
    r = func(*args, **kwargs) 
TypeError: f() takes at least 2 arguments (1 given) 
+0

Używanie dekoratora to rodzaj oszustwa/unikania prawdziwego pytania. Za kulisami ze składnią @, ponownie przypisujesz nazwę 'f' do' track_args (f) '. Ale mimo to +1 dla Ciebie :) – wim

Powiązane problemy