Dekoratoren
Der Name Dekorator ist unglücklich gewählt, da Dekoratoren nicht dem GOF Dekorator Pattern entsprechen.
Motivation
Seit Python 2.2 - mit Einführung der New Style Klassen - unterstützt Python neben den Instanzmethoden auch Klassenmethoden und statische Methoden.
Durch die Klassenmethode wird die Methode an die Klasse selbst, durch eine statische Methode an den Namensraum der Klasse gebunden.
Leider war der erste syntaktische Wurf nicht der Beste.
class First(object):
def clsMethod(cls): pass
clsMethod= classmethod(clsMethod)
Mit Python 2.4 wurde die Syntax überarbeitet und erweitert. In Anlehnung an Java wird vor die zu dekoriertende Funktion oder Methode der Dekorator mittels @ referenziert.
Die Klasse Second
class Second(object):
def iMethod(self): print "invoked with class instance"
@classmethod
def cMethod(cls): print "invoked with class"
@staticmethod
def sMethod(): print "invoked through namespace"
erzeugt die folgende Ausgabe.
>>> sec=Second()
>>> sec.iMethod()
invoked with class instance
>>> sec.cMethod()
invoked with class
>>> sec.cMethod()
invoked with class
>>> Second.cMethod()
invoked with class
>>> Second.sMethod()
invoked through namespace
Decoratoren sind ein mächtiges Werkzeug in Python um aspektorientiertzu programmieren.
Definition
Ein Dekorator ist ein aufrufbares Python Objekt, das ein Argument annimmt - die zu dekorierende Funktion.
In Python wird zwischen Signatur bewahrenden und Signatur verändernden Dekoratoren unterschieden. Die klasssischen Beispiele für Signatur veränderten Dekoratoren sind die built-in staticmethod und classmethod. Durch sie wird die Bindung der Methode an die Klasseninstanz aufgehoben.
Vorteile
Dekoratoren erlauben es, deklarativ zu programmieren, indem der Funktion ein Dekorator vorangestellt wird. Dadurch wird die Kernfunktionalität um einen neuen Aspekt erweitert. Insbesondere das Kapseln von Aspekten (Logging, Tracing) in Dekoratoren ermöglicht es, die reine Funktionalität von der erweiterten Funktionalität zu trennen und diese wiederverwendbar zu halten. Aspekte der Programmfunktionalität wie Logging müssen nicht mehr mit der Kerfunktionalität verwoben werden.
@logging
@synchronized
def myFunction( ...
Michele Simionato faßt auf http://www.phyast.pitt.edu/~micheles/python/documentation.html#id5 die Vorteile der Dekoratoren zusammen:
* decorators help reducing boilerplate code;
* decorators help separation of concerns;
* decorators enhance readability and maintenability;
* decorators are very explicit.
Techniken
Implementierung
Ein Dekorator muß ein Python Objekt sein, das mit einem Argument aufgerufen werden kann.
Sowohl Funktionen mit inneren Funktionen (Closures) als auch Objekte (Funktoren), die aufrufbar sind, eignen sich, um Dekoratoren zu implementieren.
Funktion - Closures
def decorator(func):
def closure(*__args,**__kw):
print "Hello", func
try:
return func(*__args,**__kw)
finally:
print "Good Bye", func
return closure
Funktor - callable Objekt
class Decorator(object):
def __init__(self,func):
self.__func = func
def __call__(self,*__args,**__kw):
print "Hello", self.__func
try:
return self.__func(*__args,**__kw)
finally:
print "Good Bye", self.__func
Anwendung
>>> @decorator
... def tdec(a,*b,**c): print a,b,c
...
>>> @Decorator
... def tDec(a,*b,**c): print a,b,c
...
>>> tdec(1,2,l=5)
Hello <function tdec at 0x4f3e668>
1 (2,) {'l': 5}
Good Bye <function tdec at 0x4f3e668>
>>>
>>> tDec(1,2,l=5)
Hello <function tDec at 0x2ad6d9a08488>
1 (2,) {'l': 5}
Good Bye <function tDec at 0x2ad6d9a08488>
Zustandsbehaftete Dekoratoren
Typischerweise bauen Dekoratoren einen Zustand auf. Hierzu bieten sich Funktoren mit statischen Variablen an. Die einfache Funktion present schreibt die Namen aller Anwesenden in eine Datei.
def present(name):
open("/home/grimm/Participant.txt","a").write(name+"\n")
Wer zu spät kommt, wird notiert! Dazu verwende ich die den Funktor ToLate und binde damit die Funktion present neu.
class ToLate(object):
__toLate=[]
def __init__(self,func):
self.__func=func
def __call__(self,name):
import time
ToLate.__toLate.append(time.ctime() + ": " + name)
self.__func(name)
@classmethod
def getAll(cls):
return ToLate.__toLate
>>> present("Du")
>>> present("Ich")
>>> present=ToLate(present)
>>> present("Winter")
>>> present("Kaiser")
>>> for i in ToLate.getAll(): print i
...
Sat Apr 5 18:46:35 2008: Winter
Sat Apr 5 18:46:40 2008: Kaiser
>>>
Funktionskomposition
Die Dekoration von Funktionen läuft auf eine Funktionskompositions hinaus. So wird der Ausdruck
@g
@f
def foo(): pass
auf
foo=g(f(foo).
abgebildet
- Beispiel
>>> def third(f):
... f.__name__ += third.__name__
... return f
...
>>> def second(f):
... f.__name__ += second.__name__
... return f
...
>>> @third
... @second
... def first(): print first.__name__
...
>>> first()
firstsecondthird
Funktionsattribute anpassen
Der Decorator decorator hat noch ein Schönheitsfehler. Er reicht die Funktionsattributte nicht durch.
>>> @decorator
... def test(): pass
...
>>> test()
Hello <function test at 0x2ad26cd4e5f0>
Good Bye <function test at 0x2ad26cd4e5f0>
>>> test.__name__
'closure'
decorateFuncAttr ist ein einfacher Decorator, der einen Decorator dahingegen dekoriert, daß er die Funktionsattribute des Decorators und der zu dekorierenden Funktion durchreicht.
def decorateFuncAttr(decorator):
"decorate the function attributes"
def new_decorator(f):
g = decorator(f)
g.__name__ = f.__name__
g.__doc__ = f.__doc__
g.__dict__.update(f.__dict__)
return g
new_decorator.__name__ = decorator.__name__
new_decorator.__doc__ = decorator.__doc__
new_decorator.__dict__.update(decorator.__dict__)
return new_decorator
- die Funktionsattribute des Decorators
>>> @decorateFuncAttr
... def decorator(func):
... def closure(*__args,**__kw):
... print "Hello", func
... try:
... return func(*__args,**__kw)
... finally:
... print "Good Bye", func
... return closure
...
>>> decorator.__name__
'decorator'
- die Funktionsattribute der zu dekorienden Funktion
>>> @decorator
... def test(): pass
...
>>> test()
Hello <function test at 0x2ad26cd5cc08>
Good Bye <function test at 0x2ad26cd5cc08>
>>> test.__name__
'test'
Beispiele
Die folgendene Beispiele spiegeln typische Beispiele für Dekoratoren wieder.
Für meine Beispiele will ich die Funktion fibonacci verwenden, die die Fibonacci Zahlen berechnet.
def fibonacci(n):
"Return the nth fibonacci number."
if n in (0,1):
return n
return fibonacci(n-1) + fibonacci(n-2)
Ich habe schon mal etwas vorbereitet.
>>> for i in range(0,100,2):
... beg= time.time()
... print "fibonacci(%2d)= %10d: in %10.4f seconds" % (i,fibonacci(i),time.time()-beg)
...
fibonacci( 0)= 0: in 0.0000 seconds
fibonacci( 2)= 1: in 0.0000 seconds
fibonacci( 4)= 3: in 0.0000 seconds
fibonacci( 6)= 8: in 0.0000 seconds
fibonacci( 8)= 21: in 0.0000 seconds
fibonacci(10)= 55: in 0.0001 seconds
fibonacci(12)= 144: in 0.0002 seconds
fibonacci(14)= 377: in 0.0004 seconds
fibonacci(16)= 987: in 0.0011 seconds
fibonacci(18)= 2584: in 0.0029 seconds
fibonacci(20)= 6765: in 0.0076 seconds
fibonacci(22)= 17711: in 0.0200 seconds
fibonacci(24)= 46368: in 0.0394 seconds
fibonacci(26)= 121393: in 0.1020 seconds
fibonacci(28)= 317811: in 0.2676 seconds
fibonacci(30)= 832040: in 0.6980 seconds
fibonacci(32)= 2178309: in 1.8305 seconds
fibonacci(34)= 5702887: in 4.7899 seconds
fibonacci(36)= 14930352: in 12.4315 seconds
fibonacci(38)= 39088169: in 32.6308 seconds
fibonacci(40)= 102334155: in 85.8567 seconds
fibonacci(42)= 267914296: in 227.0623 seconds
fibonacci(44)= 701408733: in 594.6344 seconds
fibonacci(46)= 1836311903: in 1558.6466 seconds
....
KeyboardInterrupt
Gedächnis aufbauen - memoize
class memoized(object):
"""Decorator that caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned, and
not re-evaluated.
"""
def __init__(self, func):
self.func = func
self.__name__= func.__name__
self.cache = {}
def __call__(self, *args):
try:
return self.cache[args]
except KeyError:
self.cache[args] = value = self.func(*args)
return value
except TypeError: # uncachable
return self.func(*args)
@memoized
def fibonacci(n):
"Return the nth fibonacci number."
if n in (0,1):
return n
return fibonacci(n-1) + fibonacci(n-2)
>>> for i in range(0,201,10):
... beg= time.time()
... print "fibonacci(%3d)= %45d: in %12.10f seconds" % (i,fibonacci(i),time.time()-beg)
...
fibonacci( 0)= 0: in 0.0000369549 seconds
fibonacci( 10)= 55: in 0.0000820160 seconds
fibonacci( 20)= 6765: in 0.0000691414 seconds
fibonacci( 30)= 832040: in 0.0000689030 seconds
fibonacci( 40)= 102334155: in 0.0000669956 seconds
fibonacci( 50)= 12586269025: in 0.0000658035 seconds
fibonacci( 60)= 1548008755920: in 0.0000650883 seconds
fibonacci( 70)= 190392490709135: in 0.0000669956 seconds
fibonacci( 80)= 23416728348467685: in 0.0000660419 seconds
fibonacci( 90)= 2880067194370816120: in 0.0000710487 seconds
fibonacci(100)= 354224848179261915075: in 0.0000720024 seconds
fibonacci(110)= 43566776258854844738105: in 0.0000681877 seconds
fibonacci(120)= 5358359254990966640871840: in 0.0000679493 seconds
fibonacci(130)= 659034621587630041982498215: in 0.0000681877 seconds
fibonacci(140)= 81055900096023504197206408605: in 0.0000669956 seconds
fibonacci(150)= 9969216677189303386214405760200: in 0.0000669956 seconds
fibonacci(160)= 1226132595394188293000174702095995: in 0.0000669956 seconds
fibonacci(170)= 150804340016807970735635273952047185: in 0.0000681877 seconds
fibonacci(180)= 18547707689471986212190138521399707760: in 0.0000669956 seconds
fibonacci(190)= 2281217241465037496128651402858212007295: in 0.0000679493 seconds
fibonacci(200)= 280571172992510140037611932413038677189525: in 0.0000669956 seconds
>>> fibonacci(1000)
...
File "<stdin>", line 13, in __call__
File "<stdin>", line 4, in fibonacci
RuntimeError: maximum recursion depth exceeded in cmp
>>> for i in range(0,20001,200):
... beg= time.time()
... print "fibonacci(%5d)= %d \nin %12.10f seconds" % (i,fibonacci(i),time.time()-beg)
...
.
.
.
fibonacci(19800)= 40345206940436314295042729139120459614144379881223162153049978480362014160056661005129017241
640668700336802670930475273181397108497771429743086347829129056684235039853067953448156722991298180378733190957
203497202417176821085040467794918166179805860675932776169843744908716442021731995003435283253463320451502901729
741551954230277175347224057155287262037326081486512995161256751251556710653441551957228260902408150439910956050
686982916064265364296658839303806149063535501482556463552311622305566047086390931110726552435413255631397045223
856858598233036947901417570900824625537459862423483537361413422799314751416212487814577754676382669022710537348
341902856017549317143660608124342787885536042058529108266676514452230634597189967541705619446325488683600069864
390015415130351696842652754460919006308823963342335674908068116994120932322688063872861224803539434800417451065
169421873140477152028533407939231616905475189122834974732277574593969245279902952167169497996102009518784264917
790188314922579905132644103602738435268739871297004160536451850850135000900739650487751913758420645118567090624
894345143527337521368542109864035715955603327189468112523664619841196879017158303058611656264912951567799205230
413948158277891815136531977688527970559004644987726546614762658704790069224324948542617229074543915767920043261
737636587547332835452399593409501184577405620375481465593141259472493935298038545497406278782727899188413610380
733467472461815137404687164169310983060323866177001449400175378569899005032114216009828586342990247168442313077
852803547126143351942116964931350888286890265378123212234347282935495770608555590127480899671624015394755422063
363199955579490070535287854730774777689756192857360494554978149037093308418939187068176841964980879859987790902
601931767710868808168331905231447073451865953497065409753876343937473540649828027825358683156004032211844498095
083614884921654303992887756734450358463473394016734849880324908416755433123174652760556975185061420201252965087
539961485724351714966539921982774695629812109332238689220867889558447398984315640248786990956741797119019192856
650260102145403281059639493258505669127123343596970082977410313043838332570304146216326827189835233711880399779
994744533068341833774356313726440392625646624658946270185872057789734082500570649244526218643529641311039159088
054004979471023690386835120664851218059454441848497881521623651435379320030368856820457375916293091618962784463
148944678977347451765845532539738513126173425207679925501351207298051499000893329086460796667041320583411728519
138760779051385081141536555268941325380066085562765631157242184979054062589629488674512459076367053497200318281
482910944731852730465003722614146270911434927057985866874893771657792931371214776002793241930378606963910986944
026653696477511912009920479133181852283335542611749918288744551667961967721989245498804441210668039914634232644
660377186904347774636953338759109074326516003609360253087185009424843964561295979813230483931893224863320690493
333564380915200883436464993330351690193277854598216130612951863145012873222693508976667700396702669353983668505
544207394288294759353802147612503467163462914769363422044655885602897460031883717325798220135841761663782874443
918837458147919648578498212170326638650361056567296244955603161266324569940563466373606432023268840170563881238
709872523557885114181135898400521183499978361449445318433761212653054967305833001826786855692567083383039787440
001318734202792292984830193491260873790261273605832866052429221685265640101549088198718025465534467340732277948
571665248644080048558001927083871249370685873521008494620141150190648792696352741080354705464713450180599261682
629976637327066138525439583984667355648117212711348540352771857478071099283267196870533925125911769624097950235
998866218066895886491499964625384559788182818936477821390073911791155105766815343879957078556374514944986033341
531863232842141119906757672887846255032595942632470177238341813025653871951108861749826555628967311476830627721
751955698151388684488964072334705269810685438183867999369562745240469416693163000803035366077976357330017047362
0652958678407354688780134235959976797797004653600
in 0.0028040409 seconds
fibonacci(20000)= 25311623237323612422401550035206072917663564858024852789519298419913127817605413152301534234
637588316374434882192110376890336735314627428853297240715551876180269316304491931589227713316423020303319710986
892357808434782585027792002936356518974833096860428609963644435145587721560436914041558195729849717542785131124
879858927182295933294835785314191488053802816242609003629935569166386139399770746850161882585843123291395263935
580968408129704229524185589918557723068824425748555892371652199122382013111847490751373229876560498663053669137
349244258226813389665074638551802362835824098611992123238359478911437654149133450084560220094557042108916377919
112654751677697044773348591098225900537749329784656510238514479206013101062889578943015925020615605281312030727
786774914434209218225907099104486173291561353554646208917884595660815728248895142963506709508242082451706676017
264170911279999999411499130104245320468819582854094684632118975822150754365155840162978745721839079492572862616
086124013796394847131011381204046717321904513278814332010251840275416961241144634886653593858709103314761566658
894598320927103041596370197072979884178487670110854252718755880086714224914340051152883343438377787922823835767
363414144102489940815648302023638205041900745045666125159651346656832893561887275494637328300758118515749615586
692788473632798705953200998446768794571964325359733571283053902904713494802587518128903147797235081042295251617
406439844239786596382330744631003665005719772345084647100781025813048232354365181450744828248129965116141619333
133898896309353201395070759921005610775340282072575742577062782013083026426346781125910918430826657216971178387
264317667411587435542988645609932555476084966868501858046597902171224265351332533714222506844861134573418279116
255171288154473259585479121132423672019906722306813088191959410161560019619547002415765537507376815522568454211
593868583994334500459039751670842528768488480859101569416032934240677930972711288068175149065316524077631183081
623770334632035146575312104131491912135954552803876310306655945891836015753400271729972224890816311447288736218
055286487685113689486395229755390469953957076889389788470846215864735295466789582262550423899987181413030550360
607720038877730384223669138203977485507931781672201933460174300241344961411459918962277418425157189978986272699
182369204534939466582738704732645231191337654476532950228864291749426530146565219094696131849836714314659349654
894255159810675460873423483507242075835444361072940876379750251478462545269384424356449282310278687013948190911
329123974757137875936127583648126875567251464566468789121692742192097081666786681521849415785902019531440305193
819222732526666526717175263186066767545561703793509563420954556127802021999226153927855724817479134355608669954
325786809712439668681100165813956963109225198036858374607953583846180172154681228804422523436845472336685023132
393283526713181306042474604521341218333052843987264385737877984996127609394624279229176592630463330840072080566
319968563155396982340229534522115056756291536378672526950569253452200840200716112205757008412683026389952728421
609942196326845753641801609918848850918582599962996271486144566966614127450405199815755438048474639974223265638
970438037329703974884716449061833101446912436491495423946915249720239351906336728273061165257128829591084342116
524656211447020153366574595321340269152145099608774305958442875853502902345475645748487531102811015459315472258
117634417102174529796681780252864601583246588529041057924724681089961354766372120575081921769109004228269695234
389853320675970934540219240771017842159365396388086244201214597182860594018236142132143260042704717528027256258
109537877138988461442569098351163712350195270131802040301676015670642685738206979488689826309041646851617830880
765069643173037097085740527472044052827859656046776741925698519186436518357552426702936128519206967323205455622
861103321400659127515511101349162562378848440013663666540550797219858167148039524293015580969682022616988370960
903778630177970204880448266288174628668543213567873056356535776198779879981136679289548409720228335057085875619
02023411398915823487627297968947621416912816367516125096563705174220460639857683971213093125
in 0.0027449131 seconds
Programmverlauf verfolgen
Zeitverlauf
def timestamp(func):
"For each invoking of the function print the timestamp"
def wrappedFunc():
print "[%s] %s() called" %( time.ctime(),func.__name__ )
return func()
return wrappedFunc
>>> class Man: pass
...
>>> class Woman: pass
...
>>> @timestamp
... def createMan(): Man()
...
>>> @timestamp
... def createWoman(): Woman()
...
>> for i in range(20):
... random.choice((createMan,createWoman))()
...
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createWoman() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createWoman() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createWoman() called
[Mon Mar 31 22:38:17 2008] createWoman() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createWoman() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createMan() called
[Mon Mar 31 22:38:17 2008] createWoman() called
[Mon Mar 31 22:38:17 2008] createMan() called
Aufrufhäufigkeit
class countcalls(object):
"Decorator that keeps track of the number of times a function is called."
__instances = {}
def __init__(self, f):
self.__f = f
self.__numCalls = 0
countcalls.__instances[f.__name__] = self
def __call__(self, *args, **kwargs):
self.__numCalls += 1
return self.__f(*args, **kwargs)
@staticmethod
def count(f):
"Return the number of times the function f was called."
return countcalls.__instances[f].__numCalls
@staticmethod
def counts():
"Return a dict of {function: # of calls} for all registered functions."
return dict([(f, countcalls.count(f)) for f in countcalls.__instances])
>>> @countcalls
... def createMan(): Man()
...
>>> @countcalls
... def createWoman(): Woman()
...
>>> for i in range(2000):
... random.choice((createMan,createWoman))()
...
>>> countcalls.counts()
{'createMan': 1015, 'createWoman': 985}
>>> createMan()
>>> countcalls.counts()
{'createMan': 1016, 'createWoman': 985}
>>> @countcalls
... def fibonacci(n):
... "Return the nth fibonacci number."
... if n in (0,1):
... return n
... return fibonacci(n-1) + fibonacci(n-2)
...
>>> fibonacci(30)
832040
>>> countcalls.counts()
{'fibonacci': 2692537}
>>> @countcalls
... @memoized
... def fibonacci(n):
... "Return the nth fibonacci number."
... if n in (0,1):
... return n
... return fibonacci(n-1) + fibonacci(n-2)
...
>>> fibonacci(30)
832040
>>> countcalls.counts()
{'fibonacci': 59}
Programmfluß
class traced:
def __init__(self,func):
self.func = func
def __call__(self,*__args,**__kw):
print "entering", self.func
try:
return self.func(*__args,**__kw)
finally:
print "exiting", self.func
>>> @traced
... def a(): b()
...
>>> @traced
... def b(): c()
...
>>> @traced
... def c(): pass
...
>>> a()
entering <function a at 0x2b4ae6259c80>
entering <function b at 0x2b4ae6259c08>
entering <function c at 0x2b4ae6259aa0>
exiting <function c at 0x2b4ae6259aa0>
exiting <function b at 0x2b4ae6259c08>
exiting <function a at 0x2b4ae6259c80>
Profilen
class profiler(object):
"Returns for each invoked function the executing time, depending and undepending on the signature"
__profileTime = {}
__erg=0
def __init__(self,func):
self.func = func
self.__startKey= None
self.__begin= None
def __call__(self,*__args,**__kw):
import time
if ( self.__startKey is None ):
self.__startKey= (__args,__kw)
self.__begin= time.time()
__erg=self.func(*__args,**__kw)
if ( self.__startKey == (__args,__kw)):
self.__actKey= (self.func.__name__,__args,__kw )
abs= time.time() - self.__begin
profiler.__profileTime[str(self.__actKey)]= profiler.__profileTime.setdefault(str(self.__actKey),0) + abs
profiler.__profileTime[self.func.__name__]= profiler.__profileTime.setdefault(self.func.__name__,0) + abs
self.__startKey= None
return __erg
@staticmethod
def sum():
return profiler.__profileTime
>>> @profiler
... def testMal(a,*args,**kargs): pass
...
>>> testMal(1)
>>> testMal(2)
>>> testMal(1,(1,2,3))
>>> testMal(1,(1,2,3),b=5)
>>> for i in range(10000): testMal(3)
...
>>> profiler.sum()
{"('testMal', (1, (1, 2, 3)), {'b': 5})": 8.106231689453125e-06, "('testMal', (1, (1, 2, 3)), {})": 9.059906005859375e-06,
"('testMal', (3,), {})": 0.021481037139892578, 'testMal': 0.021514415740966797, "('testMal', (2,), {})": 8.106231689453125e-06,
"('testMal', (1,), {})": 8.106231689453125e-06}
>>> @profiler
... def fibonacci(n):
... "Return the nth fibonacci number."
... if n in (0,1):
... return n
... return fibonacci(n-1) + fibonacci(n-2)
...
>>> fibonacci(20)
6765
>>> for i in profiler.sum().items(): print i
...
("('testMal', (1, (1, 2, 3)), {'b': 5})", 8.106231689453125e-06)
("('testMal', (1, (1, 2, 3)), {})", 9.059906005859375e-06)
("('testMal', (3,), {})", 0.021481037139892578)
('testMal', 0.021514415740966797)
("('testMal', (2,), {})", 8.106231689453125e-06)
("('fibonacci', (20,), {})", 0.068626165390014648)
("('testMal', (1,), {})", 8.106231689453125e-06)
('fibonacci', 0.068626165390014648)
Logging
class logger:
"Write a log entry, including the signature, for each invoked function"
def __init__(self,func):
self.func = func
self.__logFile= open("/home/grimm/test/logger","a")
def __call__(self,*__args,**__kw):
import time
import inspect
logMessage= "invoked" + str(self.func) + " with " +
str(inspect.getargvalues( inspect.currentframe() )[3:] ) + " at " + str(time.ctime())
print logMessage
print >> self.__logFile , logMessage
self.__logFile.flush()
return self.func(*__args,**__kw)
>>> @logger
... def funcTest(a,*args,**kwargs): pass
...
>>> funcTest(5)
invoked<function funcTest at 0x6e9e60> with ({'self': <__main__.logger instance at 0x6ed5f0>,
'inspect': <module 'inspect' from '/usr/local/python/2.5.1/lib/python2.5/inspect.py'>,
'_logger__kw': {}, '_logger__args': (5,),
'time': <module 'time' from '/usr/local/python/2.5.1/lib/python2.5/lib-dynload/time.so'>},)
at Tue Apr 1 23:05:12 2008
>>> funcTest(5,2,3,4,abcd=4)
invoked<function funcTest at 0x6e9e60> with ({'self': <__main__.logger instance at 0x6ed5f0>,
'inspect': <module 'inspect' from '/usr/local/python/2.5.1/lib/python2.5/inspect.py'>,
'_logger__kw': {'abcd': 4}, '_logger__args': (5, 2, 3, 4),
'time': <module 'time' from '/usr/local/python/2.5.1/lib/python2.5/lib-dynload/time.so'>},)
at Tue Apr 1 23:06:55 2008
Synchronisation
def synchronized(func):
"Synchronize the decorated function"
def wrapper(self,*__args,**__kw):
try:
rlock = self._sync_lock
except AttributeError:
from threading import RLock
rlock = self.__dict__.setdefault('_sync_lock',RLock())
rlock.acquire()
try:
return func(self,*__args,**__kw)
finally:
rlock.release()
Annotationen
def attrs(**kwds):
"Enrich the decorated function with attributes"
def decorate(f):
for k in kwds:
setattr(f, k, kwds[k])
return f
return decorate
>>> @attrs(version="2.2",author="Guido van Rossum",test=lambda:"inner function")
... def func(): pass
...
>>> func.version
'2.2'
>>> func.author
'Guido van Rossum'
>>> test.test()
'inner function'
Vor- und Nachbedingungen einer Funktionen
def accepts(*types):
"Check for each invoked function the right number and types of the arguments"
def check_accepts(f):
assert len(types) == f.func_code.co_argcount
def new_f(*args, **kwds):
for (a, t) in zip(args, types):
assert isinstance(a, t), \
"arg %r does not match %s" % (a,t)
return f(*args, **kwds)
new_f.func_name = f.func_name
return new_f
return check_accepts
def returns(rtype):
"Check the right type of the return value"
def check_returns(f):
def new_f(*args, **kwds):
result = f(*args, **kwds)
assert isinstance(result, rtype), \
"return value %r does not match %s" % (result,rtype)
return result
new_f.func_name = f.func_name
return new_f
return check_returns
>>> @accepts(int,float)
... def intFloat(a,b): pass
...
>>> intFloat(1,1.0)
>>> intFloat(1,1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in new_f
AssertionError: arg 1 does not match <type 'float'>
>>> @returns(list)
... def retList(a): return a
...
>>> retList(range(5))
[0, 1, 2, 3, 4]
>>> retList(tuple(range(5)) )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in new_f
AssertionError: return value (0, 1, 2, 3, 4) does not match <type 'list'>
>>>
>>> class MyInt(int): pass
...
>>> @returns(MyInt)
... @accepts(int,MyInt)
... def a(a,b): return b
...
>>> a(4,MyInt())
0
>>> a(MyInt(),MyInt())
0
>>>
Handler registrieren
def onexit(f):
"Register an exit handler"
import atexit
atexit.register(f)
return f
@onexit
def anonymous(): print "Good Bye"
Repository
Python Decorator Library
Die Python Decorator Library versteht sich als Repository für diverse Dekoratoren.
Decorator Modul
Das Decorator Modulvon Michele Simionato ist eine weitere Quelle vieler Dekoratoren.
Performance
- Fragestellung: Wie teuer ist die Indirektion eines Funktors?
Gegeben seien die drei Minimaldekoratoren decoFunc , DecoClassNew und DecoClassOld und die einfache Funktion nullFunction . Wie teuer ist die Indirektion eines Dekorators?
def decoFunc(func):
def closure(*__args,**__kw):
return func(*__args,**__kw)
return closure
class DecoClassNew(object):
def __init__(self,func):
self.__func = func
def __call__(self,*__args,**__kw):
return self.__func(*__args,**__kw)
class DecoClassOld():
def __init__(self,func):
self.__func = func
def __call__(self,*__args,**__kw):
return self.__func(*__args,**__kw)
def nullFunction(): pass
Die Funktion performanceTest(number) führt sowohl die nullFunction direkt aus als auch mit den drei Dekoratoren.
def performanceTest(number):
import time
print "invocation of def nullFunction(): pass"
def nullFunction(): pass
time1= time.time()
for i in xrange(number):
nullFunction()
time2=time.time()
print "time for " + str(number) + " invocations: " + str(time2-time1) ,"\n"
for i in (decoFunc,DecoClassNew,DecoClassOld):
print "decorator "+ str(i) + " of def nullFunction(): pass"
@i
def nullFunction(): pass
time1= time.time()
for i in xrange(number):
nullFunction()
time2=time.time()
print "time for " + str(number) + " invocations: " + str(time2-time1) ,"\n"
- 1.000.000 Aufrufe später
>>> performanceTest(1000000)
invocation of def nullFunction(): pass
time for 1000000 invocations: 0.170559883118
decorator <function decoFunc at 0x2b780b071758> of def nullFunction(): pass
time for 1000000 invocations: 0.491285085678
decorator <class '__main__.DecoClassNew'> of def nullFunction(): pass
time for 1000000 invocations: 0.729209184647
decorator __main__.DecoClassOld of def nullFunction(): pass
time for 1000000 invocations: 0.953649997711
- Ergebnisse:
- 1.000.000 Aufrufe benötigen unter 1 Sekunde
- Zeitverbrauch(Dekorator als Closure) < Zeitverbrauch(Dekorator als new-style class) < Zeitverbrauch(Dekorator als old-style class)
- der direkte Aufruf der Funktion ist um den Faktor 3-6 schneller als der Aufruf über die Dekoratoren
Ausblick
Ab Python 2.6 werden Klassendekoratoren unterstützt. Damit existiert eine weitere, elegante Art, das SingletonPattern umzusetzen.
def singleton(cls):
"Modifies the decorated class to a singleton"
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass: pass
Weiterlesen...