Wednesday, October 29, 2008

Python introspekcje funkcji

Nie wyobrażacie sobie jak byłem zdziwiony kiedy wczoraj na kanale IRC python.pl zobaczyłem, pierwszy raz w życiu - na oczy, implementację introspekcji funkcji.

Funkcja może być potraktowana jak obiekt o zbiorze jakiś właściwości. Weźmy sobie funkcję:

def fun(a,b=1,c='defc'):
"""DOCSTRING"""
print '{0} {1} {2}'.format(a,b,c)

O bebechach samej definicji funkcji może nam dużo powiedzieć już samo:

dir(fun)

func_doc
Zwróci nam dokumentację funkcji # 'DOCSTRING'

func_name
To nic innego jak nazwa funkcji # 'fun'

func_defaults
Krotka podające wartości domyślne funkcji # (1, 'defc')

func_code
Zwróci nam obiekt reprezentujący ciało funkcji. Możemy spróbować go wykonać używając metody exec. Oczywiście próba zakończy się niepowodzeniem ponieważ nie przekazaliśmy do kodu argumentów.

Obiekt ten jest o tyle ciekawy iż sam posiada kilka własności, które mogą pomóc nam dalej diagnozować funkcję. Na przykład:

func_code.co_argcount
Poinformuje nas o tyle ile funkcja przyjmuje parametrów

func_code.co_filename
Powie w jakim pliku została zdefiniowana funkcja

func_code.nlocals
Ilość lokalny zmiennych (łącznie z argumentami)

func_code.co_varnames
Krotka z nazwami lokalnych zmiennych (rozpoczyna się od argumentów funkcji)

Bardzo ciekawą własnością jest func_code.co_flags. Sprawdzając czy zostały dla niej ustawione konkretne bity możemy szybko dowiedzieć się kilku interesujących rzeczy.

Jeżeli w co_flags został ustawiony bit 0x04 Funkcja posługuje się parametrem *arguments. Bit 0x08 oznacza iż nasze obiekt posługuje się **kwarguments zaś 0x20 gdy funkcja jest generatorem.

Pierwszym sposobem, aby sprawdzić czy funkcja posiada, którąś z tych właściwości jest wykonanie ilorazu bitowego z poszukiwaną wartością. Tak np. jeżeli

fun.func_code.co_flags & 0x04

wyjdzie 0 oznacza iż nasza funkcja nie korzysta z *args, jeżeli otrzymamy 0x04 - korzysta.

Więcej szczegółów:
http://docs.python.org/reference/datamodel.html#index-1940
http://docs.python.org/reference/datamodel.html#index-1965

No comments: