2010-09-19 29 views
6

Piszę kod, aby określić nazwę, do której przypisany jest obiekt. Ma to na celu ogólne debugowanie i dalsze zapoznanie się z obiektami wewnętrznymi Pythona.Uzyskiwanie dostępu do nazwy tworzonego obiektu przypisanego do

Mam to ustrukturyzowane jako dekorator klas, tak aby wszystkie wystąpienia tej klasy miały zarejestrowane nazwy, jeśli jest to możliwe. Kod jest dość długi, więc nie opublikuję go, chyba że zostanie poproszony. Ogólna technika jest następujący chociaż

  1. ozdobić klasę __init__ metodę z kodem, aby robić to, co chcę

  2. ustawić caller = inspect.currentframe().f_back i otwartej inspect.getframeinfo(caller).filename i wysłać go do ast.parse. Nie robię tutaj żadnego sprawdzania błędów, ponieważ (1) jest to tylko do debugowania/profilowania/hakowania (2) ten dokładny proces został "właśnie" zakończony lub kod nie został uruchomiony. Czy jest z tym jakiś problem?

  3. znaleźć instancję ast.Assignment który powoduje, że aktualnie wykonywany __init__ sposób uruchomić

  4. jeśli len(assignment.targets) == 1 to istnieje tylko jedna pozycja na lewej stronie, i mogę uzyskać nazwę z targets[0].id. W prostej formie, takiej jak a = Foo(), assignment.value jest instancją ast.Call. jeśli jest to literał (na przykład lista), to value będzie tą listą i kaucją, ponieważ obiekt, który mnie interesuje, nie jest przypisany do nazwy.

Jaki jest najlepszy sposób, aby potwierdzić, że jest w rzeczywistości assignment.value.functype(obj).__call__ obiektu, który mnie interesuje. Jestem pewien, że mam pewność, że to „gdzieś tam” lub kod nawet nie działał. Po prostu potrzebuję, aby był na najwyższym poziomie. Oczywistym wyjściem jest chodzić i upewnić się, że nie zawiera żadnych połączeń wewnętrznych. Wtedy mam gwarancję, że mam imię. (Moje rozumowanie jest poprawne, nie jestem pewien, czy są to jego założenia). To nie jest idealne, ponieważ jeśli interesuje mnie Foo, może to spowodować odebranie a = Foo(Bar()), ponieważ nie wiem, czy to jest a = Bar(Foo()).

Oczywiście mogę tylko sprawdzić assignment.value.func.id ale wtedy ktoś mógłby zrobić Foobar = Foo lub coś więc nie chcę polegać na tym zbyt mocno

Każda pomoc będzie mile widziana. Jak zawsze, interesują mnie inne sugestie lub problemy, które mogą mi przeszkadzać.

Jestem również zaskoczony, że po prostu wymyśliłem znacznik "python-internals".

+0

+ wiele - Python jest fajny! – katrielalex

Odpowiedz

2

AST nie może udzielić Ci tej odpowiedzi. Spróbuj użyć frame.f_lasti, a następnie zaglądając do kodu bajtowego. Jeśli następna linia nie jest STORE_FAST, masz połączenia wewnętrzne lub coś innego, niż inne proste zadanie, którego szukasz.

def f(): 
    f = sys._getframe() 
    i = f.f_lasti + 3 # capture current point of execution, advance to expected store 
    print dis.disco(f.f_code, i) 
0

nie wiem o ile pomoc ta jest, ale czy za pomocą połączenia z locals()? Zwraca wartość dict, która zawiera nazwę i wartość wszystkich zmiennych lokalnych.

Na przykład:

s = '' 
locals() 
>>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', '__name__': '__main__', '__doc__': None} 
t = s # I think this is what is of most importance to you 
locals() 
>>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', 't': '', '__name__': '__main__', '__doc__': None} 

Więc można przechodzić tego słownika i sprawdzić, które zmienne mają (jak ich wartości) Przedmiotem typu, którego szukasz.

Tak jak powiedziałem, nie wiem, na ile pomoc jest ta odpowiedź, ale jeśli potrzebujesz wyjaśnienia, zostaw komentarz, a ja postaram się odpowiedzieć jak najlepiej.

+0

To nie działa, ponieważ 'locals()' zawsze odnosi się do ramki, do której jest wywoływana, szukam jednej klatki w górę. Mogę to uzyskać za pomocą 'sys._getframe()' dla 'inspect.currentfrmae'. Problem polega na tym, że 'foo = bar()' nie tworzy wpisu w 'locals()' (odwołując się do klatki, w której odbywa się przypisanie) dopóki nie pojawi się _tąd 'bar .__ init __()'. Ale to jest odpowiednie miejsce na nazwę, ponieważ mogę po prostu zrobić to z dekoratorem na 'barze' zamiast umieszczać kod po _every_ assignment. – aaronasterling

+0

@AaronMcSmooth: Czy wywołanie globals() zamiast lokalnych rozwiązać ten problem – inspectorG4dget

+1

@ InspectoG4det Nie z tego samego powodu. 'globals' jest po prostu' frame.f_locals' dla zewnętrznej ramki, więc wpis nie jest umieszczony w 'frame.f_locals' dopóki wewnętrzna ramka (' bar .__ init __() 'w tym przypadku) nie powróci. – aaronasterling

0

nie robię żadnego błędu sprawdzania tutaj, ponieważ (1) to tylko do debugowania/profilowania/hacking (2) dokładny proces ten był „tylko” zakończona lub kod nie zostanie uruchomiony. Czy jest z tym jakiś problem?

Tak:

  1. uruchomić program

  2. jednostkę przeczekać importuje danego modułu skrypt foo.py

  3. Edit foo.py

teraz kod załadowany do Pythona proces nie pasuje do kodu znalezionego na dysku.

Kolejny powód, dla którego demontaż kodu bajtowego może być lepszą techniką.

0

Oto, jak to się robi. Wiele dzięki anonimowemu dawcy wskazówek. Dużo szczęścia w twoim dążeniu do odzyskania reputacji na twoje konto alt.

import inspect 
import opcode 


def get_name(f): 
    """Gets the name that the return value of a function is 
    assigned to. 

    This could be modified for classes as well. This is a 
    basic version for illustration that only prints out 
    the assignment instead of trying to do anything with it. 
    A more flexible way would be to pass it a callback for when 
    it identified an assignment. 

    It does nothing for assignment to attributes. The solution 
    for that isn't much more complicated though. If the 
    instruction after the function call is a a `LOAD_GLOBAL`, 
    `LOAD_FAST` or `LOAD_DEREF`, then it should be followed by 
    a chain of `LOAD_ATTR`'s. The last one is the attribute 
    assigned to. 
    """ 

    def inner(*args, **kwargs): 
     name = None 

     frame = inspect.currentframe().f_back 
     i = frame.f_lasti + 3 

     # get the name if it exists 
     code = frame.f_code 
     instr = ord(code.co_code[i]) 
     arg = ord(code.co_code[i+1]) # no extended arg here. 
     if instr == opcode.opmap['STORE_FAST']: 
      name = code.co_varnames[arg] 
     elif instr in (opcode.opmap['STORE_GLOBAL'], 
         opcode.opmap['STORE_NAME']): 
      name = code.co_names[arg] 
     elif instr == opcode.opmap['STORE_DEREF']: 
      try: 
       name = code.co_cellvars[arg] 
      except IndexError: 
       name = code.co_freevars[arg - len(code.co_cellvars)] 
     ret = f(*args, **kwargs) 
     print opcode.opname[instr] 
     if name: 
      print "{0} = {1}".format(name, ret) 
     return ret 

    return inner 


@get_name 
def square(x): 
    return x**2 

def test_local(): 
    x = square(2) 

def test_deref(): 
    x = square(2) 
    def closure(): 
     y = x 
    return closure 

x = square(2) 
test_local() 
test_deref()() 

To nie powinno być zbyt trudne, aby dowiedzieć się Przyporządkowanie do list_[i] = foo() albo, w tym wartość i przy użyciu frame.f_locals. Te trudne będą literałami i kiedy zostaną przekazane jako argument. Oba te przypadki powinny być dość trudne.

Powiązane problemy