<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pl">
	<id>http://brain.fuw.edu.pl/edu/index.php?action=history&amp;feed=atom&amp;title=TI%2FDekoratory</id>
	<title>TI/Dekoratory - Historia wersji</title>
	<link rel="self" type="application/atom+xml" href="http://brain.fuw.edu.pl/edu/index.php?action=history&amp;feed=atom&amp;title=TI%2FDekoratory"/>
	<link rel="alternate" type="text/html" href="http://brain.fuw.edu.pl/edu/index.php?title=TI/Dekoratory&amp;action=history"/>
	<updated>2026-04-21T20:20:06Z</updated>
	<subtitle>Historia wersji tej strony wiki</subtitle>
	<generator>MediaWiki 1.34.1</generator>
	<entry>
		<id>http://brain.fuw.edu.pl/edu/index.php?title=TI/Dekoratory&amp;diff=1819&amp;oldid=prev</id>
		<title>Jarekz: Utworzono nową stronę &quot; Dekoratory są w miarę ezoteryczną cechą Pythona — w przeciwieństwie do funkcji, klas czy iteratorów nie są powszechną cechą języków programowania. Niemniej...&quot;</title>
		<link rel="alternate" type="text/html" href="http://brain.fuw.edu.pl/edu/index.php?title=TI/Dekoratory&amp;diff=1819&amp;oldid=prev"/>
		<updated>2015-05-23T10:38:53Z</updated>

		<summary type="html">&lt;p&gt;Utworzono nową stronę &amp;quot; Dekoratory są w miarę ezoteryczną cechą Pythona — w przeciwieństwie do funkcji, klas czy iteratorów nie są powszechną cechą języków programowania. Niemniej...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nowa strona&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;br /&gt;
Dekoratory są w miarę ezoteryczną cechą Pythona — w przeciwieństwie do funkcji, klas czy iteratorów&lt;br /&gt;
nie są powszechną cechą języków programowania. Niemniej, warto je omówić mimo wszystko, gdyż są niezwykle&lt;br /&gt;
eleganckie i użyteczne.&lt;br /&gt;
&lt;br /&gt;
Krótko mówiąc, dekorator to obiekt (np. funkcja), który można wywołać przekazując mu jako argument dekorowany obiekt. Tym dekorowanym obiektem może być funkcja lub klasa. Wartość zwrócona przez to wywołanie zostaje użyta zamiast dekorowanego obiektu.&lt;br /&gt;
&lt;br /&gt;
== Składnia ==&lt;br /&gt;
&lt;br /&gt;
Dekoratora używa się wstawiając linijkę zaczynającą się od @ przed definicją dekorowanego obiektu (klasy czy funkcji).&lt;br /&gt;
&lt;br /&gt;
Popatrzmy na przykład:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; def ustaw_atrybut_xxx(funkcja):  # definicja naszego dekoratora&lt;br /&gt;
...     funkcja.xxx = True&lt;br /&gt;
...     return funkcja&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; @ustaw_atrybut_xxx               # wywołanie dekoratora&lt;br /&gt;
... def f():                         # dekorowany obiekt&lt;br /&gt;
...     print &amp;quot;ciao&amp;quot;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; f()                              # wywołanie obiektu zwróconego przez dekorator&lt;br /&gt;
ciao&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; f.xxx                            # dodatkowy atrybut ustawiony przez dekorator&lt;br /&gt;
True&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Definicja funkcji (zaczynająca się od &amp;lt;tt&amp;gt;def&amp;lt;/tt&amp;gt;) tworzy funkcję, czyli obiekt typu &amp;lt;tt&amp;gt;function&amp;lt;/tt&amp;gt;).&lt;br /&gt;
Ten obiekt jest przepuszczany przez funkcję &amp;lt;tt&amp;gt;ustaw_atrybut_xxx&amp;lt;/tt&amp;gt;, która dodaje dodatkowe pole.&lt;br /&gt;
W rezultacie zastosowanie dekoratora powoduje, że pod nazwą &amp;lt;tt&amp;gt;f&amp;lt;/tt&amp;gt; zostaje zachowana oryginalna funkcja,&lt;br /&gt;
ale z dodatkowym atrybutem.&lt;br /&gt;
&lt;br /&gt;
Składnia wywołania dekoratorów jest pomyślana tak, by trudno było przeoczyć fakt, że definiowany obiekt został dekorowany. Niemniej, zanim pojawiła się składnia z &amp;lt;tt&amp;gt;@&amp;lt;/tt&amp;gt;, możliwe było użycie dekoratorów poprzez jawne wywołanie:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; def f():&lt;br /&gt;
...     print &amp;quot;ciao&amp;quot;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; f = ustaw_atrybut_xxx(f)   # równoważne użyciu @ustaw_atrybut_xxx w poprzednim przykładzie&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; f()&lt;br /&gt;
ciao&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; f.xxx&lt;br /&gt;
True&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ten przykład pokazuje też, co dokładnie powoduje wstawienie dekoratora — zachowanie pod nazwą definiowanej funkcji wartości zwracanej przez wywołanie dekoratora z oryginalną funkcja jako argumentem.&lt;br /&gt;
&lt;br /&gt;
== Klasyczne zastosowanie dekoratorów: metody statyczne i klasowe ==&lt;br /&gt;
Funkcje zdefiniowane w klasie tworzą metody. Ich pierwszy argument to zawsze &amp;lt;tt&amp;gt;self&amp;lt;/tt&amp;gt;, magiczny parametr&lt;br /&gt;
za który Python podstawia obiekt na którym wywoływana jest metoda. Niemniej, czasami wygodnie jest zdefiniować&lt;br /&gt;
w klasie funkcję, którą można wywołać bez dostępu do instancji. Aby mieć taką możliwość, definiuje się metodę&lt;br /&gt;
klasową (używając dekoratora &amp;lt;tt&amp;gt;classmethod&amp;lt;/tt&amp;gt;) lub statyczną (używając dekoratora &amp;lt;tt&amp;gt;staticmethod&amp;lt;/tt&amp;gt;).&lt;br /&gt;
Różnica między nimi jest taka, że metoda klasowa ma pierwszy magiczny parametr tak samo jak zwykłe metody, tylko że Python wstawia za niego samą klasę, a nie jedną z jej instancji. Metoda statyczna zachowuje się jak zwykła funkcja,&lt;br /&gt;
i lista argumentów jest przekazywana bez zmian.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; class T(object):&lt;br /&gt;
...     def a(self, x):&lt;br /&gt;
...         print self, x&lt;br /&gt;
...&lt;br /&gt;
...     @classmethod&lt;br /&gt;
...     def b(cls, x):&lt;br /&gt;
...         print cls, x&lt;br /&gt;
...&lt;br /&gt;
...     @staticmethod:&lt;br /&gt;
...     def c(x):&lt;br /&gt;
...         print x&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; t = T()&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; t.a(1)&lt;br /&gt;
&amp;lt;__main__.T object at ...&amp;gt; 1&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; t.b(2)&lt;br /&gt;
&amp;lt;class '__main__.Test'&amp;gt; 2&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; t.c(3)&lt;br /&gt;
3&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; # nie możemy wywołać T.a()&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; T.b(4)&lt;br /&gt;
&amp;lt;class '__main__.Test'&amp;gt; 4&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; T.c(5)&lt;br /&gt;
5&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Istotna różnica między metodą &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; i metodami &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; oraz &amp;lt;tt&amp;gt;c&amp;lt;/tt&amp;gt; jest to, że te drugie można wywołać&lt;br /&gt;
poprzez klasę, bez tworzenia instancji.&lt;br /&gt;
&lt;br /&gt;
Nazwy pierwszego parametru metod &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; i &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt;, czyli &amp;lt;tt&amp;gt;self&amp;lt;/tt&amp;gt; i &amp;lt;tt&amp;gt;cls&amp;lt;/tt&amp;gt;&lt;br /&gt;
powinny być właśnie takie. Użycie nazwy &amp;lt;tt&amp;gt;self&amp;lt;/tt&amp;gt; dla parametru nie będącego pierwszym parametrem&lt;br /&gt;
zwykłej metody prowadzi do konfuzji osoby czytającej kod. Podobnie nazwanie takiego parametru jakkolwiek inaczej jest złamaniem reguł stylu. W przypadku parametru &amp;lt;tt&amp;gt;cls&amp;lt;/tt&amp;gt; reguły nie są aż takie ścisłe, gdyż czasem nazywa się go &amp;lt;tt&amp;gt;klass&amp;lt;/tt&amp;gt;, niemniej użycie słowa nie kojarzącego się od razu z klasą jest również błędem.&lt;br /&gt;
&lt;br /&gt;
== Dekoratory definiowane jako funkcje czy klasy ==&lt;br /&gt;
Zdefiniowany na początku dekorator &amp;lt;tt&amp;gt;ustaw_atrybut_xxx&amp;lt;/tt&amp;gt;, mimo że wystarczający do zademonstrowania składni,&lt;br /&gt;
raczej nie nie ma zastosowania praktycznego. Zanim przejdziemy do definiowania użytecznych dekoratorów,&lt;br /&gt;
omówmy w jaki sposób można to zrobić. Na wstępnie zaznaczyliśmy, że dekorator to obiekt który można wywołać jak funkcję. Jako funkcja był też napisany nasz przykładowy dekorator &amp;lt;tt&amp;gt;ustaw_atrybut_xxx&amp;lt;/tt&amp;gt;. Inny sposób na otrzymanie wywoływalnych obiektów to zdefiniowanie klasy z metodą &amp;lt;tt&amp;gt;__call__&amp;lt;/tt&amp;gt;. Przewaga klasowego podejścia do dekoratorów nad funkcyjnym jest taka, że są one w ten sposób czytelniejsze. Definiowanie dekoratorów przy użyciu funkcji wymaga zastosowania dwu- lub trzykrotnie zagnieżdzonych funkcji. Przy definicji przez klasę analogiczna definicja zawiera po prostu dwie lub trzy metody. Dlatego też, w niniejszym omówieniu, dalsze dekoratory będziemy definiować jako klasy.&lt;br /&gt;
&lt;br /&gt;
Na początek przypomnijmy, jak działa dodanie specjalnej metody &amp;lt;tt&amp;gt;__call__&amp;lt;/tt&amp;gt;. Mianowicie, jeśli mamy obiekt, którego klasa definiuje &amp;lt;tt&amp;gt;__call__&amp;lt;/tt&amp;gt;, to można go użyć jak funkcji i wywoła (dostawiając listę argumentów w nawiasach okrągłych).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; class T(object):&lt;br /&gt;
...     def __init__(self):&lt;br /&gt;
...         print &amp;quot;w __init__&amp;quot;&lt;br /&gt;
...     def __call__(self):&lt;br /&gt;
...         print &amp;quot;w __call__&amp;quot;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; t = T()&lt;br /&gt;
w __init__&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; t()&lt;br /&gt;
w __call__&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Przypomnijmy też, jak działa dodanie metody &amp;lt;tt&amp;gt;__init__&amp;lt;/tt&amp;gt;. Mianowicie, zostaje ona wywołana automatycznie po tym, jak stworzymy nowy obiekt. Tworzenie obiektów w Pythonie przypomina zwykłe wywołanie funkcji, tyle tylko, że zamiast funkcji używa się klasy. Metoda &amp;lt;tt&amp;gt;__init__&amp;lt;/tt&amp;gt; nie zwraca nic.&lt;br /&gt;
&lt;br /&gt;
Reasumując, operacja wywołania dokonana na klasie powoduje stworzenie nowego obiektu i wywołanie &amp;lt;tt&amp;gt;__init__&amp;lt;/tt&amp;gt;, natomiast operacja wywołania dokonana na obiekcie powoduje wywołanie &amp;lt;tt&amp;gt;__call__&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dla przykładu zdefiniujmy &amp;lt;tt&amp;gt;ustaw_atrybut_xxx&amp;lt;/tt&amp;gt; na nowo, tym razem jako klasę.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; class ustaw_atrybut_xxx(object):&lt;br /&gt;
...     def __init__(self):&lt;br /&gt;
...         pass&lt;br /&gt;
...     def __call__(self, funkcja):&lt;br /&gt;
...         funkcja.xxx = True&lt;br /&gt;
...         return funkcja&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; @ustaw_atrybut_xxx()&lt;br /&gt;
... def f():&lt;br /&gt;
...     return 'bonjour'&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; f()&lt;br /&gt;
'bonjour'&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; f.xxx&lt;br /&gt;
True&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nieco inny jest sposób wykorzystania dekoratora — o ile wcześniej po znaku &amp;lt;tt&amp;gt;@&amp;lt;/tt&amp;gt; następowała tylko nazwa funkcji,&lt;br /&gt;
to teraz dostawiliśmy nawiasy, tak by skonstruować obiekt &amp;lt;tt&amp;gt;ustaw_atrybut_xxx&amp;lt;/tt&amp;gt;.o&lt;br /&gt;
&lt;br /&gt;
== Dekorator typu ''trace'' ==&lt;br /&gt;
Zdefiniujmy użyteczny dekorator, który wypisze nazwę wykonywanej funkcji przed i po jej wykonaniu. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;pycon&amp;quot;&amp;gt;&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; class logged(object):&lt;br /&gt;
...     def __init__(self, funkcja):&lt;br /&gt;
...         self.funkcja = funkcja&lt;br /&gt;
...     def __call__(self, *args, **kwargs):&lt;br /&gt;
...         print &amp;quot;wywołanie&amp;quot;, self.funkcja.__name__&lt;br /&gt;
...         x = self.funkcja(*args, **kwargs)&lt;br /&gt;
...         print self.funkcja.__name__, &amp;quot;zwróciła&amp;quot;, x&lt;br /&gt;
...         return x&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; @logged&lt;br /&gt;
... def f():&lt;br /&gt;
...     return g() + g()&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; @logged&lt;br /&gt;
... def g():&lt;br /&gt;
...     return 1&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt; f()&lt;br /&gt;
wywołanie f&lt;br /&gt;
wywołanie g&lt;br /&gt;
g zwróciła 1&lt;br /&gt;
wywołanie g&lt;br /&gt;
g zwróciła 1&lt;br /&gt;
f zwróciła 2&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Tutaj mamy taką sytuację, że nasz obiekt klasy dekorującej podstawiamy za dekorowany obiekt, i wywołanie &amp;lt;tt&amp;gt;f()&amp;lt;/tt&amp;gt; czy &amp;lt;tt&amp;gt;g()&amp;lt;/tt&amp;gt;, wywołuje naprawdę &amp;lt;tt&amp;gt;logged.__call__&amp;lt;/tt&amp;gt;, a dopiero pośrednio oryginalną funkcję.&lt;br /&gt;
&lt;br /&gt;
== Dekoratora zwracające oryginalną funkcję i dekoratory zwracające inny obiekt ==&lt;br /&gt;
&lt;br /&gt;
Dwa poprzednie przykłady różnią się w istotny sposób. Pierwszy dekorator, &amp;lt;tt&amp;gt;ustaw_atrybut_xxx&amp;lt;/tt&amp;gt;, zwraca&lt;br /&gt;
przekazany mu obiekt, lekko tylko go zmieniając. Natomiast drugi dekorator, &amp;lt;tt&amp;gt;logged&amp;lt;/tt&amp;gt;, zwraca inny&lt;br /&gt;
wywoływalny obiekt — instancję &amp;lt;tt&amp;gt;logged&amp;lt;/tt&amp;gt; — który zostaje dowiązany pod nazwą oryginalnej funkcji.&lt;br /&gt;
&lt;br /&gt;
Obie wersje są dozwolone, i obie wersje są użyteczne, w zależności od sytuacji. W pierwszej wersji działanie dekoratora ogranicza się do momentu definicji funkcji i wywołania dekoratora. Oznacza to, że dekorator może ustawić atrybut na funkcji, wypisać komunikat, czy dopisać funkcję do jakiegoś rejestru, ale nie może wpływać na sposób działania&lt;br /&gt;
funkcji. Bardziej interesująca jest druga wersja, gdzie możliwe są nie tylko operacje w trakcie definicji funkcji,&lt;br /&gt;
ale możliwe jest w zasadzie nieograniczone oddziaływanie na każde wywołanie funkcji. Przewagą pierwszej wersji nad drugą jest prostota i wydajność — nie tylko definicja dekoratora jest prostsza, ale również&lt;br /&gt;
* lista funkcji widoczna w komunikacie o wyjątku nie zawiera funkcji dekorującej, której obecność może być niespodzianką dla użytkownika, zwłaszcza że często jest to anonimowa funkcja tworzona przy każdym wywołaniu dekoratora&lt;br /&gt;
* działanie programu jest minimalnie szybsze, bo unikamy niewielkiego narzutu na każde wywołanie.&lt;br /&gt;
&lt;br /&gt;
== Do czego (i czy wogóle) warto używać dekoratorów? ==&lt;br /&gt;
Ponieważ już mniej więcej wiemy jak się definiuje dekoratory, pora odpowiedzieć na pytanie, czy i dlaczego warto je wykorzystywać. W przypadku &amp;lt;tt&amp;gt;logged&amp;lt;/tt&amp;gt;, z całą pewnością moglibyśmy wstawić odpowiednie wyrażenia &amp;lt;tt&amp;gt;print&amp;lt;/tt&amp;gt; na początek i koniec każdej z definiowanych funkcji. Wykorzystanie dekoratorów pozwala na rozdzielenie sfer odpowiedzialności pomiędzy właściwe funkcje (dokonujące &amp;quot;obliczeń&amp;quot;) oraz funkcję wypisującą komunikaty. W przypadku mniej trywialnych funkcji powoduje to uproszczenie kodu i ułatwia jego zrozumienie. Zaletą jest też to, że unikamy duplikacji — zamiast identycznych poleceń &amp;lt;tt&amp;gt;print&amp;lt;/tt&amp;gt; w każdej funkcji, piszemy je tylko raz, a następnie &amp;quot;wstawiamy&amp;quot;, dodając jedną linijkę wywołującą dekorator.&lt;br /&gt;
&lt;br /&gt;
Przykłady funkcjonalności jaką można zaimplementować w formie dekoratorów obejmuje&lt;br /&gt;
* przechowywanie wykonanych wcześniej obliczeń w celu przyspieszenia działania programu,&lt;br /&gt;
* oznaczanie funkcji jako &amp;quot;przestarzałych&amp;quot; {{ang|deprecated}} i wypisywania ostrzeżenia przy pierwszym wywołaniu,&lt;br /&gt;
* pomiar czasu działania funkcji&lt;br /&gt;
* i oczywiście wiele innych&amp;amp;hellip;&lt;br /&gt;
&lt;br /&gt;
==Przydatne linki==&lt;br /&gt;
*[http://wiki.python.org/moin/PythonDecoratorLibrary Python Decorator Library]&lt;br /&gt;
*moduł [http://docs.python.org/dev/library/functools.html functools]&lt;br /&gt;
*[https://portal.g-node.org/python-autumnschool/_media/materials/advanced_python/advanced_python.s5.html Prezentacja o dekoratorach, wyjątkach i context managers]&lt;br /&gt;
*moduł [http://pypi.python.org/pypi/decorator decorator]&lt;/div&gt;</summary>
		<author><name>Jarekz</name></author>
		
	</entry>
</feed>