Zanurkuj w Pythonie/Funkcja getattr
Funkcja getattr
edytuj
Powinniśmy już wiedzieć, że w Pythonie funkcje są obiektami. Ponadto możemy dostać referencję do funkcji bez znajomości jej nazwy przed uruchomieniem programu. W tym celu podczas działania programu należy wykorzystać funkcję getattr
.
getattr
>>> li = ["Larry", "Curly"] >>> li.pop #(1) <built-in method pop of list object at 010DF884> >>> getattr(li, "pop") #(2) <built-in method pop of list object at 010DF884> >>> getattr(li, "append")("Moe") #(3) >>> li ["Larry", "Curly", "Moe"] >>> getattr({}, "clear") #(4) <built-in method clear of dictionary object at 00F113D4> >>> getattr((), "pop") #(5) Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'tuple' object has no attribute 'pop'
- Dzięki temu dostaliśmy referencję do metody
pop
. Zauważmy, że w ten sposób nie wywołujemy metodypop
; aby ją wywołać musielibyśmy wpisać polecenieli.pop()
. Otrzymujemy referencję do tej metody. (Adres szesnastkowy wygląda inaczej na różnych komputerach, dlatego wyjścia będą się nieco różnić.) - Operacja ta także zwróciła referencję do metody
pop
, lecz tym razem nazwa metody jest określona poprzez łańcuch znaków w argumencie funkcjigetattr
.getattr
jest bardzo przydatną, wbudowaną funkcją, która zwraca pewien atrybut dowolnego obiektu. Tutaj wykorzystujemy obiekt, który jest listą, a atrybutem jest metodapop
. - Dzięki temu przykładowi możemy zobaczyć, jaki duży potencjał kryje się w funkcji
getattr
. W tym przypadku zwracaną wartościągetattr
jest metoda (referencja do metody). Metodę tę możemy wykonać podobnie, jak byśmy bezpośrednio wywołalili.append("Moe")
. Tym razem nie wywołujemy funkcji bezpośrednio, lecz określamy nazwę funkcji za pomocą łańcucha znaków. getattr
bez problemu pracuje na słownikach- Teoretycznie
getattr
powinien pracować na krotkach, jednak krotki nie posiadają żadnej metody, dlategogetattr
spowoduje wystąpienie wyjątku związanego z brakiem atrybutu o podanej nazwie.
getattr
na modułach
edytuj
getattr
działa nie tylko na wbudowanych typach danych. Argumentem tej funkcji może być także moduł.
getattr
w apihelper.py
>>> import odbchelper >>> odbchelper.buildConnectionString #(1) <function buildConnectionString at 00D18DD4> >>> getattr(odbchelper, "buildConnectionString") #(2) <function buildConnectionString at 00D18DD4> >>> object = odbchelper >>> method = "buildConnectionString" >>> getattr(object, method) #(3) <function buildConnectionString at 00D18DD4> >>> type(getattr(object, method)) #(4) <type 'function'> >>> import types >>> type(getattr(object, method)) == types.FunctionType True >>> callable(getattr(object, method)) #(5) True
- Polecenie to zwraca nam referencję do funkcji
buildConnectionString
z modułuodbchelper
, który przeanalizowaliśmy w Rozdziale 2. - Wykorzystując
getattr
, możemy dostać taką samą referencję, do tej samej funkcji. W ogólnościgetattr(obiekt, "atrybut")
jest odpowiednikiemobiekt.atrybut
. Jeśli obiekt jest modułem, atrybutem może być cokolwiek zdefiniowane w tym module np. funkcja, klasa czy zmienna globalna. - Tę możliwość wykorzystaliśmy w funkcji
info
. Obiekt o nazwieobject
został przekazany jako argument do funkcjigetattr
, ponadto przekazaliśmy nazwę pewnej metody lub funkcji jako zmiennąmethod
. - W tym przypadku zmienna
method
przechowuje nazwę funkcji, co można sprawdzić pobierając typ zwracanej wartości. - Ponieważ zmienna
method
jest funkcją, więc można ją wywoływać. Zatem w wyniku wywołaniacallable
otrzymaliśmy wartośćTrue
.
getattr
jako funkcja pośrednicząca
edytuj
Funkcja getattr
jest powszechnie używana jako funkcja pośrednicząca (ang. dispatcher). Na przykład mamy napisany program, który może wypisywać dane w różnych formatach (np. HTML i PS). Wówczas dla każdego formatu wyjścia możemy zdefiniować odpowiednią funkcję, a podczas wypisywania danych na wyjście getattr
będzie nam pośredniczył między tymi funkcjami. Jeśli wydaje się to trochę zagmatwane, zaraz zobaczymy przykład.
Wyobraźmy sobie program, który potrafi wyświetlać statystyki strony w formacie HTML, XML i w czystym tekście. Wybór właściwego formatu może być określony w linii poleceń lub przechowywany w pliku konfiguracyjnym. Moduł statsout
definiuje trzy funkcje -- output_html
, output_xml
i output_text
, a wówczas program główny może zdefiniować pojedynczą funkcję, która wypisuje dane na wyjście:
getattr
import statsout
def output(data, format="text"): #(1)
output_function = getattr(statsout, "output_%s" % format) #(2)
return output_function(data) #(3)
- Funkcja
output
wymaga jednego argumentu o nazwiedata
, który ma zawierać dane do wypisania na wyjście. Funkcja ta może także przyjąć jeden opcjonalny argumentformat
, który określa format wyjścia. Gdy argumentformat
nie zostanie określony, przyjmie on wartość"text"
, a funkcja się zakończy wywołując funkcjęoutput_text
, która wypisuje dane na wyjście w postaci czystego tekstu. - Łańcuch znaków
"output_"
połączyliśmy z argumentemformat
, aby otrzymać nazwę funkcji. Następnie pobraliśmy funkcję o tej nazwie z modułustatsout
. Dzięki temu w przyszłości będzie łatwiej rozszerzyć program nie zmieniając funkcji pośredniczącej, aby obsługiwał więcej wyjściowych formatów. W tym celu wystarczy dodać odpowiednią funkcję dostatsout
np.output_pdf
i wywołujemy funkcjęoutput
podając argumentformat
jako"pdf"
. - Teraz możemy wywołać funkcję wyjściową w taki sam sposób jak inne funkcje. Zmienna
output_function
jest referencją do odpowiedniej funkcji wstatsout
.
Czy znaleźliśmy błąd w poprzednim przykładzie? Jest to bardzo niestabilne rozwiązanie, ponadto nie ma tu kontroli błędów. Co się stanie gdy użytkownik poda format, którego nie zdefiniowaliśmy w statsout
? Funkcja getattr
rzuci nam wyjątek związany z błędnym argumentem, czyli podaną nazwą funkcji, która nie istnieje w module statsout
.
Na szczęście do funkcji getattr
możemy podać trzeci, opcjonalny argument, czyli domyślnie zwracaną wartość, gdy podany atrybut nie istnieje.
getattr
import statsout
def output(data, format="text"):
output_function = getattr(statsout, "output_%s" % format, statsout.output_text)
return output_function(data) #(1)
- Ta funkcja już na pewno będzie działała poprawnie, ponieważ podaliśmy trzeci argument w wywołaniu funkcji
getattr
. Trzeci argument jest domyślną wartością, która zostanie zwrócona, gdy podany atrybut, czy metoda nie zostanie znaleziona.
Jak mogliśmy zauważyć, funkcja getattr
jest niezwykle użyteczna. Jest ona sercem introspekcji. W następnych rozdziałach zobaczymy jeszcze więcej przydatnych przykładów.