Python

  • 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#id5die 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
    6
    40668700336802670930475273181397108497771429743086347829129056684235039853067953448156722991298180378733190957
    2
    03497202417176821085040467794918166179805860675932776169843744908716442021731995003435283253463320451502901729
    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
    6
    37588316374434882192110376890336735314627428853297240715551876180269316304491931589227713316423020303319710986
    8
    92357808434782585027792002936356518974833096860428609963644435145587721560436914041558195729849717542785131124
    8
    79858927182295933294835785314191488053802816242609003629935569166386139399770746850161882585843123291395263935
    5
    80968408129704229524185589918557723068824425748555892371652199122382013111847490751373229876560498663053669137
    3
    49244258226813389665074638551802362835824098611992123238359478911437654149133450084560220094557042108916377919
    1
    12654751677697044773348591098225900537749329784656510238514479206013101062889578943015925020615605281312030727
    7
    86774914434209218225907099104486173291561353554646208917884595660815728248895142963506709508242082451706676017
    2
    64170911279999999411499130104245320468819582854094684632118975822150754365155840162978745721839079492572862616
    0
    86124013796394847131011381204046717321904513278814332010251840275416961241144634886653593858709103314761566658
    8
    94598320927103041596370197072979884178487670110854252718755880086714224914340051152883343438377787922823835767
    3
    63414144102489940815648302023638205041900745045666125159651346656832893561887275494637328300758118515749615586
    6
    92788473632798705953200998446768794571964325359733571283053902904713494802587518128903147797235081042295251617
    4
    06439844239786596382330744631003665005719772345084647100781025813048232354365181450744828248129965116141619333
    1
    33898896309353201395070759921005610775340282072575742577062782013083026426346781125910918430826657216971178387
    2
    64317667411587435542988645609932555476084966868501858046597902171224265351332533714222506844861134573418279116
    2
    55171288154473259585479121132423672019906722306813088191959410161560019619547002415765537507376815522568454211
    5
    93868583994334500459039751670842528768488480859101569416032934240677930972711288068175149065316524077631183081
    6
    23770334632035146575312104131491912135954552803876310306655945891836015753400271729972224890816311447288736218
    0
    55286487685113689486395229755390469953957076889389788470846215864735295466789582262550423899987181413030550360
    6
    07720038877730384223669138203977485507931781672201933460174300241344961411459918962277418425157189978986272699
    1
    82369204534939466582738704732645231191337654476532950228864291749426530146565219094696131849836714314659349654
    8
    94255159810675460873423483507242075835444361072940876379750251478462545269384424356449282310278687013948190911
    3
    29123974757137875936127583648126875567251464566468789121692742192097081666786681521849415785902019531440305193
    8
    19222732526666526717175263186066767545561703793509563420954556127802021999226153927855724817479134355608669954
    3
    25786809712439668681100165813956963109225198036858374607953583846180172154681228804422523436845472336685023132
    3
    93283526713181306042474604521341218333052843987264385737877984996127609394624279229176592630463330840072080566
    3
    19968563155396982340229534522115056756291536378672526950569253452200840200716112205757008412683026389952728421
    6
    09942196326845753641801609918848850918582599962996271486144566966614127450405199815755438048474639974223265638
    9
    70438037329703974884716449061833101446912436491495423946915249720239351906336728273061165257128829591084342116
    5
    24656211447020153366574595321340269152145099608774305958442875853502902345475645748487531102811015459315472258
    1
    17634417102174529796681780252864601583246588529041057924724681089961354766372120575081921769109004228269695234
    3
    89853320675970934540219240771017842159365396388086244201214597182860594018236142132143260042704717528027256258
    1
    09537877138988461442569098351163712350195270131802040301676015670642685738206979488689826309041646851617830880
    7
    65069643173037097085740527472044052827859656046776741925698519186436518357552426702936128519206967323205455622
    8
    61103321400659127515511101349162562378848440013663666540550797219858167148039524293015580969682022616988370960
    9
    03778630177970204880448266288174628668543213567873056356535776198779879981136679289548409720228335057085875619
    0
    2023411398915823487627297968947621416912816367516125096563705174220460639857683971213093125
    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

  • Design Patterns in Python

    Abstrakte Methoden und Klassen

    Python kennt keine abstrakte Methoden:
    • es könne keine Interfaceklassen mit klassischen Python Sprachmitteln implementiert werden, hingegen mit Metaklassen: Metaclass for Interface Checking
    • das wesentlich Strukturmittel der Design Patterns ist es aber, gegen das Interface zu programmierenZ

    Zwei Exceptionstypen werden gerne verwendet, um abstrakte Methoden/Klassen zu simulieren

    1. assert 0, "method is abstract"
    2. raise NotImplementedError
    • insbesonder der erste Ansatz ist mit Vorsicht zu genießen, den ein Aufruf des python Interpreters mit -O optimiert die Assertion weg

    Abstrakte Methode

    class Interface:
    def showMe(self):
    raise NotImplementedError

    class Implementation( Interface ):
    def showMe(self):
    print "implemented"

    imp=Implementation()
    int=Interface()
    imp.showMe()

    int.showMe()
    • ergibt:
      implemented
      Traceback (most recent call last):
      File "abstractMethode.py", line 12, in ?
      int.showMe()
      File "abstractMethode.py", line 3, in showMe
      raise NotImplementedError
      NotImplementedError

    Abstrake Klasse

    class Interface:
    def __init__(self):
    print "init Interface"
    raise NotImplementedError
    def showMe(self):
    print "Interface"

    class Implementation( Interface ) :
    def __init__(self):
    print "init Implementation"
    Interface.showMe(self)


    imp=Implementation()

    int=Interface()
    • ergibt:
      init Implementation
      Interface
      init Interface
      Traceback (most recent call last):
      File "abstractClass.py", line 11, in ?
      int=Interface()
      File "abstractClass.py", line 4, in __init__

      raise NotImplementedError
      NotImplementedError

    Dynamische Typisierung

    Template Methode

    Kann mit Python coffeeinhaltiges Getränk gekocht werden?
    • Wie sieht nun der Java Code mit Python aus?
    • Interface Coffeein:
    class Coffeein:

    def anleitung(self):
    self.wasserKochen()
    self.coffeeinHinzufuegen()
    self.wasserAufgiessen()
    self.zutatHinzufuegen()
    self.ruehreUm()

    def wasserKochen(self):
    print "Koche das Wasser"

    def wasserAufgiessen(self):
    print "Giesse das Wasser auf"

    def ruehreUm(self):
    print "Rühre um"

    def coffeeinHinzufuegen(self):
    raise NotImplementedError

    def zutatHinzufuegen(self):
    raise NotImplementedError
    • Implementierung Kaffee
    import Coffeein

    class Kaffee(Coffeein.Coffeein):

    def coffeeinHinzufuegen(self):
    print "Gib das Kaffeepulver in die Tasse"

    def zutatHinzufuegen(self):
    print "Füge den Zucker hinzu"
    • Implementierung Tee
    import Coffeein

    class Tee(Coffeein.Coffeein):

    def coffeeinHinzufuegen(self):
    print "Gib den Teebeutel in die Tasse"

    def zutatHinzufuegen(self):
    print "Gib die Milch hinzu"
    • Anleitung
    import Coffeein
    import Kaffee

    import Tee

    if __name__ == "__main__":
    kaffee = Kaffee.Kaffee()

    tee = Tee.Tee()
    kaffee.anleitung()
    print "----------------------"

    tee.anleitung()
    • ergibt
      Koche das Wasser
      Gib das Kaffeepulver in die Tasse
      Giesse das Wasser auf
      Füge den Zucker hinzu
      Rühre um
      ----------------------
      Koche das Wasser
      Gib dem Teebeutel in die Tasse
      Giesse das Wasser auf
      Gib die Milch hinzu
      Rühre um

    clone Protokoll

    import copy 

    class Cloner(object):
    def clone(self):
    return copy.deepcopy(self)
    • durch das Ableiten der Basisklasse Cloner von object ist Cloner eine new-style class
    • copy.deepcopy löst die neue Instanz von allen Referenzen zu self

    Fabrikmethode old-style class

    klassische - statisch

    Häufig trifft gibt es Sourcecode der Form.
    const Window* getNewWindow( const& Window oldWindow){

    int type= oldWindow.getType();

    Window* newWindow;
    switch( type ){
    case 0:{
    newWindow= new NullWindow;
    break;
    }
    case 1:{
    newWindow= new EinsWindow;
    break;
    }
    case 2:{
    newWindow= new ZweiWindow;
    break;
    }
    case 3:{
    newWindow= new DreiWindow;
    break;
    }

    ...
    default:{
    newWindow= new DefaultWindow;

    }

    return newWindow;
    Dies lässt sich in Java/C++ dadurch lösen, daß die Basisklasse eine virutelle Fabrikmethode clone() erhält, die in jedem abgeleiteten Window überladen werden muß.
    class Window{

    ...
    public:
    virtual Window* clone()=0;

    ....
    }
    class DefaultWindow: public Window{
    DefaultWindow* clone(){ return new DefaultWindow();}

    }
    ...

    const Window* getNewWindow( const& Window oldWindow){

    return Window* newWindow= oldWindow.clone();
    • MOVED TO... nun ist es zu Laufzeit möglich den Konstruktur zu wählen
    • daher spricht man bei dieser Variante der Fabrikmethode gerne vom virtuellen Konstruktor

    Python spezifisch

    Die Anwendung des clone Protokolls hilft uns hier weiter.
    >>> import cloner
    >>>class DefaultWindow( cloner.Cloner ):
    ... pass

    >>> a=DefaultWindow()
    >>> dir(a)
    ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__',

    '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',

    '__weakref__', 'clone']
    >>> b=a.clone()
    >>> a == b

    False
    >>> print a
    <cloner.DefaultWindow object at 0x40356eac>
    >>> print b

    <cloner.DefaultWindow object at 0x403a706c>
    >>> type(a) == type(b)

    True
    • Anmerkungen
      • dir(a): Dictionary von a
      • a==b: sind die beide Objekte identisch; besitzen sie die gleiche Adresse
      • type (a) == type (b) : sind die beiden Objekte vom gleichen Typ

    _getattr_ und _setattr_ hook

    • ähnlicher der
      __init__
      Methode beim Instanziieren eines Objekts, wird beim Zugriff auf ein Attribut eines Objekts gegebenfalls intern die
      __getattr__
      aufgerufen
    • Python unterscheiden nicht zwischen Methoden und Variablen eines Objekts, sondern bezeichnet diese als Attribute
      MOVED TO...jeder Zugriff auf das Objekt stösst implizit eine Prozessieren der
      __getattr__
      Methode an, falls das Attribut nicht explizit aufgelöst werden kann

    Es macht einen Unterschied, ob auf das Attribut lesend oder schreibend zugegriffen wird. Ein kleines Beispiel soll dies verdeutliche.

    class TestClass:

    def __init__(self):
    self.__notPrivate=10

    def __getattr__(self,attrib):
    print "__getattr__: " , str( attrib )
    return attrib
    • Nutzung
    >>> test= TestClass()

    >>> dir(test )
    __getattr__: __members__
    __getattr__: __methods__

    ['_TestClass__notPrivate', '__doc__', '__getattr__', '__init__', '__module__']
    • mittels dir(test) werden die Attribute
      TestClass__notPrivate
      und
      __getattr__
      im dem Objektdictionary nachgeschlagen
    >>> print _TestClass__notPrivate
    10
    >>> test.vier
    __getattr__: vier

    'vier'
    • das Attribut _TestClass__notPrivate wird direkt ausgegeben
      MOVED TO... dies Attribut ist nicht privat, sondern nur gemangelt
    • die Attribute vier wird als Fallback im Dictionary gesucht, da es nicht direkt referenziert werden kann

    Proxy

    • Ein Proxy ist ein Stellvertreter für eine Komponente, der sich wie die Komponente verhält aber diese um zusätzliche Funktionalität erweitert.
      Klassischerweise leiten sich der Stellvertreter und die Komponente von einer abstrakten Basisklasse ab.
      Aufrufe des Clienten sprechen das Interface, den Proxy, an, der das an seine Implementierung delegiert.
      Exemplarisch kann man dies im Klassendiagramm sehen.
    • Eine Proxy Klasse ist in Python schnell programmiert.
    • Diese soll ein im Initialisizer mitgegebens Objekt obj kapseln und alle lesenden Zugriffe auf deren Attrinute mitprotokollieren.
    class CountAccessProxy:

    def __init__(self, obj):
    self._obj = obj
    self.countAccess={}

    def __getattr__(self, attrib):
    self.countAccess[attrib]= self.countAccess.setdefault(attrib,0) +1
    return getattr(self._obj, attrib)
    • angewandt, sofern man die old-style Klassen benützt
    >>> import CountAccess

    >>> proxy=CountAccess.CountAccessProxy([])
    >>> proxy.countAccess
    {}
    >>> proxy.extend([i for i in range(10)])

    >>> proxy.count(3)
    1
    >>> proxy.index(3)

    3
    >>> proxy.insert(5,["string"])
    >>> for i in proxy:

    ... print i,
    ...
    0 1 2 3 4 ['string'] 5 6 7 8 9

    >>> print proxy[4]
    4
    >>> print proxy[5:0:-1]

    ["['string']", '4', '3', '2', '1', '0']

    >>> proxy.countAccess
    {'count': 2, 'index': 1, '__getslice__': 1, 'extend': 1, '__getitem__': 2, 'insert': 2, '__iter__':

    1, '__len__': 1}
    • ALERT! Dies Idiom lässt sich nicht naiv auf new-style Klassen anwenden, da hier nicht notwendigerweise
      __getAttr__
      prozessiert wird.
      Die new-style class Anwendung des Proxy-Patterns ist im Python-Cookbook beschrieben.

    Singleton Pattern - old-style class

    • wird nicht nur den Zugriff auf eine Attribute eines Objekts, sondern auch auf die zu erzeugenden neuen Objekte gekapselt, so erhält man vollkommene Kontrolle über die zu kapselnde Implementierung
    • damit lässt sich die Anzahl der Instanzen einer Klasse kontrollieren
    class Singleton:

    """ A python singleton """

    class __impl:
    """ Implementation of the singleton interface """
    def getId(self):
    """ Test method, return singleton id """
    return id(self)

    # storage for the instance reference
    __instance = None

    def __init__(self):
    """ Create singleton instance """
    # Check whether we already have an instance
    if Singleton.__instance is None:
    # Create and remember instance
    Singleton.__instance = Singleton.__impl()

    def __getattr__(self, attr):
    """ Delegate access to implementation """
    return getattr(self.__instance, attr)

    def __setattr__(self, attr, value):
    """ Delegate access to implementation """
    return setattr(self.__instance, attr, value)
    • die Singleton Klasse fungiert als Proxy, die alle Aufrufe an die innere Klasse __impl delegiert
    • da Singleton.__instance eine Klassenvariable ist, kann im Objektinitialisierer
      __init__()
      sichergestellt werden, daß es nur eine Implementierung - je Klasse - geben kann
    • getattr(self.__instance, attr) ist äquivalent zu self.__instance.attr
    • Anwendung:
    >>> handler1= Singleton()

    >>> handler2= Singleton()
    >>> print "Handler: ",id(handler1 ), "Implementation: " ,handler1.getId()

    Handler: 1077243884 Implementation: 1077243436
    >>> print "Handler: ",id(handler2 ), "Implementation: " ,handler2.getId()

    Handler: 1077243564 Implementation: 1077243436
    • MOVED TO... die Id des Handlers ändert sich, während die Id der Implementierung identisch bleibt

    Funktionsobjekte

    • Python kennt keine case Anweisung
    • dies stellt aber keinen Nachteil dar, den die Kombination von Dictionaries und Lambda- Funktionen (anonyme Funktionen) erlaubt mächtigere Konstrukte
    >>> select={

    ... "+": (lambda x,y: x+y),

    ... "-": (lambda x,y: x-y),

    ... "*": (lambda x,y: x*y),

    ... "/": (lambda x,y: x/y) }

    >>> for i in ("+","-","*","/"):
    ... select[i](3,4)

    ...
    7
    -1
    12
    • die Lambda-Funktionen sind Funktionskörper ohne Namen
    • sie verkörperen in diesem Anwendungsfall eine Strategie, wie Werte verrechnet werden sollen

    Strategy Pattern

    • Strategien stellen Familien von Algorithmen dar, die in Objekten gekapselt sind, so daß sie zu Laufzeit ausgetauscht werden können ( vgl. Strategy Pattern )

    Anwendung

    • sortiere eine Liste von Strings nach verschiedenen Kriterien - Prädikaten
    compare= {"lt": lambda x,y: cmp(x,y),                         # lexikograpisch

    "gt": lambda x,y: cmp(y,x),

    "ltLen":lambda x,y: cmp(len(x),len(y)), # Anzahl der Zeichen

    "gtLen":lambda x,y: cmp(len(y),len(x)),

    "igCaseLt":lambda x,y: cmp(x.lower(), y.lower()), # lexikographisch, nicht case sensitive

    "igCaseGt":lambda x,y: cmp(y.lower(),x.lower()),

    "numValueLt":lambda x,y: cmp(int(x),int(y)), # numerische Wert des Strings

    "numValueGt":lambda x,y: cmp(int(y),int(x)),

    }
    • wobei cmp eine built-in Funktion ist:
    >>> print cmp.__doc__

    cmp(x, y) -> integer

    Return negative if x<y, zero if x==y, positive if x>y.
    • compare angewandt ergibt
    >>> for i in ("lt","gt","ltLen","gtLen","igCaseLt","igCaseGt"):

    ... print i,
    ... allStrings.sort(compare.compare[i])

    ... print ":",allStrings
    ...
    lt : ['Dies', 'Teststring', 'ein', 'ist', 'kurzer', 'nur']

    gt : ['nur', 'kurzer', 'ist', 'ein', 'Teststring', 'Dies']

    ltLen : ['nur', 'ist', 'ein', 'Dies', 'kurzer', 'Teststring']

    gtLen : ['Teststring', 'kurzer', 'Dies', 'nur', 'ist', 'ein']

    igCaseLt : ['Dies', 'ein', 'ist', 'kurzer', 'nur', 'Teststring']

    igCaseGt : ['Teststring', 'nur', 'kurzer', 'ist', 'ein', 'Dies']

    >>> for i in ("lt","numValueLt","numValueGt"):
    ... print i,

    ... intList.sort(compare.compare[i])
    ... print ":",intList

    ...
    lt : ['1', '101', '10101', '21', '3', '3', '4', '56']

    numValueLt : ['1', '3', '3', '4', '21', '56', '101', '10101']

    numValueGt : ['10101', '101', '56', '21', '4', '3', '3', '1']

    Statische und Klassenmethoden

    • old-style Klassen ermöglichen es durch self Methoden an Instanzen zu binden
    • new-style Klassen werden um statische und Klassenmethoden ergänzt
      • statische Methoden: entsprechen den statische Methoden in C++/Java
        • sie können ohne Objektreferenz über den Klassenqualifier aufgerufen werden
        • es wird nur eine statische Methode angelegt
      • Klassenmehoden sind ein neues Konzept
        • sie werden wie statische Methoden aufgerufen und auch nur einmal angelegt
        • der wesentliche Unterschied besteht darin, daß sie an die Klasse gebunden werden

    Beispiel

    class HelloBase:

    def helloStatic(name): print "Hello", name
    helloStatic= staticmethod(helloStatic) #1

    def helloClass(cls,name): print "Hello from %s" %cls.__name__,name
    helloClass= classmethod( helloClass ) #2


    class HelloDerived( HelloBase ): pass
    • #1: durch den Ausdrücke #1 wird die Methode helloStatic zur statischen Methode
    • #2: entsprechend wird durch den Ausdruck #2 eine Klassenmethode helloClass definiert
      insbesondere wird hier eine Klassenreferenz durch den Ausdruck cls erzeugt
    • alle Begrüssungen:
    >>> helloBase= HelloBase()
    >>> helloBase.helloStatic("rainer")

    Hello rainer
    >>> HelloBase.helloStatic("rainer")
    Hello rainer
    >>> helloBase.helloClass("rainer")

    Hello from HelloBase rainer
    >>> HelloBase.helloClass("rainer")
    Hello from HelloBase rainer

    >>> helloDerived.helloStatic("rainer")
    Hello rainer
    >>> helloDerived.helloClass("rainer")

    Hello from HelloDerived rainer
    • bemerkenswert ist:
      • alle Methoden, ob statisch oder an die Klasse gebunden, könnnen sowohl über die Klasse als auch über die Instanz aufgerufen werden
      • die Methode def helloClass(cls,name) wird auf die richtige Klasse abgebildet

    Singleton Pattern - new-style class

    • beim Instanziieren einer Klasse wird mittels
      __new__
      ein neues Objekt angelegt, das dann mittels
      __init__
      initialisiert wird
    • folgender Automatismus wird angestossen
      1. die statische Methode
        __new__()
        der Klasse wird aufgerufen. Diese erhält als erstes implizites Argument ( vgl. self ) die Klasse als Argument und gibt eine neue Klasseninstanz zurück
      2. der anschließende Aufruf der Methode
        __init__()
        initialisierte das gerade instanziierte Klassenobjekt
    • TIPdiesen Prozeß, den Speicher erst mittels
      __new__()
      bereitzustellen und mit
      __init__()
      zu initialisieren, entspricht dem Zusammenspiel des Operators new und dem Konstruktor in C++
    • die enge Verwandheit mit C++ unterstreicht das Code Beispiel
    newThing = X.__new__(X, *a, **k)

    if isinstance(newThing,X):
    X.__init__(newThing,*a,**k)
    • denn,
      • __new__()
        entspricht dem operator new
      • __init__()
        enspricht dem Konstruktor in C++, der auch in C++ erst prozessiert wird, falls Speicher angefordert werden konnte
    • Beispiel
    class Minimal(object):             #1

    def __new__(cls): #2
    print "__new__ :",cls
    return object.__new__(cls) #3

    def __init__(self): #4
    print "__init__ :", self
    • #1: erzeuge eine new-style Klasse
    • #2: übergib die Klasse
    • #3: rufe den Klasseninstanziierer der Basisklasse auf und gib eine fertige - nicht initialisierte - Instanz zurück
    • #4: wird direkt durch #3 angestossen um das Objekt zu initialisieren
    Die Ausgabe dazu:
    >>> Minimal()
    __new__ : <class 'NewClass.Minimal'>

    __init__ : <NewClass.Minimal object at 0x4036baac>
    <NewClass.Minimal object at 0x4036baac>
    • das Singleton Pattern ( Guido von Rossum )
    class Singleton(object):

    def __new__(cls, *args, **kwds): #1

    it = cls.__dict__.get("__it__") #2

    if it is not None: #3
    return it
    cls.__it__ = it = object.__new__(cls) #3

    it.init(*args, **kwds)
    return it
    def init(self, *args, **kwds): #4

    pass
    • Anmerkungen:
      • #1: Aufruf der
        __new__()
        Methode mit voller Signatur:
        • cls: Klassennamen ( notwendig )
        • *args: positionsgebundene Argument ( optional )
        • **kwds: namensgebundene Argumente ( optional )
      • #2: prüfe, ob der Eintrag
        __it__
        im Klassendictionary existiert
      • #3: gib gegenfalls die Basisklasseninstanz zurück, oder erzeuge eine neue
      • #4: initialisiere einmalig die Instanz TIPda bei jedem Aufruf von
        __new__()
        implizit
        __init__()
        prozessiert wird, sollte man diese Methode nicht überladen
    • Anwendung
    >>> class MySingleton(Singleton):
    ... def init(self):

    ... print "calling init"
    ... def __init__(self):
    ... print "calling __init__"

    ...
    >>> x = MySingleton()
    calling init
    calling __init__
    >>> y = MySingleton()

    calling __init__
    >>> assert x is y #1
    >>>
    • init()wird im Gegensatz zu
      __init__()
      nur einmal prozessiert
    • #1 beweist die Objektidentität

    Iterator Protokoll

    • Gerne spricht man beim Iterator Protokoll auch von den Komponenten Producer und Consumer.
      • Producer : iterierbare Objekt, das die Elemente zur Verfügung stellt
      • Consumer : Iteratorionscontext, in dem die Elemente konsumiert werden
    • damit selbstdefinierte Klassen in einem Iterationskontext verwendet werden können, müssen sie das Iterator Protokoll umsetzten
    • das Protokoll besteht aus den zwei Konzepten des Iterators und des Iterator Erzeugers
      • der Erzeuger
        __iter__()
        für einen sequentiellen Iterator
      • next(): der Iterator
    • als Beispiel zuerst einen impliziten Iterationskontext
    for i in myContainer: pass
    • stösst folgenden Automatismus an
      1. iter( myContainer ) wird implizipt aufgerufen
      2. iter( myContainer ) ruft myContainer.__iter__() auf, falls diese existiert
      3. myContainer.__iter__() gibt einen Iterator zurück
      4. der Iterator iteriert myContainer, bis die Exceptions StopIteration geworfen wird
    • Neben dem impliziten Iterieren, kann man den Iterator auch explizit auf Trab halten.
    >>> a=[1,2,3]
    >>> i=iter(a)

    >>> print i
    <listiterator object at 0x40356ccc>
    >>> i.next()
    1
    >>> i.next()

    2
    >>> i.next()
    3
    >>> i.next()
    Traceback(most recent call last):

    File "<stdin>", line 1, in ?
    StopIteration

    >>>

    Iterator Pattern - classical

    • folgende kleine Klasse erzeugt aus einem Charakterstream einen Tokenstream
    class TokenIterator:

    def __init__(self,string,re_ex ):
    self.re_token= re_ex
    self.alltokens= self.re_token.split(string)

    self.alltokens= self.alltokens[1::2] # 1

    self.allTokensIndex=0


    def next(self): # 2

    try:
    token= self.alltokens[ self.allTokensIndex ]

    except IndexError: # 3
    raise StopIteration # 4
    self.allTokensIndex += 1

    return token

    def __iter__(self): #5
    return self
      • #0: splite den String string an jedem Token
      • #1: mich interessieren nur die Token
      • #2: durch die Methode next() wird der TokenIterator zum Iterator
      • #3 und #4: im try Block erzeuge ich einen IndexError, der gemäß des Iterator Protokolls auf einen StopIteration Exception umgebogen wird
      • #5:
        __iter__()
        muß laut Spezifikation eine Iterator bereitstellen; dies ist aber gerade TokenIterator, also self
    • jetzt ist es möglich über die Tokens zu iterieren
    >>> import tokenIterator
    >>> import re

    >>> re_word= re.compile(r"(\w+)")
    >>> words= tokenIterator.TokenIterator(open("/etc/passwd").read(), re_word)

    >>> for word in words:
    ... print word
    ...
    root
    x

    0
    0
    root
    bin
    tcsh

    ....

    pop
    bin
    false
    >>> re_num= re.compile(r"(\d+)")

    >>> numbers= tokenIterator.TokenIterator(open("/etc/passwd").read(), re_num)

    >>> for number in numbers:
    ... print number
    ...
    0

    0
    1
    1

    ...

    14
    76
    70
    67

    100

    Iterator Pattern - yield

    • Python 2.3 wurde um das Schlüsselwort yield erweitert
    • dadurch ist es möglich, zustandsbehaftete Funktionen zu implementieren, die als Generatorfunktionen bezeichnet werden

    >>> def IntGenerator():

    ...     for i in range(4):

    ... yield i
    ...
    >>> ints= IntGenerator()
    >>> type(ints)

    <type 'generator'>
    >>> ints.next()
    0
    >>> ints.next()

    1
    >>> ints.next()
    2
    >>> ints.next()
    3

    >>> ints.next()
    Traceback(most recent call last):
    File "<stdin>", line 1, in ?

    StopIteration
    >>> for i in IntGenerator(): print i,
    ...

    0 1 2 3
    >>> a,b,c,d= IntGenerator()

    >>> print a,b,c,d
    0 1 2 3
    • Anmerkungen:
      • yield liefert den Wert des Funktionsaufrufes zurück
      • die Funktion erzeugt einen Iteratorkontext für die Zahlen 0,1,2 und 3
      • die Ausführung der Funktion wird mit der yield Anweisung eingefroren und beim nächsten Durchlauf wieder aufgenommen
      • die Iteration ist mit der Abarbeitung des Funktionskörpers oder einer expliziten return Anweisung beendet
    • Besonderheiten
      • return Anweisungen dürfen keinen Wert zurückgeben
      • yield darf nicht im try Block einer try - finally Exception Behandlung verwendet werden, da es keine Zusicherung gibt, daß finally prozessiert wird
    • nun kann man den TokenIterator deutlich kompakter als Funktion implementieren
    def tokenIterator( string, re_token ):

    re_token= re_token
    alltokens= re_token.split( string )

    for token in alltokens[1::2]:
    yield token
    • die Ausgabe entspricht der der Klasse TokenIterator
    >>> import re
    >>> import tokenIterator

    >>> re_word= re.compile(r"(\w+)")
    >>> for t in tokenIterator.tokenIterator( open("/etc/passwd").read(), re_word ):

    ... print t
    ...
    root

    ...

    >>> re_number= re.compile(r"(\d+)")

    >>> for t in tokenIterator.tokenIterator( open("/etc/passwd").read(), re_number ):

    ... print t
    ...
    0
    0
    1

    ...

    >>>
    • TIP durch die neue Library itertools ist es möglich, die funktionale Programmierung und das Iteratokonzept zu einem mächtigen und performanten Werkzeug zu verbinden

     


  •  

     Eingabe und Ausgabe

    Kommandozeile

    • alle Kommandozeilenargument sind über sys.argv zugänglich
    • sys.argv[0] enthält den Namen der Applikation
    • das Progrämmchen stellt die Kommandozeile dar
    import sys
    for ( index,value ) in enumerate( sys.argv ):
    
       print "sys.argv[%d] = %s" % (index,value)
    • ein Aufruf der Form
    python commandline.py dies ist nur ein Test

    führt zu folgender Ausgabe:

    sys.argv[0] = commandline.py
    sys.argv[1] = dies
    sys.argv[2] = ist
    sys.argv[3] = nur
    sys.argv[4] = ein
    sys.argv[5] = Test
    • TIP die built-in Funktion enumerate ist ein praktisches Handwerkzeug, um sich zu einer Sequenz sowohl den Index wie auch den Wert ausgeben zu lassen
    >>> for (i,v) in enumerate(["eins","zwei","drei"]):
    
    ...     print i, v
    ...
    0 eins
    1 zwei
    2 drei
    • REFACTOR schreibe ein kleines Programm, das RainerGrimm als Kommandozeilenargument erhält und folgende Ausgabe erzeugt:
      Mein Name ist RainerGrimm
    • TIP sobald es anspruchsvoller wird, sollte die Libraries getopt oder insbesondere optparse für das Parsen der Kommandozeile benützen

     

    Dateien

    • das Dateihandling ist schon in der Kernfunktionalität enthalten
    • durch f= open('Datei'[,'Modus']) wird eine Datei geöffnet
    • für Modus gibt es folgende Optionen
      • r (read) fürs Lesen
      • w (write) fürs Schreiben
      • a (append) fürs Anhängen
      • b (binary) für binäre Lesen oder Schreiben
      • + (update) um mit einer Datei sowohl Lesen als auch Schreiben zu können
        beim Kontextwechsel vom Schreiben zum Lesen muss immer erst der Schreibpuffer geflusht werden, um ein definiertes Verhalten zu gewährleisten
    • das folgende Programm besteht aus folgenden Schritten
      • lege die Datei MeineTestDatei zum Schreiben und Lesen an
      • schreibe den String "das ist nur ein Test"
      • flushe den Output MOVED TO... schreibe den String in die Datei
      • setze den Dateizeiger auf den Anfang der Datei
      • lies die ganze Datei ein
    >>> testDatei=open("MeineTestDatei","w+")
    >>> print >> testDatei, "das ist nur ein Test"
    
    >>> testDatei.flush()
    >>> testDatei.seek(0)
    >>> print ( testDatei.readlines() )
    
    ['das ist nur ein Test\n']
    • per Default wird eine Datei zum Lesen geöffnet
    • der Unterschiede zwischen den Modi "r+" und "w+" zum Lesen und Schreiben einer Datei ist, daß durch "w+" eine neue Datei angelegt wird, während mit "r+" die Datei zum Lesen geöffnet wird, also insbesondere vorhanden sein muß

    Methoden

    • das Dateiobjekt unterstützt folgendes Interface

      MethodeBeschreibung
      file.read([n]) liest die ganze Datei, falls kein n angegeben wurde,
      sonst höchstens n Bytes
      file.readline([n]) lies ein ganze Zeile, falls kein n angegeben wurde.
      Andernfalls maximal n Bytes
      file.readlines() lies alle Zeilen und gib sie als Liste zurück
      file.xreadlines() gibt eine zu iterierende Sequenz aller Zeilen zurück, ohne alle Zeilen vollständig im Speicher zu halten
      file.write(s) schreibe den String s
      file.writelines(l) schreibe alle Strings der List l
      file.close() schliesse die Datei
      file.tell() gibt den aktuellen Dateizeiger zurück
      file.seek(offset[,where]) setze den Dateizeiger auf die angegebene Position
      file.isatty() gibt 1 zurück, falls file ein interaktives Terminal ist
      file.flush() flushe den Ausgabepuffer
      file.truncate([size]) verkürzt die Datei auf maximal size Bytes
      file.fileno() gibt den filedescriptor als Integer zurück

     

    Lesen

    • um durch die Zeilen einer Datei zu iterieren, bietet sich bei kleinen Dateien
    for line in file.readlines()

    und für große Dateien der effizienterer Ausdruck

    for line in file

    an, da dieser lazyist

    • mittels der built-in Funktion type sieht man einfach die semantische Unterschiede der drei Methoden, um Dateien zu lesen
    >>> type( open("/etc/passwd").read() )
    <type 'str'>
    
    >>> type( open("/etc/passwd").readlines() )
    <type 'list'>
    >>> type( open("/etc/passwd") )
    
    <type 'file'>
    • die feinen Unterschiede:
      • open(... ).read(): erhalte die Datei als String
      • open(... ).readlines(): erhalte die Datei als Liste von Strings
      • open(... )(): erhalte ein Dateiobjekt
    • REFACTOR gib das Objekt unmittelbar mit print aus (ersetze type durch print);
      entspricht die Ausgabe deiner Erwartung

     

    Schreiben

    • aus Symmetrie entspricht die Ausgabe von read der Eingabe von write bzw. der von readlines der von writelines
    • mittels des Ausgabeoperators >> und der String Formatierung lässt sich die Ausgabe einfach anpassen
    file=open("Ausgabe.txt","w")
    
    a=1
    print >> file, "%d Meine Ausgabe" % a
    ...
    
    file.close()
    • REFACTORkopiere die Datei "/etc/passwd" in dein home unter einem neuen Namen
      • verwende dazu read und write
      • verwende dazu readlines und writelines
      • TIP eine klassische Aufgabe für die Library shutil
    • sowohl readline() aus auch readlines() berücksichtigen das plattformabhängige Zeilenendzeichen
    • um den Dateizeiger mittels seek zu setzen, kann neben dem obligatorischen Offset auch die relative Position angeben, wobei where drei Werte annehmen kann:
      • 0: Offset bezogen auf den Dateianfang
      • 1: Offset bezogen auf die aktuelle Dateizeigerposition
      • 2: Offset bezogen auf das Dateiende
    • der Zustand der Datei ist in Attributen hinterlegt

      AttributBeschreibung
      file.closed 0 falls die Datei geöffnet ist , 2 falls nicht
      file.mode Modus, indem die Datei geöffnet wurde
      file.name Name der Datei

     

    Standard Input, Output und Error

    • die drei Standard Dateiobjekte besitzen die bekannten Integers als file descriptoren
    >>> import sys
    
    >>> sys.stdin.fileno()
    0
    >>> sys.stdout.fileno()
    
    1
    >>> sys.stderr.fileno()
    2
    • das Umleiten von sys.stdout ist dadurch schnell implementiert
    >>> sys.stdout= open("/home/grimm/test3","w")
    
    >>> print 1,2,3
    • mit den Methoden ist es einfach möglich eine Zeile einzulesen
    import sys
    def gets():
        text= ""
    
        while 1:
            c= sys.stdin.read(1)
    
            text += c
            if c == '\n': break
        return text   
    ret=gets()
    
    print "Ausgabe: ",ret,

    Die gleiche Funktionalität bieten die built in Funktion raw_input und inputan.

    • während raw_input die Eingabe als String interpretiert
    s=raw_input("Gib irgend etwas ein: " )
    • evaluiert input die Eingabe
    z=input("Gib eine Zahl ein: " )

    print Anweisung

    • print schreibt auf sys.stdout
    • print erwartet die Argumente als kommaseparierte Liste
    • für jedes Argument wird die interne Funktion str() aufgerufen und ein Leerzeichen eingefügt
    • die print Ausgabe wird durch ein Zeilenendzeichen abgeschlossen, sofern kein Komma an die print Anweisung angehängt wird
    >>> int=1
    >>> float=3.3
    
    >>> string="test"
    >>> print "die Werte :" ,int,float,string
    die Werte : 1 3.3 test

     

    String Formatierung

    • ähnlich zur C sprintf Funktion kann der auszugebende String formatiert werden
    import sys
    def printf(format,*args):
       sys.stdout.write(format % args) 
    • der Modulo Operator s % derzeugt einen formatierten String, mit:
      1. s: Formatstring mit eingebetteten Konvertierungszielen, die mit % beginnen
      2. d: ein Tupel von Objekten oder ein Dictionary
    >>> exclamation="Ni"
    >>> print "The knights who says %s!" % exclamation
    
    The knights who says Ni!
    • die String Konvertierer

      CharakterBedeutung
      s String oder jedes andere Objekt
      r s benutze aber repr() statt str()
      c Zeichen
      d,i Integer
      u Unsigned Integer
      o Oktal
      x,X Hexadezimal in Klein- oder Großbuchstaben
      e,E Floating Point Exponent mit kleinem oder großem e
      f Floating Point Dezimal
      g,G Float Point Dezimal oder Exponent mit kleinem oder großem e
      % Literal %
    • die allgemeine Form eines Konvertierobjekts: %[(name)][flags][widht][.precision]Charakter, mit
      • name: Schlüssel eines Dictionaries
      • flags:
        • -: Linksausrichtung
        • +: + Zeichen soll ausgegeben werden
        • 0: Whitespace als Füllzeichen
      • width: minimale Feldbreite
      • precision:
        • maximale Anzahl an Zeichen eines Strings
        • Nachkommastellen einer Floating Point Zahl
        • mininale Stellenzahl einer Integer
    • die Anzahl der Objekte auf der rechten mit der linke Seite von % müssen übereinstimmen und ein angegebener Schlüssel muß auflösbar sein

    Beispiele

    • die einzelnen Optionen
    >>> a=42
    >>> b=13.142783
    >>> c="hello"
    
    >>> d={"x":13,"y":1.54321,"z":"world"}
    >>> e= 562894829394939070L
    
    >>> print "a is %d" % a
    a is 42
    >>> print "%10d %f" % (a,b)
    
            42 13.142783
    >>> print "%+010d %E"  % (a,b)
    
    +000000042 1.314278E+01
    >>> print "%(x)-10d %(y)0.3g"  % d
    13         1.54
    
    >>> print "%0.4s %s"  % (c,d["z"])
    hell world
    >>> print "e= %d" % e
    e= 562894829394939070
    • Stringformatierung und triple-quoted strings sind ein einfaches Mittel um Textbausteinchen erstellen zu lassen
    examen=""" \
    
    Hallo %(Vorname)s %(Nachname)s.
    
    Sie haben %(Punkte)d von %(Gesamtpunkte)d Punkten erreicht.
    
    Das entspricht der Note %(Note)s.
    
                   Mit freundlichen Grüßen
                   %(Autor)s.
    
    """
    
    print examen %{ "Vorname": "Herbert", "Nachname": "Mustermann", "Punkte": 50, "Gesamtpunkte":130, "Note":"mangelhaft", "Autor":"Rainer Grimm"}    

    ergibt die Ausgabe

    Hallo Herbert Mustermann.
    
    Sie haben 50 von 130 Punkten erreicht.
    Das entspricht der Note mangelhaft.
    
                   Mit freundlichen Grüßen
                   Rainer Grimm.

     

    • REFACTORgib /etc/passwd mit Hilfe von print und Formatstrings in einer übersichtlicheren Form aus
      • TIP die String Funktion split() hilft dir einen String in Komponenten zu splitten
    >>> purifyPasswd( "%10s %2s %6s %6s %25s %25s %15s" )
    
          root  x      0      0                      root                         /      /bin/tcsh
           bin  x      1      1                       bin                      /bin      /bin/bash
        daemon  x      2      2                    Daemon                     /sbin      /bin/bash
            lp  x      4      7           Printing daemon            /var/spool/lpd      /bin/bash
          mail  x      8     12             Mailer daemon   /var/spool/clientmqueue     /bin/false
         games  x     12    100             Games account                /var/games      /bin/bash
        wwwrun  x     30      8         WWW daemon apache           /var/lib/wwwrun     /bin/false
           ftp  x     40     49               FTP account                  /srv/ftp      /bin/bash
        nobody  x  65534  65533                    nobody           /var/lib/nobody      /bin/bash
           irc  x     39  65534                IRC daemon             /usr/lib/ircd      /bin/bash
            at  x     25     25         Batch jobs daemon         /var/spool/atjobs      /bin/bash
       postfix  x     51     51            Postfix Daemon        /var/spool/postfix     /bin/false
           man  x     13     62       Manual pages viewer            /var/cache/man      /bin/bash
          news  x      9     13               News system                 /etc/news      /bin/bash
          uucp  x     10     14  Unix-to-Unix CoPy system                 /etc/uucp      /bin/bash
          ldap  x     76     70         User for OpenLDAP             /var/lib/ldap      /bin/bash
           pop  x     67    100                 POP admin              /var/lib/pop     /bin/false
    
  •  

    Funktionale Programmierung in Python

    Techniken

    Closures

    • innere Funktionen, die den Aufrufkontext konserviert

    Python 2.*

    • mit Python 2.* ist nur der lesende Zugriff auf die Variablen im umgebenden, nicht globalen Scope möglich
    • Beispiel: lesende Closure
    >>> def addWith(first):

    ... def innerFunction(second):
    ... return first + second

    ... return innerFunction
    ...
    >>> a10=addWith(10)

    >>> a15=addWith(15)
    >>> a10(30)
    40
    >>> a15(30)

    45
    >>> a10(130)
    140
    >>>
    • Python unterstützt keine schreibenden Zugriff auf den umgebenden Scope
    • Beispiel: schreibende Closure
    >>> def counter(start):

    ... def memorizeCount():
    ... start+=1
    ... return start

    ... return memorizeCount
    ...
    >>> c=counter(10)

    >>> c()
    Traceback(most recent call last):
    File "<stdin>", line 1, in <module>

    File "<stdin>", line 3, in memorizeCount

    UnboundLocalError: local variable 'start' referenced before assignment
    • der Ausdruck start+=1 definiert die lokale Variable start ein
    • da sie keinen Wert besitzt, kann dieser auch nicht inkrementiert werden

    Python 3.*

    • mit Python 3.0 wurde das Schlüsselwort nonlocal eingeführt, mit der die innere Funktionen den umgebenden, nicht globalen Scope schreibend, referenzieren kann right erweiterte Umsetzung von Closures
    • Beispiel: schreibende Closure
    >>> def counter(start):

    ... def memorizeCount():
    ... nonlocal start
    ... start+=1
    ... return start

    ... return memorizeCount
    ...
    >>> a=counter(10)

    >>> b=counter(-5)
    >>> a(),b()
    (11, -4)

    >>> a(),b()
    (12, -3)
    >>> a(),b()

    (13, -2)

    lambda Funktionen

    Lambda Funktion in Python
    Eine Funktion, die eine beliebige Anzahl von Argument annimmt und den Wert eines Ausdrucks zurückgibt.
    • lambda Funktionen werden, da sie keinen Namen besitzen, auch gerne als anonyme Funktionen bezeichnet
    • Syntax: lambda a,b,.. : expr
    • Beispiel: lesende Closure
    >>> def addWith_(first):
    ... return lambda second: first + second

    ...
    >>> a10=addWith_(10)
    >>> a15=addWith_(15)

    >>> a10(30)
    40
    >>> a15(30)
    45
    >>> a10(130)

    140
    • Beispiel: Erzeugen eines Filter
    def makefilter(keep):

    """ Return a function that takes a string and returns a copy of that
    string consisting only of the characters in 'keep'.
    """
    import string
    # make a string of all chars, and one of all those NOT in 'keep'
    allchars = string.maketrans('', '')

    delchars = ''.join([c for c in allchars if c not in keep])

    # return the function
    return lambda s,a=allchars,d=delchars: s.translate(a, d)
    • in Aktion:
    >>> onlyVocals=makefilter("aeiou")

    >>> onlyVocals("Only for testing purpose.")
    'oeiuoe'
    >>> onlyUpper=makefilter(string.ascii_uppercase)

    >>> onlyUpper("A Few CamelCap Words.")
    'AFCCW'

    Generatoren

    Charakteristisch für funktionale Programmiersprachen ist das Verarbeiten von Listen. Generatoren lassen Listen leicht entstehen.

    yield Funktion

    Producer
    Das Schlüsselwort yield verwandelt eine Funktion in einen Generator, der einen Iterator (vgl. Iterator Protokollzurückgibt.
    • der Iterator besitzt ein Gedächtnis, das mit yield abgefragt werden kann
    • yield liefert den Wert des Funktionsaufrufes zurück
    • die Ausführung der Funktion wird mit der yield Anweisung eingefroren und beim nächsten Durchlauf wieder aufgenommen
    • die Iteration ist mit der Abarbeitung des Funktionskörpers oder einer expliziten return Anweisung beendet right die Exception StopIteration wird geworfen
    • die return Anweisung darf keinen Wert zurückgeben
    • Beispiel: Iterator über alle natürlichen Zahlen
    >>> def generateNaturalNumbers():
    ... i=1
    ... while True:

    ... yield i
    ... i+=1
    ...
    >>> n=generateNaturalNumbers()

    >>> n.next()
    1
    >>> for i in range(100000): n.next()

    ...
    .
    .
    .
    100000
    100001
    >>> n.next()

    100002
    • Beispiel: Aufzählung der Ascii-Zeichen - Verknüpfung einer endlichen und unendlichen Liste
    >>> import string

    >>> n=generateNaturalNumbers()
    >>> zip( string.ascii_letters, n )

    [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7), ('h', 8), ('i', 9), ('j', 10), ('k', 11),

    ('l', 12), ('m', 13), ('n', 14), ('o', 15), ('p', 16), ('q', 17), ('r', 18), ('s', 19), ('t', 20), ('u', 21),

    ('v', 22), ('w', 23), ('x', 24), ('y', 25), ('z', 26), ('A', 27), ('B', 28), ('C', 29), ('D', 30), ('E', 31),

    ('F', 32), ('G', 33), ('H', 34), ('I', 35), ('J', 36), ('K', 37), ('L', 38), ('M', 39), ('N', 40), ('O', 41),

    ('P', 42), ('Q', 43), ('R', 44), ('S', 45), ('T', 46), ('U', 47), ('V', 48), ('W', 49), ('X', 50), ('Y', 51),

    ('Z', 52)]
    Consumer
    Mit Python 2.5 können Generatoren auch Werte annehmen. Damit werden Generatoren zu Coroutinen, den es ist möglich, sie an beliebigen Stellen aufzurufen, zu verlassen oder zu beenden.
    • für einen Generator n gilt:
      • n.next(): Iterator produziert einen Wert
      • n.send(): Iterator konsumiert einen Wert
      • n.close(): beendet die Iteration
    • Beispiel: Generator als Coroutine
    >>> def generateNumbers():
    ... i=1
    ... while True:

    ... val=(yield i)
    ... if (val): i=val

    ... else: i+=1
    ...
    >>> n=generateNumbers()

    >>> n.next()
    1
    >>> n.send(1000)
    1000

    >>> n.next()
    1001
    >>> n.next()
    1002
    >>> n.close()

    >>> n.next()
    Traceback(most recent call last):
    File "<stdin>", line 1, in <module>

    StopIteration

    • David Beazley, Autor von Python Essential Reference und SWIG, zeigt in seinen Präsentationen GeneratorsUK.pdf und Coroutines.pdf die Anwendung von Generatoren bis zum Entwurf eines Multitasking Betriebssystems.

     

    Generator Expressions

    "... generator expressions as a high performance, memory efficient generalization of list comprehensions [1] and generators [2]." (PEP 289)
    • Generator Expressions verbinden die Funktionalität von List Comprehension mit der Bedarfsauswertung von Generatoren.
    • Beispiel:Speicherverbrauchvergleich von list comprehension und generator expressions
      • xrange ist selbst lazy
    >>> listComprehension=   [i for i in xrange(1000)]

    >>> generatorExpression= (i for i in xrange(1000))

    >>> >>> listComprehension
    [0, 1, 2, 3, 4, 5, 6,

    .
    .
    .
    , 995, 996, 997, 998, 999]

    >>> generatorExpression
    <generator object <genexpr> at 0x7ff2485f6910>
    >>> sys.getsizeof( listComprehension )

    9032
    >>> sys.getsizeof( generatorExpression )
    80
    • Beispiel:xrange nachgebaut
      • die Funktion generateOpenRange ist eine Erweiterung der Funktion generateNaturalNumbers
      • sie erzeugt einen Strom von Zahlen
      • sowohl der Startwert als auch die Schrittweite kann vorgegeben werden
    def generateOpenRange(start=1,step=1):

    i=start
    while True:
    yield i
    i+=step
    • Beispiel: Generator Expression in Aktion
    >>> quads= ( n*n for n in generateOpenRange(10) )

    >>> quads.next(), quads.next(), quads.next(),quads.next(), quads.next()

    (100, 121, 144, 169, 196)
    >>> for i in ( 2**n for n in generateOpenRange(10,10) ):

    ... if (i > 2**60): break
    ... print i,

    ...
    1024 1048576 1073741824 1099511627776 1125899906842624 1152921504606846976
    >>> for i in itertools.islice( ( -1.0/n for n in generateOpenRange(start=-1,step=-1) ),1,10):

    ... print i,
    ...
    0.5 0.333333333333 0.25 0.2 0.166666666667 0.142857142857 0.125 0.111111111111 0.1

    >>> quadNumbers= ( x for x in generateOpenRange(1000) if round( math.sqrt( x ))**2 == x )

    >>> quadNumbers.next(), quadNumbers.next(), quadNumbers.next(),quadNumbers.next(), quadNumbers.next()

    (1024, 1089, 1156, 1225, 1296)
    >>> for i in itertools.islice(( x for x in generateOpenRange(1000) if round( math.sqrt( x ))**2 == x ),25,35):

    ... print i,
    ...
    3249 3364 3481 3600 3721 3844 3969 4096 4225 4356

    >>> quadNumbersEndingIn5= ( x for x in generateOpenRange(1000,5) if round( math.sqrt( x )) **2 == x )

    >>> quadNumbersEndingIn5.next(), quadNumbersEndingIn5.next(), quadNumbersEndingIn5.next(),quadNumbersEndingIn5.next(),

    quadNumbersEndingIn5.next()
    (1225, 1600, 2025, 2500, 3025)

    >>> for i in itertools.islice(( x for x in generateOpenRange(start=0,step=5)

    if round( math.sqrt( x )) **2 == x ),100,105):

    ... print i,
    ...
    250000 255025 260100 265225 270400

    >>> itertools.islice(( x for x in generateOpenRange(start=0,step=5)

    if round( math.sqrt( x )) **2 == x ),1000,10001).next()

    25000000
    • der letzte Ausdruck verlangt eine Erklärung
      • iteriert über alle Zahlen x, die durch 5 teilbar sind:
        generateOpenRange(start=0,step=5)
      • filtere alle xheraus, die eine Quadratzahl sind:
        round( math.sqrt( x )) **2 == x
      • bilde einen Slice über die 1000 Zahl der Sequenz, und fordere die Ausgabe an:
        itertools.islice(....),1000,1001).next()

    built-in Funktionen

    Während map, filter und zip aus einer Liste eine neue Liste erzeugen, reduziert reduce die Liste auf den Ausgabewert. Mit Python 3.0 sind map, filter und zip lazy, d.h.: sie erzeugen die resultierende Liste erst auf Bedarf. reduce wird vom built-in in die Bibliothek functools verschoben.
    Diese drei Funktionen map, filter und reducerepräsentieren die drei typischen Anwendungsfälle, mit den Sequenzen verarbeitet werden. Sie sind die drei elementaren funktionalen Bausteine, die funktionle Programmiersprachen auszeichnen.

    map

    • wende eine Funktion sukzessive auf jedes Element der Eingabesequenz an und gib die neuen Werte als Liste zurück
    • Beispiele:
    >>> map( lambda x: x*x, range(10))

    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    >>> import string
    >>> map( lambda x,y : str(x)+y, range(10),string.ascii_uppercase)

    ['0A', '1B', '2C', '3D', '4E', '5F', '6G', '7H', '8I', '9J', 'NoneK', 'NoneL', 'NoneM', 'NoneN', 'NoneO',

    'NoneP', 'NoneQ', 'NoneR', 'NoneS', 'NoneT', 'NoneU', 'NoneV', 'NoneW', 'NoneX', 'NoneY', 'NoneZ']

    >>>

    filter

    • wende Filter sukzessive auf jedes Element der Eingabesequenz an und gib nur die Element zurück, die zu True evaluieren
    • die filternde Funktion wird als Prädikat bezeichnet
    • Beispiel:
    >>> filter( lambda x: x % 2 != 0, range(10))

    [1, 3, 5, 7, 9]
    >>> filter( lambda a : not a.startswith("None") , map(lambda x,y : str(x)+y, range(10),string.ascii_uppercase) )

    ['0A', '1B', '2C', '3D', '4E', '5F', '6G', '7H', '8I', '9J']

    reduce

    • wende eine Funktion, die zwei Argumente verlangt, sukzessive auf das Ergebnis der letzten Iteration und ein Element der Eingabesequenz an
    • bei der ersten Iteration werden die ersten zwei Elemente der Eingabesequenz verarbeitet
    >>> reduce( lambda a,b: a*b,range(1,10))

    362880
    >>> import operator
    >>> reduce( operator.add ,range(10))

    45
    >>> reduce(operator.concat, filter( lambda a : not a.startswith("None") ,map( lambda x,y : str(x)+y, range(10),string.ascii_uppercase)))

    '0A1B2C3D4E5F6G7H8I9J'

    zip

    • fasse Sequenzen zu einer Lister zusammen, wobei die Länge der resultierende Liste dem Minimum der Längen des Sequenzen entspricht
    • Beispiel: Liste von Trippels
    >>> zip(range(1,4),range(101,104),range(1001,10000000))

    [(1, 101, 1001), (2, 102, 1002), (3, 103, 1003)]
    • TIP zip ist sehr hilfreich um aus Listen Dictionaries zu erzeugen
    • Beispiel: List-Dictionary-Idiom
    >>> print accs
    ['root', 'bin', 'daemon', 'lp', 'mail', 'games', 'wwwrun', 'ftp', 'nobody', 'irc',

    'at','postfix', 'man', 'news', 'uucp', 'ldap', 'pop']

    >>> print ids
    ['0', '1', '2', '4', '8', '12', '30', '40', '65534', '39', '25', '51', '13', '9', '10',

    '76', '67']
    >>> d=dict( zip( accs, ids ) )

    >>> d
    {'bin': '1', 'ftp': '40', 'daemon': '2', 'uucp': '10', 'postfix': '51', 'nobody': '65534', 'pop': '67', 'ldap': '76',

    'games': '12', 'at': '25', 'lp': '4', 'news': '9', 'mail': '8', 'wwwrun': '30', 'irc': '39', 'root': '0', 'man': '13'}

    >>> d["man"]
    '13'
    >>> d.keys()
    ['bin', 'ftp', 'daemon', 'uucp', 'postfix', 'nobody', 'pop', 'ldap', 'games', 'at', 'lp', 'news', 'mail', 'wwwrun',

    'irc', 'root', 'man']

    list comprehension

    • list comprehension bietet die Funktionalität von map und filter auf einfache Weise an
    • die expression entspricht der map und die Bedingung der filter Funktion
    • Syntax: zwei äquivalente Ausdrücke
    [ expression for item1 in sequence1
    for item2 in sequence2
    ...

    for itemN in sequenceN
    if condition ]
    s=[]
    for item1 in sequence1:

    for item2 in sequence2:
    ...
    for itemN in sequenceN:

    if condition: s.append(expression)
    • Seiteneffekte
      • list comprehension mit Python 2.* als funktionaler syntatic sugar erzeugt Seiteneffekte !!!!
      • der Wert x ist im folgenden Ausdruck nach der list comprehension im globalen Scope
    grimm@laiard ~ $ /home/grimm/python/Python-2.6/python
    Python 2.6 (r26:66714, Oct 16 2008, 14:40:39)

    [GCC 3.4.6] on linux2
    Type "help", "copyright", "credits" or "license" for more information.

    >>> [ x*x for x in range(10)]
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    >>> print x
    9
    • im Gegensatz zu generator expressions werden list expressions strikt ausgewertet
    • Beispiel: Bedarfsauswertung versus strikt Auswertung
    >>> [x*x for x in range(10) if x%2 != 0]

    [1, 9, 25, 49, 81]
    >>> (x*x for x in range(10) if x%2 != 0)

    <generator object <genexpr> at 0x7ff7876db9b0>

    TIP Das Decorate-Sort-Undecorate Idiom (auch bekannt als Schwartzian transform) hilft, um eine Liste nach einem vorgegebenen Kriterium zu sortieren.
    Sortiere die numerischen Strings nach ihrem numerischen Wert.
    • Beispiel: Naives Sortieren
    >>> strings= [ str(i) for i in range(11,6,-1) ]

    >>> strings
    ['11', '10', '9', '8', '7']

    >>> strings.sort()
    >>> print strings
    ['10', '11', '7', '8', '9']
    • Beispiel: Decorate-Sort-Undecorate
    >>> decorate= [ ( int(i),i) for i in strings ]

    >>> decorate
    [(11, '11'), (10, '10'), (9, '9'), (8, '8'), (7, '7')]

    >>> decorate.sort()
    >>> decorate
    [(7, '7'), (8, '8'), (9, '9'), (10, '10'), (11, '11')]

    >>> undecorate=[ y for x,y in decorate ]
    >>> undecorate

    ['7', '8', '9', '10', '11']

    Es gilt als guter Stil in Python, statt den built-in Funktion map und filter list comprehension zu verwenden.

    Bibliotheken

    functools

    Durch die Methode partialerlaubt Python das teilweise evaluieren von Funktionen.
    • Beispiel: partial Evaluation
    >>> import functools
    >>> def divide(nom,denom): return nom/denom

    ...
    >>> divide(1.0,3)
    0.33333333333333331
    >>> divideBy3= functools.partial(divide,denom=3)

    >>> divideBy3(1.0) == divide(1.0,3)
    True

    itertools

    Die itertools Bibliothek kombiniert functional Tools mit Iteratoren. Die Methoden imap, izip und ifilter sind die lazy Äquivalente zu den built-in Funktionen map, zip und filterund ersetzen diese mit Python 3.* . Das heißt insbesondere, das die Elemente explizit angefordert (consumiert) werden müssen.

    Iteratoren erzeugen

    Dieses Methoden erzeugen in der Regel einen Iterator über einen unendlichen Datenstrom.
    • count(n=0): Natürlichen Zahlen
    • cycle(seq): Wiederholung der Sequenz
    • chain(seq1,seq2,..): (endliche) Verkettung der Iteratoren
    • slice(seq,[start],stop[,end]: endlicher Slice von seq
    • tee(seq,n=2): vervielfältigt eine Sequenz
    • Beispiel: Iteratoren erzeugen
    >>> import itertools
    >>> intFrom5=itertools.count(5)

    >>> intFrom5.next(), intFrom5.next()
    (5, 6)

    >>> i=itertools.cycle("123")
    >>> i.next(),i.next(),i.next(),i.next()

    ('1', '2', '3', '1')
    >>> i=itertools.chain("123",[(1,2),(3,4)])

    >>> i.next(),i.next(),i.next(),i.next()

    ('1', '2', '3', (1, 2))

    >>> list( itertools.islice( itertools.count(),2,20,4) )

    [2, 6, 10, 14, 18]
    >>> iters= itertools.tee(range(5),3)

    >>> first,second,third=list(iters)
    >>> list(first),list(second),list(third)

    ([0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4])

    Durch Anwendung der Funktion list auf einen endlichen Datenstrom, erzwinge ich die strikte Auswertung des Datenstroms.

     

    Funktion auf die Elemente anwenden

    • Beispiel: imap
    >>> itertools.imap(operator.add, [1,2,3],[3,2,1])

    <itertools.imap object at 0x7f956f8bb4d0>
    >>> for i in itertools.imap(operator.add, [1,2,3],[3,2,1]): print i,

    ...
    4 4 4

    Elemente filtern

    Neben dem built-in Äquivalent ifilter, gibt es die zwei Haskell Varianten takewhile und dropwhile, die abhängig von dem Prädikat die Elemente einer iterierbaren Sequenz zurückgeben.
    • für die Sequenz seq gilt:
      • takewhile(predicate, seq): gib die Element solange zurück, solange das Prädikat zu True evaluiert
      • dropwhile(predicate, seq): entferne die Elemente solange, solange das Prädikate zu True evaluiert
    • Beispiel: takewhile/dropwhile Vergleich
    >>> def lessThan20(x): return ( x < 20 )

    ...
    >>> for i in itertools.takewhile(lessThan20,range(10,31)): print i,

    ...
    10 11 12 13 14 15 16 17 18 19

    >>> for i in itertools.dropwhile(lessThan20,range(10,31)): print i,

    ...
    20 21 22 23 24 25 26 27 28 29 30

    >>> list(itertools.takewhile(lessThan20,range(10,31))) + list(itertools.dropwhile(lessThan20,range(10,31)))

    [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]

    Elemente gruppieren

    • groupby(iter,key_func=None):gruppiert alle Elemente aus iter entsprechend ihrem key
      • der Schlüssel sind per Default die iterierten Elemente
      • die Liste iter muß entsprechend dem Schlüssel sortiert sein
      • leider ist itertools.groupby sehr schwierig anzuwenden
    • Beispiel: brain melting with groupby
    >>> import itertools

    >>> testing="00001234445"
    >>> for key, group in itertools.groupby(testing):

    ... for test in group:
    ... print test
    ... print "------"

    ...
    0
    0
    0
    0
    ------
    1
    ------
    2

    ------
    3
    ------
    4
    4
    4
    ------
    5
    ------

    >>> things = [("animal", "bear"), ("animal", "duck"), ("plant", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]

    >>>
    >>> for key, group in itertools.groupby(things, lambda x: x[0]):

    ... for thing in group:
    ... print "A %s is a %s." % (thing[1], key)

    ... print " "
    ...
    A bear is a animal.
    A duck is a animal.

    A cactus is a plant.

    A speed boat is a vehicle.

    A school bus is a vehicle.

    Ausblick

    Auch mit Python 3.0 wurden Pythons funktionale Elemente verbessert. So sind die built-in Funktionen map, filter und lambda durch ihr Äquivalente in der itertools Bibliothek aus Python 2.* ersetzt worden. Diese wenden nun Bedarfsauswertung statte strikte Auswertung an.
    Darüber hinaus findet keine Verschmutzung des globalen Scopes mittels list comprehension mehr statt.
     
  •  

    Funktionen und Funktionale Programmierung

    Funktionen

    Elementare Syntax

    • mittels des Schlüsselworts def wird eine Funktion definiert
    def add(x,y):
      return x+y
    
    ...
    print add(3,4)

    Prinzipien

    • def erzeugt ein Objekt und weist es den Funktionsnamen add zu
    • return sendet ein Objekt an den Aufrufer zurück
    • die Argumente werden mittels call-by-object an die Funktion übergeben 

    Namensauflösung

    • der Funktionskörper besitzt einen eigenen, lokalen Namensraum
      • locals(): gibt den lokalen Namensraum aus
      • globals(): gibt den globalen Namensraum aus  right mit global kann der globale Namensraum referenziert werden
    x=1
    y=1
    def enclosing():
        y=3
    
        def local():
            global x
            x=y
        local()

    ergibt

    >>> enclosing()
    >>> x
    3
    >>> y
    1
    • das Schlüsselwort globalbewirkt zwei Dinge:
      • x aus dem globalen scope wird in den lokalen scope der Funktion local eingeführt, so daß die Anweisung x=y keine neue lokale Variable x erklärt
      • die Zuweisung an x im lokalen Scope bleibt für den globalen Scope erhalten
    • durch y=3 wird y im enclosing unabhängig vom globalen scope neu angelegt

    Die Namensauflösung läßt sich auf die einfache LEGB-Regelreduzieren:

    • LEGB Regel : Local MOVED TO... Enclosing MOVED TO... Global MOVED TO... Built-In
    • TIP in Python 3 kann durch das Schlüsselwort nonlocal auf den umgebenen Scope schreibend zugegriffen werden
    >>> n=5
    >>> def outer(n):
    
    ...    def inner():
    ...       nonlocal n
    ...       n*=2 # bind in scope of outer
    
    ...    inner()
    ...    print (n)
    ...
    >>> outer(100)
    
    200
    >>> print (n)
    5

    Details

    1. Rückgabewert
      1. die Funktion wird explizit über eine return Anweisung verlassen
        MOVED TO... der Rückgabewert bestimmt sich durch das oder die Argumente nach der return Anweisung
      2. die Funktion wird implizit durch das Abarbeiten des Funktionskörpers verlassen
        MOVED TO... der Rückgabwert ist None
    2. Funktionsaufruf
      SyntaxErläuterung
      func(wert) Standardfall: Position bestimmt die Übergabe
      func(name=wert) Schlüsselwort: name bestimmt die Übergabe
    3. Funktionsdefinition
      SyntaxErläuterung
      def func(name) Standardfall: Übereinstimmung durch Position oder name
      def func(name=wert) Angabe eines Defaultwertes
      def func(*name) sammle alle Positionsargumente in einem Tupel name auf
      def func(**name) sammle alle (Schlüssel,Wert) Paare in einem Dictionary name ein

     

    Beispiele

    • Schlüsselworte und Defaultargumente
    def fPos(a,b,c): print a,b,c
    
    
    def fDef(a,b=2,c=3): print a,b,c
    • REFACTOR mögliche Eingaben:
    fPos(1,2,3)
    
    fPos(c=3,b=2,a=1)
    fPos(1,c=3,b=2)
    
    fDef(1)
    fDef(a=1)
    fDef(1,4)
    
    fDef(1,4,5)
    fDef(1,c=6)
    • Beliebige Argumente
    def fTup(*args): print args
    
    
    def fDict(**kargs): print kargs
    
    def fPosTupDict(a,*pargs,**kargs): print a, pargs, kargs
    • REFACTOR mögliche Eingaben
    fTup()
    fTup(1)
    
    fTup(1,2,3,4)
    fDict()
    fDict(a=1,b=2)
    
    fPosTupDict(1,2,3,x=1,y=2)
    • HAND schreibe eine Funktion makeDict , die ein Dictionary aus benamten Argument erzeugt
    >>> newDict= makeDict(red=1, green=2, blue=3)
    
    >>> print newDict
    {'blue': 3, 'green': 2, 'red': 1}
    
    >>> classicDict= {'blue': 3, 'green': 2, 'red': 1}
    
    >>> print classicDict
    {'blue': 3, 'green': 2, 'red': 1}

    TIP dies ist die Funktionalität von **kargs:
    es nimmt Paare von (Argumentname,Argumentwert) an und erzeugt ein Dictionaryeintrag mit dem gequotetenArgumentnamen

    Funktionale Programmierung

    • Python ist eine multiparadigmen Programmiersprache
    • neben der objektorientierte unterstützt sie insbeondere die funktionale Programmierung

    apply (deprecated)

    • erlaubt es eine Funktion mit einem Tupel oder Dictionary aufzurufen
    • Syntax:
    apply( func [,args[,kwargs]] )
    
    >>> def makeSum(x,y,z): return x+y+z
    
    ...
    >>> print makeSum(2,3,4)
    9
    >>> print apply(makeSum , (2,3,4))
    
    9
    >>> l=[2,3,4]
    >>> print apply(makeSum ,l)
    
    9
    >>> print apply(makeSum , range(2,5) )
    
    9
    • seit Python 2.3 ist folgende Syntax vorzuziehen
    >>> print apply.__doc__
    
    apply(object[, args[, kwargs]]) -> value
    
    Call a callable object with positional arguments taken from the tuple args,
    
    and keyword arguments taken from the optional dictionary kwargs.
    Note that classes are callable, as are instances with a __call__() method.
    
    Deprecated since release 2.3. Instead, use the extended call syntax:
        function(*args, **keywords).
    
    >>> l=[2,3,4]
    >>> makeSum( *l )
    
    9
    >>> makeSum( *(range(2,5) ) )
    
    9
    >>> d= {"x":2,"y":3,"z":4}
    
    >>> makeSum(**d)
    9
    >>> makeSum( **{"x":2,"y":3,"z":4})
    
    9
    >>> makeSum( **{"z":2,"x":3,"y":4})
    
    9
    • TIP während * eine Sequenz derefenziert, dereferenziert ** ein Dictionary

    lambda

    • erzeugt eine anonyme Funktion, die an einen Namen gebunden werden kann
    • Syntax:
    lambda args: expression
    a= lambda x,y: x+y
    sum=a(2,3)
    
    #sum=2+3=5
    sum=(lambda x,y,z:x+y+z)(*(2,3,4))
    
    sum=(lambda x,y,z:x+y+z)(**{"x":1,"y":2,"z":3})
    
    #sum=2+3+4=9
    •   Lambda-Funktionen sind insbesondere als Ersatz für benamte Funktionen von Interesse und können so den Blick aufs wesentliche lenken
      • beide select Anweisung sind äquivalent
    selectLam={
    
      "+": (lambda x,y: x+y),
    
      "-": (lambda x,y: x-y),  
      "*": (lambda x,y: x*y),
    
      "/": (lambda x,y: x/y)
    
      }

    und

    def add_(x,y): return x+y
    
    def sub_(x,y): return x-y
    
    def mul_(x,y): return x*y
    
    
    def div_(x,y): return x/y
    
    selectDef={
    
      "+": add_ ,
      "-": sub_ ,  
      "*": mul_ ,
    
      "/": div_
      }
    • HAND das Ergebnis
    >>>for i in ("+","*","-","/"):
    
    ...     print i + ": " , selectLam[i](3,4) , selectDef[i](3,4)
    
    ...
    +: 7 7
    *: 12 12
    -: -1 -1
    
    /: 0 0
    • TIP die Technik, Referenzen auf Methoden oder Funktionen zu hinterlegen, ist unter dem Namen Dispatch Table Wikipedia:Dispatch_table bekannt

    map

    • wende eine Funktion auf die Elemente einer Sequenz an und gib eine neue Liste zurück
    • Syntax:
    t= map(func,s)
    def func(x): return x*x
    quad= map( func , range(1,10) )
    
    quad=[1, 4, 9, 16, 25, 36, 49, 64, 81]
    
    quadLambda=map(lambda x: x*x, range(1,10))
    
    quadLambda=[1, 4, 9, 16, 25, 36, 49, 64, 81]
    • erwartet die Funktion n Argumente, dann müssen auch n Sequenzen angegeben werden
    >>> map(lambda x,y: x+y , range(2,8), range(2,8))
    
    [4, 6, 8, 10, 12, 14]
    • natürlich können auch benamte Funktionen in map Ausdrücken verwendt werden
    >>> map(abs,range(-10,10))
    
    [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    • falls die Funktion None ist, wird die Identität verwendet
    • damit ist es auch möglich, Sequenzen zu einer Listen zusammenzuführen, wobei fehlende Einträge mit None aufgefüllt werden
    >>> map(None, range(1,10 ))
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    >>> map(None, range(1,4),range(101,105),range(1001,1010))
    
    [(1, 101, 1001), (2, 102, 1002), (3, 103, 1003), (None, 104, 1004), (None, None, 1005), (None, None, 1006), (None, None, 1007), (None,None, 1008), (None, None, 1009)]
    
    >>> map(lambda x,y,z: x+y+z, range(1,4),range(101,104),range(1001,1004))
    
    [1103, 1106, 1109]
    • HELP Wie kann man einfacher die Liste [1, 2, 3, 4, 5, 6, 7, 8, 9] erzeugen?
    • REFACTOR berechne 2 ** i für i= -10 bis 10

    zip

    • zusammenführen von Sequenzen zu einer Liste, wobei die Länge der resultierende Liste dem Minimum der Längen des Sequenzen entspricht
    • Syntax:
    zip(s1,s2,...,sn) 
    >>> zip(range(1,4),range(101,104),range(1001,10000000))
    
    [(1, 101, 1001), (2, 102, 1002), (3, 103, 1003)]
    • TIP zipist sehr hilfreich um aus Listen Dictionaries zu erzeugen
      • erzeuge aus der Liste aller accounts und Ids eine Map
    >>> print accs
    ['root', 'bin', 'daemon', 'lp', 'mail', 'games', 'wwwrun', 'ftp', 'nobody', 'irc',
    'at','postfix', 'man', 'news', 'uucp', 'ldap', 'pop']
    
    >>> print ids
    ['0', '1', '2', '4', '8', '12', '30', '40', '65534', '39', '25', '51', '13', '9', '10',
    '76', '67']
    >>> dict( zip( accs, ids ) )
    
    {'bin': '1', 'ftp': '40', 'daemon': '2', 'uucp': '10', 'postfix': '51', 'nobody': '65534',
    
     'pop': '67', 'ldap': '76', 'games':'12', 'at': '25', 'lp': '4', 'news': '9', 'mail': '8',
    
     'wwwrun': '30', 'irc': '39', 'root': '0', 'man': '13'}
    • REFACTOR erzeuge das gleiche Dictionary aus
    ['root', '0', 'bin', '1', 'daemon', '2', 'lp', '4', 'mail', '8', 'games', '12', 'wwwrun',
    
    '30','ftp', '40','nobody', '65534', 'irc', '39', 'at', '25', 'postfix', '51', 'man', '13',
    
    'news','9', 'uucp', '10', 'ldap', '76', 'pop', '67']
    >>> from funktionenUndFunktionaleProgrammierung import *
    >>> myPasswd= dictFromList( passList )
    
    {'bin': '1', 'ftp': '40', 'daemon': '2', 'uucp': '10', 'postfix': '51', 'nobody': '65534',
    
    'pop': '67', 'ldap': '76', 'games':'12', 'at': '25', 'lp': '4', 'news': '9', 'mail': '8',
    
     'wwwrun': '30', 'irc': '39', 'root': '0', 'man': '13'}
    
    >>> myPasswd["pop"]
    '67'

    reduce

    • wende eine Funktion mit zwei Eingabeparametern sukzessive auf die Elemente einer Sequenz an, um einen Wert auszugeben
      der erste Eingabeparameter stelle das Ergebnis des vorherigen Rechenschrittes dar
    • Syntax:
    reduce( func, s)
    def sumup(x,y): return x+y
    sumup=reduce( sumup, range(1,11) )
    
    sumup= reduce(lambda x,y: x+y, range(1,11) )
    
    #sumup=55
    fak= reduce(lambda x,y: x*y, range(1,11) )
    
    #fak=3628800
    • REFACTORerzeuge aus der Liste von Wörter ["Only","for","testing","purpose","."] einen Satz mit Hilfe der
      • reduce Funktion
      • join Funktion der String Klasse

    filter

    • wende einen Filter auf eine Sequenz an und gib nur die Elemente in einer Sequenz zurück, für die der Filter True ergibt
    • Funktionen, die ihre Eingabe zu True oder False evaluieren, werden Prädikate genannt
    • Syntax:
    filter( func, s ) 
    a=filter(lambda x: x < 5, range(1,10))
    
    a=[1, 2, 3, 4]
    b=filter(lambda x: round( math.sqrt( x ))**2 == x , range(1,20000))  
    
    b=[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400,
    
    441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369,
    
    1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401, 2500, 2601,2704,
    
    2809,  2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, 4096, 4225, 4356,4489,
    
    4624, 4761, 4900, 5041, 5184, 5329, 5476, 5625, 5776, 5929, 6084, 6241, 6400, 6561, 6724,
    
    6889, 7056, 7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, 9216, 9409,
    
    9604, 9801, 10000, 10201, 10404, 10609, 10816, 11025, 11236, 11449, 11664, 11881, 12100,
    
    12321, 12544, 12769, 12996, 13225, 13456, 13689, 13924, 14161, 14400, 14641, 14884, 15129,
    
    15376, 15625, 15876, 16129, 16384, 16641, 16900, 17161, 17424, 17689, 17956, 18225, 18496,
    
    18769, 19044, 19321, 19600, 19881]
    • REFACTOR gib alle Zeilen der Datei /etc/serviceszurück, die nicht mit # beginnen
      • TIP die Liste über alle Zeilen einer Datei erhält du mittels open("Dateiname").readlines()
      • Beispiel
    >>> new= filterSequenz(open("/etc/services").readlines() ,"#")  
    
    >>> for i in new[::1000]  : print i,
    
    ...
    tcpmux          1/tcp           # TCP Port Service Multiplexer
    tpip            594/tcp         # TPIP
    saism           1436/tcp        # Satellite-data Acquisition System 2
    
    jwclient        1938/tcp        # JetVWay Client Port
    vytalvaultvsmp  2547/udp        # vytalvaultvsmp
    amt-cnf-prot    3054/tcp        # AMT CNF PROT
    
    pda-gate        4012/udp        # PDA Gate
    bctp            8999/tcp        # Brodos Crypto Trade Protocol

    list comprehension

    • erzeuge eine neue Liste durch das sukzessive Iterieren über bestehende Sequenzen
    • die hinzugefügten Elemente können Ausdrücke sein, die vorgegebenen Bedingungen genügen müssen
    • die Mächtigkeit dieser Konstrukts entspricht der Mächtigkeit der vorgestellten Methoden map und filter
    • TIP es gilt als guter Stil in Python im Zweifelsfall list comprehension anzuwenden
    • Syntax:
    [ expression for item1 in sequence1
                 for item2 in sequence2
                 ...
    
                 for itemN in sequenceN
                 if condition ]

    diese Syntax ist äquivalent zu:

    s=[]
    for item1 in sequence1:
    
      for item2 in sequence2:
        ...
               for itemN in sequenceN:
    
                 if condition: s.append(expression)
    
    • ein paar Variationen:
    a=map(lambda x: x*x, range(1,10 ))
    
    a=[ x*x for x in range(1,10)]
    
    #a=[1, 4, 9, 16, 25, 36, 49, 64, 81]
    
    b=filter(lambda x: x < 5, range(1,10) )
    
    b=[x for x in range(1,10) if x < 5 ]
    
    #b=[1, 2, 3, 4]
    
    c=filter( lambda x: round( math.sqrt( x ))**2 == x , range(1,20000) )
    
    c=[ x for x in range( 1,20000) if round( math.sqrt( x ))**2 == x ]
    
    #c=[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400,
    441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369,
    
    1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401, 2500, 2601,2704,
    
    2809,  2916, 3025, 3136, 3249, 3364, 3481, 3600, 3721, 3844, 3969, 4096, 4225, 4356,4489,
    
    4624, 4761, 4900, 5041, 5184, 5329, 5476, 5625, 5776, 5929, 6084, 6241, 6400, 6561, 6724,
    
    6889, 7056, 7225, 7396, 7569, 7744, 7921, 8100, 8281, 8464, 8649, 8836, 9025, 9216, 9409,
    
    9604, 9801, 10000, 10201, 10404, 10609, 10816, 11025, 11236, 11449, 11664, 11881, 12100,
    
    12321, 12544, 12769, 12996, 13225, 13456, 13689, 13924, 14161, 14400, 14641, 14884, 15129,
    
    15376, 15625, 15876, 16129, 16384, 16641, 16900, 17161, 17424, 17689, 17956, 18225, 18496,
    
    18769, 19044, 19321, 19600, 19881]
    
    d=[(a,b) for a in range(1,5) for b in ("a","b")]
    
    #d=[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b'), (4, 'a'), (4, 'b')]

    TIP Das Decorate-Sort-Undecorate Idiom (auch bekannt als Schwartzian transform) hilft, um eine Liste nach einem vorgegebenen Kriterium zu sortieren.
    Sortiere die numerischen Strings nach ihrem numerischen Wert.

    • so nicht:
    >>> strings= [ str(i) for i in range(7,12) ]
    
    >>> print strings
    ['7', '8', '9', '10', '11']
    
    >>> strings.sort()
    >>> print strings
    ['10', '11', '7', '8', '9']
    • aber so
    >>> decorate= [ ( int(i),i) for i in strings ]
    
    >>> decorate.sort()
    >>> undecorate=[ y for x,y in decorate ]
    
    >>> print undecorate
    ['7', '8', '9', '10', '11']
    • REFACTORSortiere die Variationen von Rainer und Michael
       ["Rainer", "michaeL", "rAiner", "michaEl", "raIner", "MichAel"]
    • HAND seit Python 2.4 ist das sortieren mit Python built-in Funktionalität deutlich einfacher, denn die Funktionen sort wurde erweitert und die freie Funktion sortedeingeführt
      1. während sort stabil auf der Liste sortiert, gibt sorted, eine neue, sortierte Liste zurück
      • beide Funktionen unterstützen die Schlüsselwörter cmp , key und reverse
        1. cmp: Sortierfunktion
        2. key: Sortierschlüssel
        3. reverse : Sortierreihenfolge
      • REFACTOR löse die Aufgabe, Strings nach ihrem numerischen Wert zu sortieren, mit den neuen Sortierfunktionen
    • HANDprogrammiere einen Filter für eine Datei
      1. entferne alle abschließenden Leerzeichen aus der Zeile
      2. entferne alle Leerzeilen aus der Datei
      3. Zeilenendzeichen müssen erhalten bleiben
      4. vergleiche die Länge der Datei mit der Länge des resultierenden Strings
        • TIP die Methoden rstrip, join von String und len einer Sequenz lösen die Einzelschritte
    >>> test = cal.cleanFile("/home/grimm/schulung/Python/Beispiele/grundlagen.py")
    >>> print test
    
    (1320, 1270)
    >>> test = cal.cleanFile("/etc/services")
    
    >>> print test
    (304796, 304758)

    Beispiel

    HAND Erzeuge ein dreidimensionales Objekt und weise jedem Punkt in dem Raum einen Defaultwert zu.
    TIP Löse die Aufgabe iterativ oder Wie sind die Beispiele aus Python Grundlagen entstanden?

    • erzeuge die Indizes
    >>> [ (i,j,k ) for i in range( 1,3) for j in range(1,2) for k in range(1,10)]
    
    [(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 1, 4), (1, 1, 5), (1, 1, 6), (1, 1, 7), (1, 1, 8), (1, 1, 9),
    
    (2, 1, 1), (2, 1, 2), (2, 1, 3), (2, 1, 4), (2, 1, 5), (2, 1, 6), (2, 1, 7), (2, 1, 8), (2, 1, 9)]
    • weise jedem Punkt einen Defaultwert zu
    >>> zip( [ (i,j,k ) for i in range( 1,3) for j in range(1,2) for k in range(1,10)], [0]*2*1*9 )
    
    [((1, 1, 1), 0), ((1, 1, 2), 0), ((1, 1, 3), 0), ((1, 1, 4), 0), ((1, 1, 5), 0), ((1, 1, 6), 0),
    
    ((1, 1, 7), 0), ((1, 1, 8), 0), ((1, 1, 9), 0), ((2, 1, 1), 0), ((2, 1, 2), 0), ((2, 1, 3), 0),
    
    ((2, 1, 4), 0), ((2, 1, 5), 0), ((2, 1, 6), 0), ((2, 1, 7), 0), ((2, 1, 8), 0), ((2, 1, 9), 0)]
    • erlaube den punktweisen Zugriff durch ein Dictionary
    >>> dict( zip( [ (i,j,k ) for i in range( 1,3) for j in range(1,2) for k in range(1,10)], [0]*2*1*9 ) )
    
    {(1, 1, 9): 0, (1, 1, 8): 0, (1, 1, 5): 0, (1, 1, 4): 0, (1, 1, 7): 0, (1, 1, 6): 0, (1, 1, 1): 0,
    
    (1, 1, 3): 0, (1, 1, 2): 0, (2, 1, 8): 0, (2, 1, 9): 0, (2, 1, 6): 0, (2, 1, 7): 0, (2, 1, 4): 0,
    
    (2, 1, 5): 0, (2, 1, 2): 0, (2, 1, 3): 0, (2, 1, 1): 0
    • parametrisiere alles, indem du eine Funktion darum legst
    >>> def make3DObjekt(x,y,z,default):
    
    ...     return dict( zip( [ (i,j,k ) for i in range( 1,x+1) for j in range(1,y+1) for k in range(1,z+1)], [default]*x*y*z ) )
    
    ...
    • probiere es aus
    >>> print make3DObjekt(1,2,3,10)
    
    {(1, 2, 1): 10, (1, 2, 2): 10, (1, 2, 3): 10, (1, 1, 1): 10, (1, 1, 3): 10, (1,1, 2): 10}
    
    >>> print make3DObjekt(1,0,0,5)
    {}
    >>> print make3DObjekt(3,3,3,0)
    
    {(3, 1, 3): 0, (3, 1, 2): 0, (3, 1, 1): 0, (3, 3, 1): 0, (3, 3, 3): 0, (3, 3, 2): 0, (1, 2, 1): 0,
    
    (1, 2, 2): 0, (1, 2, 3): 0, (2, 2, 3): 0, (2, 2, 2): 0, (2, 2, 1): 0, (3, 2, 2): 0, (3, 2, 3): 0,
    
    (3, 2, 1): 0, (1, 3, 3): 0, (1, 3, 2): 0, (1, 3, 1): 0, (1, 1, 1): 0, (1, 1, 3): 0, (1, 1, 2): 0,
    
    (2, 3, 1): 0, (2, 3, 2):0, (2, 3, 3): 0, (2, 1, 2): 0, (2, 1, 3): 0, (2, 1, 1): 0}
    
    >>> cube=make3DObjekt(3,3,3,0)
    >>> print cube[1,1,2]
    
    0
    >>> cube[1,1,2]= 10
    >>> cube[1,1,3]= 20
    
    >>> print cube[1,1,2] + cube[1,1,3]
    
    30

    Funktionale Programmierung

    Sprachen de>enYahooKEerror
    len
  •  

    Grundlagen


    Python ist eine objektorientierte Skriptsprache.
    Sie wurde von  Guido van Rossum Anfang der 90er entwickelt, der sie nach der englischen Comedyreihe Monty Python's Flying Circus benannt hat.

    Grundkonzepte

    • objektorientiert
      • alles ist ein Objekt
    • portabel
      • auf jeder Plattform verfügbar, insbesondere unter den Unix Systemen, Linux, Windows und Mac
    • mächtig
      • dynamische Typisierung
      • automatische Speicherverwaltung (garbage collection)
      • Unterstützung komplexer Systeme durch Packages, Module, Klassen und Exceptions
      • mächtige Operationen auf den built-in Datentypen wie Listen, Dictionaries und Strings
      • reichhaltige Bibliothek
    • kommunikativ
      • gute Python-C Bindung (ctypes Modul)
      • Python Implementierung in
        • C: CPython
        • java: Jython
        • C#: IronPython
        • python: PyPy
    • einfach zu nutzen
      • unterstützt gut den try and error Ansatz HAND Prototyping
      • kurzer Weg bis zum ersten Programm
    • einfach zu erlernen
      • Syntax an C angelehnt
      • folgt dem Prinzip der kleinsten Überraschung
      • unterstützt Introspektion sehr gut

    Programmausführung

    Mehrere Wege bieten sich an, Python Code auszuführen.

    • Command String
    grimm@ackbar ~ $ python -c "print 3.0/3.1"

    0.967741935484
    • Interaktiver Prompt für den try and error Ansatz
    grimm@sirrah uml $ python
    Python 2.3.3 (#2, Feb 16 2004, 10:50:52)

    [GCC 3.3.2] on linux2
    Type "help", "copyright", "credits" or "license" for more information.

    >>>
    • System Command
    python test.py
    • als Applikation, durch den Aufruf des Pythoninterpreters  #!/usr/bin/env pythonin TestProgramm
    grimm@sirrah TestProgramm 
    

    Syntax

    • ein paar einfache Syntaxmerkmale von Python
    >>> import sys
    >>> if 1 :
    ... a="eins"

    ... print a # ein
    ... print 3+4\
    ... -5

    ... print sys.path
    • Unterstrukturen werden durch
      • : eingeleitet
      • Einrückung ausgezeichnet
    • # erklärt alles rechts davon zum Kommentar
    • ; ist am Ende eines Ausdrucks nicht notwendig
    • Variablen werden impliziert definiert
    • \ leiten Fortsetzungszeilen ein
    • import lädt Dateien (Module) oder Verzeichnisse (Pakete); diese werden über den Modulnamen referenziert

    Erste Schritte

    Interaktiver Prompt

    REFACTOR nach Aufruf von python öffnet sich eine Interaktive Python Shell
    Zahlen

    • ein bißchen Arithmetik
    2+2
    3/2
    3.0/2
    1j*1j
    a=(1j*1j)

    a.real
    a.imag

    Strings

    • werden durch doppelte  (") oder einfache (') Anführungszeichen definiert
    s1="0123456"
    s2='0123456'
    print s1, s2

    • erlauben eine einfache Arithmetik
    s1+s2
    3*s1
    • Zeichen escapen
    test="escape\"me"
    • über mehrere Zeilen
    a="1234\
    567"
    b="""1234

    567"""
    • raw String
    c="1234\

    567"
    cRaw=r"1234\
    567"
    • Indexzugriff
      • das erste Element besitzt den Index 0
      • negative Indizes werden von hinten her ermittelt
        • TIP a[-n] ist das nt letzte Zeichen
    a="0123456789"

    a[0]
    a[len(a)-1]
    a[-1] # das letzte Zeichen

    a[-3] # das drittletzte Zeichen
    • ALERT! Strings sind immutable; d.h.: sie können nicht mehr verändert werden
    • TIP die Indexgrenzen werden überprüft
    a[3]=5 
    a[len(a)]
    • Scheibchenweise (slices) Strings
      •  string[i:j] ergibt einen neuen String einschließlich dem Anfangsindex i und ausschließlich dem Endindex j
    MyString="0123456789"
    MyString[1:2]
    MyString[:2]

    MyString[1:]
    MyString[:]
    MyString[-1000:10000]
    • TIP für fehlende Grenzwerte wird 0 bzw sys.maxint eingesetzt, so wird MyString[:] als MyString[0:sys.maxint] interpretiert
    • das slicen von Objekten kann auf alle sequentiellen Datentypen angewandt werden

    REFACTOR lasse durch einen beliebigen String ein -Zeichen durchlaufen

    • TIP mittels
    for i in range(len(MyString)):

    print i

    erhälst du die Indizes
    Listen

    • Listen sind im Gegensatz zu Strings veränderbar (mutable), ihnen kann daher ein neuer Wert zugewiesen werden
    a = ['spam', 'eggs', 100, 1234]

    a[-2]
    a[0:2] = [1, 12]

    a[0:2] = []
    a[1:1] = ['bletch', 'xyzzy']

    a[1:2] = ['bb', 'cc']
    q = [2, 3]

    p = [1, q, 4]
    p[1][0]

    Beispiele

    • HAND Die Funktionen werden durch
    import sys
    sys.path.append("/home/grimm/schulung/Python/Beispiele")

    import grundlagen

    geladen

    • Die built-in Funktion dir

    dir(grundlagen)

     

    gibt einen Überlick über das Modul.

    Wo liegen die Grenzen von n Fakultät ?

    def fakRange(scope):
    result=1
    for i in range(1,scope+1):

    result *= i
    print result

     

    Alle nichttrivialen Teiler

    Bestimme alle nichttrivialen Teiler von start bis end.

     
    def teiler(start,end):

    print [(n,"teiler: " +str( [ i for i in range(2, int(2,int(n/2)+1     ) if n%i == 0 ])) for n in range( start,end+1)]

    Fakultät.

    Bestimme alle Fakultäten von start bis end.

     def fakultaet(start,end):

    print [ (n,"Fak= " + str(reduce(( lambda x,y: x*y),[ i for i in range(1,n+1) ] ) ) ) for n in range( start,end+1)]

    Quicksort

    def qsort(L):
    if len(L) <= 1: return L

    return qsort([lt for lt in L[1:] if lt < L[0] ] ) + L[0:1] + qsort([ge for ge in L[1:] if ge >= L[0]])

    Zähle das Vorkommen jedes Wortes

    import re
    re_word= re.compile(r"(\w+)")

    def wordCount(s):
    allParts= re_word.split(s)

    wordDict= {}
    for word in allParts[1::2]:

    wordDict[word]= wordDict.setdefault(word,0) + 1

    return wordDict

    Filtere einen String

    def makefilter(keep):

    """ Return a function that takes a string and returns a newstring,
    consisting only of the characters from the old string in 'keep'.

    """
    import string
    # make a string of all chars, and one of all those NOT in 'keep'
    allchars = string.maketrans('', '')

    delchars = ''.join([c for c in allchars if c not in keep])

    # return the function
    return lambda s,a=allchars,d=delchars: s.translate(a, d)

     

  • Kontrollfluss

    Bedingungen

    • die allgemeine Form der bedingten Auswahlanweisung besitzt folgende Form:
    if expression:

    statements
    elif expression:
    statements
    elif expression:
    statements
    ...

    else:
    statements
    • die Angabe der elif beziehungsweise else Anweisung ist optional
    • TIP Python kennt keine case Anweisung, denn sie stellen für objektorientierte Sprachen keinen guten Stildar
      • mittels Dictionaries und anonymen Funktionen sind case Strukturen schnell simuliert:
    select={

    "+": (lambda x,y: x+y),

    "-": (lambda x,y: x-y),
    "*": (lambda x,y: x*y),

    "/": (lambda x,y: x/y) }


    result= select["+"](3,4)
    • REFACTOR übersetze die if Anweisungen in eine Dictionary Abfrage; ALERT! else Anweisung
    def choiceIf(choice):    
    if choice == 1:
    val= "red"

    elif choice == 2:
    val= "green"
    elif choice == 3:

    val= "blue"
    else:
    val="white"
    return val
    • da leere Anweisungen syntaktisch falsch sind, muß hier explizit das Schlüsselwort pass verwendet werden
    if expression:

    pass
    else:
    statements
    • TIP dies gilt auch für Funktionen und Methoden

    Schleifen

    • beide Schleifentypen - while and for - können mit einer optional else Anweisung formuliert werden
    • der else Programmblock wird genau dann ausgeführt, wenn die Schleifeniteration vollständig ausgeführt wurde 

    while

    • die while Schleife besitzt folgende Form
    while expression:
    statements
    else:
    statements
     
    • ALERT! Python erlaubt keine Zuweisungen an Stellen, in denen Ausdrücke erwartet werden
    • folgender gültige C Ausdruck ist daher nicht zulässig
    • nextLine soll die nächste Zeile liefern
    while( line= nextLine() ){ process(line); }
    • Das entsprechende Python-Idiom lautet:
    for line in nextLine(): process(line)

     

    for

    • die for Schleife besitzt folgende Form, wobei i über die Sequenz s iteriert
    for i in s:

    statements
    else:
    statements
    • mittels break und continuekann der Schleifendurchlauf manipuliert werden
      • continue beendet die aktuelle Schleife und fährt gegebenfalls mit dem nächsten Schleifendurchlauf weiter
      • break beendet die while bzw. for Schleife
    • Python kennt keine Schleife, bei der am Ende des Schleifendurchlaufs die Bedingung geprüft wird
    • REFACTORIteriere über folgende Sequenzen und gibt sie mittels print aus
      • range(-10,10)
      • ["red","green","blue"]
      • "TestString"
      • [(1,"eins"),(2,"zwei"),(3,"drei"),(4,"vier")]
        • gib die Tupels
        • gib den ersten Wert des Tupels aus
      • ALERT!{1:"eins",2:"zwei",3:"drei",4:"vier"}
        • gib die Items
        • gib jeweils nur die keys und values aus
      • HAND open("/etc/passwd") und
        urllib.urlopen("http://www.python.org")

    Ausnahmen - Exceptions

    Integraler Bestandteil

    • bei syntaktische Fehlern wirft der Pythoninterpreter eine Ausnahme
    • ein paar verschiedene Ausnahmen
      Sind die Ausnahmemeldungen intuitiv?
    >>> 1/0
    Traceback(most recent call last):
    File "<stdin>", line 1, in ?

    ZeroDivisionError: integer division or modulo by zero
    >>> d={]
    File "<stdin>", line 1

    d={]
    ^
    SyntaxError: invalid syntax
    >>> d={}
    >>> d["gibtsNicht"]

    Traceback(most recent call last):
    File "<stdin>", line 1, in ?

    KeyError: 'gibtsNicht'
    >>> arr=[]
    >>> arr[1]
    Traceback(most recent call last):

    File "<stdin>", line 1, in ?
    IndexError: list index out of range

    >>> import Bibilothek
    Traceback(most recent call last):
    File "<stdin>", line 1, in ?

    ImportError: No module named Bibilothek
    >>> 3+"string"
    Traceback(most recent call last):

    File "<stdin>", line 1, in ?
    TypeError: unsupported operand type(s) for +: 'int' and 'str'
    • Python besitzt eine differenzierte built-in Ausnahmenhierachie:

      Ausnahmenhierachie
    • fängt man die Ausnahme durch einen Ausnahmebehandlung nicht ab, so passiert folgendes:
      1. die Ausnahme wird bis zum Mainprogramm weiterpropagiert
      2. im Mainprogramm wird der default exception handler aufgerufen
      3. der default exception handler schreibt eine Fehlermeldung, die den Typ der Ausnahme und den stack trace (die Liste der aktiven Zeilen und Funktionen, als die Ausnahme geworfen wurde) enthält
      4. der default exception handler beendet (terminates) das Programm
    • MOVED TO... Exceptions brechen aus dem normalen Kontrollfluß aus

    Aspekte der Ausnahmebehandlung

    fangen mit try/except/else

    Beispiele
    • eine Ausnahme zu fangen in seiner einfachsten Form:
    >>> try:
    ... 7/0
    ... except:

    ... print "Kein Fehler!"
    ...
    Kein Fehler!
    • es geht auch spezieller:
    >>> try:
    ... 7/0
    ... except ZeroDivisionError:

    ... print "Kein Fehler"
    ...
    Kein Fehler
    • mit kleinen Überraschungen
    >>> try:
    ... 7/0
    ... except StandardError:

    ... print "Zu allgemein"
    ... except ZeroDivisionError:
    ... print "Toter Code"

    ...
    Zu allgemein
    • die except Handler werden sukzessive abgearbeitet
    • TIP der erste exception handler, der zutrifft wird benützt
      • MOVED TO... first match for best match
      • MOVED TO... die exception handler sollten vom speziellen zum allgemeinen definiert werden
    • optional kann noch ein abschließendes else angegeben werden, das genau dann prozessiert wird, wenn keine Ausnahme auftrat
    >>> try:
    ... 7/1
    ... except:

    ... pass
    ... else:
    ... print "keine Ausnahme"
    ...

    7
    keine Ausnahme
    • falls die Ausnahme nicht gefangen werden konnte, führt dies in der Regel zum Programmabruch
    >>> try:

    ... 7/0
    ... except IndexError:
    ... pass

    ...
    Traceback(most recent call last):
    File "<stdin>", line 2, in ?

    ZeroDivisionError: integer division or modulo by zero
    • HAND mittels reguläre expressions kann geprüft werden, ob ein String einer gültigen Zahl entspricht
    def is_a_number(x):
    import re
    num_re = re.compile(r'^[-+]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][-+]?[0-9]+)?$')

    matchObject = num_re.match(str(x))

    if matchObject:
    return True
    else:
    return False
    • dies geht deutlich einfacher mit Exceptions
      MOVED TO... schreibe eine Funktion mit der gleichen Funktionalität
    Syntax
    • die Ausnahmebehandlung kann leicht komplex werden
    try:
    <Anweisungen> # wird zuerst ausgeführt
    except <name1>:
    <Anweisungen> # wird ausgeführt, falls eine Ausnahme vom Type name1 geworfen wurde

    except <name2>,<Daten>:
    <Anweisungen> # wird ausgeführt, falls Ausnahme name2 geworfen wurde, erhalte dabei zusätzlich Daten
    except (name3,name4):

    <Anweisungen> # wird ausgeführt, falls eine Ausnahme vom Typ name3 oder name3 geworfen wurde
    except:
    <Anweisungen> # fange die restlichen Ausnahmen auf, und führe die Anweisungen aus
    else:
    <Anweisungen> # wird ausgeführt, falls der try Block keine Ausnahme warf
     
    • HANDAnnahme:
      • in try werden jeweils Ausnahmen vom Typ name1, name2, eine nicht aufgezählt Ausnahme und keine Ausnahme geworfen
      • dann kommt es zu folgendem Kontrollfluß nach dem try Block:
        • name1:
          • try MOVED TO... except <name1>
        • name2:
          • try MOVED TO... except <name1> MOVED TO... except <name2>, <Daten>
        • nicht aufgezähler Typ:
          • try MOVED TO... except <name1> MOVED TO... except <name2>, <Daten> MOVED TO... except (name3,name4) MOVED TO... except
        • keine Exception:
          • try MOVED TO... else
    • Exceptions verhalten sich Sprunganweisungen (goto) deutlich ähnlicher als Funktionsaufrufen,
    >>> try:
    ... 7/0
    ... print "Ignoriere die Zeile"

    ... except:
    ... print "Ignoriere den Fehler"
    ...
    Ignoriere den Fehler

    denn ein Funktionsaufruf würde nach gefangener Exception

    print "Ignoriere die Zeile"


    prozessieren

    aufräumen mit try/finally

    • häufig kommt es vor, daß im try Block eine Ressource (Datei,Socket,Lock,...) gebunden wird, die natürlich wieder freigegeben werden sollte
      MOVED TO... daß hätte aber zu Folge, daß in jedem exception handler und im abschließenden else Block die Ressource wieder freigeben werden müsste ( vgl.Syntaxbeispiel )- ein mühsames und fehlerträchtiges Unterfangen
    • aus diesem Grund bietet Python ein weiteres Konzept an, um mit Ausnahmebehandlungen umzugehen:
    try:
    lockARessource()
    finally:
    releaseTheRessource()
    • unabhängig davon, ob eine Exception auftrat, wird der finally Block abgearbeitet
    • durch die try Anweisung wird der Kontrollfluß immer zum finally Block gelenkt
    • ALERT! die beiden Konzepte try/except/else und try/finally können einschließlich Python 2.4 nur exclusive verwendet werden
    • falls eine Ausnahme auftrat, wird diese nach dem Prozessieren des finally Block wieder geworfen
    >>> def lockARessource(): raise IndexError

    ...
    >>> try:
    ... try:
    ... lockARessource()
    ... finally:

    ... print "Räume auf"
    ... except IndexError:
    ... print "Error"

    ...
    Räume auf
    Error

    werfen mit raise/assert

    • bisher war wir nur in der passiven Situation: eine Exception trat auf und wir reagierten mit except/else oder finally darauf
    • mittels raise ist es möglich, eine Exception explizit zu werfen
    • folgende Variationen stehen zur Verfügung
    raise <name>         # wirf explizt eine Exception
    raise <name>,<data> # übergibt zusätzlich Daten

    raise # wirf die aktuelle Exception weiter

    Beispiele

    • wirf eine Exception
    >>> def raiseError(): raise IndexError

    >>> try:
    ... raiseError()
    ... except IndexError:
    ... print "Mein Index Error"

    ...
    Mein Index Error
    • Datenübergabe und Exceptions weiterwerfen ( rethrow )
    def raiseError() : raise IndexError,"Ich werde wieder geworfen"
    >>> try:

    ... raiseError()
    ... except IndexError,data:
    ... print data

    ... raise
    ...
    Ich werde wieder geworfen
    Traceback(most recent call last):
    File "<stdin>", line 2, in ?

    File "<stdin>", line 1, in raiseError

    IndexError: Ich werde wieder geworfen
    assert
    • eine Sonderform unter den Exceptions nimmt die assertAnweisung in doppelter Hinsicht ein
      1. ihr werfen ist an eine Bedingung geküpft
      2. sie wird explizit durch das Aufrufen des Python Interpreters mit Optimierung -O" unterbunden
    • die Syntax des Aufrufes ist
    assert <test>,<data>

    wobei dies äquivalent zu folgendem Ausdruck ist

    if __debug__:
    if not <test>:

    raise AssertionError, <data>
    • TIP durch das explizite Ausschalten der Exceptions eignen sich die Assertion für die Entwicklung des Codes, während die restlichen Exceptions die Fehler im Produktivcode fokusieren

  •  

      Linux-Magazin
    TitelInhaltDatum
    Deko mit Nutzen Dekoratoren in Python 06/2009
    Im Zeichen der Drei Umstieg auf Python 3 09/2009
    Erfrischend neu C++0x, Teil 1: Die Erweiterung der Kernfunktionalität 04/2010
    Reichhaltiges Angebot C++0x, Teil 2: Die Erweiterung der Standardbibliothek 05/2010
    Magischer Mechanismus Template Metaprogramming mit C++ 01/2011
    Kurz und bündig Prägnante Programmierung in Haskell 06/2011
    Passendes Werkzeug C++ Compiler im Vergleich 12/2017
    Unterschiedlich Quicksort Implementierunge im Vergleich Quicksort 03/2021

     

      Linux-Magazin Online
    TitelInhaltDatum
    Funktionale Programmierung (1) Grundzüge der funktionalen Programmierung 09/2009
    Funktionale Programmierung (2) Funktionale Programmierung mit Python 10/2009
    Funktionale Programmierung (3) Das MapReduce Framework 11/2009

     

      Linux-Magazin Pro
    TitelInhaltDatum
    What’s new in Python 3 What do Python 2.0 programmers need to know about Python 3? 107/2009
    GCC, Clang, and MSVC compilers with C++ Perfect Match 207/2018
  • Rezensionen

      Linux-Magazin
    TitelAutorDatum
    Das Python Praxisbuch Farid Hajji 10/2009
    The C++ Standard Library Extension Pete Becker 02/2010
    Patterns For Parallel Software Design Jorge Luis Ortega-Arjona 07/2010
    Clojure Stefan Kamphausen, Tim Oliver Kaiser 01/2011
    The D Programming Language Andrei Alexandrescu 03/2011
    Haskell Intensivkurs Marco Block, Adrian Neumann 06/2011
    Learn You a Haskell for Great Good! Miran Lipovaca 06/2011
    Effektiv C++ programmieren Scott Meyers 10/2011
    Haskell: The Craft of Functional Programming Simon Thompson 02/2012
    Hadoop: Zuverlässige, verteilte und skalierbare Big-Data-Anwendungen Ramon Wartala 04/2012
    Multicore-Software Urs Gleim, Tobias Schüle 06/2012
    The C++ Standard Library Nicolai Josuttis 08/2012
    Template Metaprogramming Davide di Gennaro 11/2012
    Erlang/OTP Pavlo Baron 04/2013
    Scala in Depth Joshua Suereth 05/2013
    MapReduce Design Pattern Donald Miner, Adam Shook 06/2013
    Introducing Erlang Simon St. Laurent 06/2013
    The C++ Programming Language Bjarne Stroustrup 11/2013
    DSL Engineering Markus Völter 03/2014
    Requirements Engineering Ralf Baumann 08/2014
    Parallel und Concurrent Programming in Haskell Simon Marlow 09/2014
    From Mathematics to Generic Programming Alexander A. Stepanov 06/2015
    Discovering Modern C++ Peter Gottschling 05/2016
    Clean C++ Stephan Roth 03/2018
  •  

    Module und Packages


    Eine Python Sourcedatei wird als Modul bezeichnet. Werden die Dateien darüber hinaus noch in Verzeichnissen organisiert, so wird diese Verzeichnisstruktur Package genannt.

    Module

    Syntax

    • folgender Code source.py soll als Beispiel dienen
    var="Variable"
    
    def function() : print "I'm a function"
    
    class SimpleClass:
    
        def name(self): return "I'm a methode"
    • es gibt im wesentlichen zwei Möglichkeiten Python Source einzubinden und eine Namensvariation

    Einbinden des ganzen Moduls

    durch import module

    >>> import source
    >>> print source.var
    
    Variable
    >>> source.function()
    I'm a function
    >>> myInst= source.SimpleClass()
    
    >>> myInst.name()
    I'm a methode

    Explizites Einbinden einzelner Definitionen des Moduls

    durch from module import definition

    >>> from source import var
    >>> from source import function
    
    >>> from source import SimpleClass
    >>> print var
    Variable
    >>> print function()
    
    I'm a function
    >>> function()
    I'm a function
    >>> myInst= SimpleClass()
    >>> myInst.name()
    
    I'm a methode

     

    • TIP durch from source import * werden alle Definitionen in den globalen Namensraum eingebunden

    Einbinden unter einem anderen Namen

    durch import module as newSource oder from module import definition as newName

    • Beispiel
      • import source as newSoure erlaubt es, die Funktionalität von source durch newSource aufzurufen
      • mittels from source import var as newVar ist es möglich, die Funktion var durch newVar aufzurufen
    • ALERT! durch das Einbinden des ganzen Moduls oder der Definition unter einem anderen Namen ist es auf einfache Weise möglich, Namenskollisionen zu vermeiden

    Empfehlung

    • Module sollten nicht in den globalen Namensraum geladen werden, denn Ausdrücke der From
    from source import function

    verdeckt vorhandene Attribute

    • das Laden bestehender Funktionalität unter neuem Namen sollte mit Vorsicht verwendet werden, denn einerseits macht es den Code schwerer lesbar und andererseits können gefährliche Seiteneffekte entstehen
    • folgender Code versteckt den built-in Befehl dir
    >>> from source import function as dir
    >>> dir()
    I'm a function
    >>> dir([])
    
    Traceback(most recent call last):
      File "<stdin>", line 1, in ?
    
    TypeError: function() takes no arguments(1 given)
    • TIP mittels del dir wird die dir Definition im aktuellen Namensraum gelöscht MOVED TO... das built-in dir ist nun wieder sichtbar
    • REFACTORlade das Modul auf verschiedene Arten und nütze es; speicher dazu das Modul ab und rufe den Pythoninterpreter aus dem Modulverzeichnis auf
      • einbinden des ganzen Modules
      • explizite Einbinden einzelner Definition des Moduls
      • einbinden unter einem anderen Namen

     

    Besonderheiten

    • während die import Anweisung an jeder Stelle des Codes erscheinen darf, muß die from module import Anweisung am Anfang des Moduls stehen
    • durch Definition der Liste __all__ in dem Modul ist es möglich, festzulegen, welche Definition durch from module import * verfügbar sind
    __all__= ["function","var"]

    Namen

    • jedes Modul definiert eine Variable __name__, die den Name des Moduls enhält
    • wird das Modul unter einem Alias eingebunden, enthält __name__ den ursprünglichen Namen
    >>> import os as dir
    >>> dir.__name__
    'os'
    • der Name des Top Level Moduls ist __main__
    • daher ist es einfach zu prüfen, ob ein Modul als Hauptprogramm benützt wird
    if __name__ == "__main__":
      statements
    else:
    
      statements
    • der if Zweig wird ausgeführt, falls das Modul als Hauptprogramm genutzt wird, andererseits der else Zweig
    • die built-in Funktion reload()erlaubt ein Modul zur Laufzeit erneut zu laden
      • falls das Modul unter einem Alias geladen wurde, muß der Befehl reload diesen Alias verwenden
    import source
    ...
    reload(source)

    Der Suchpfad für Module kann mittels

    • der Modulvariable sys.path
    >>> import sys
    >>> print sys.path
    
    ['', '/usr/local/lib/python23.zip', '/usr/local/lib/python2.3',
     '/usr/local/lib/python2.3/plat-linux2', '/usr/local/lib/python2.3/lib-tk',
    
     '/usr/local/lib/python2.3/lib-dynload', '/usr/local/lib/python2.3/site-packages']
    >>> sys.path.append( "/home/grimm")
    
    >>> print sys.path
    ['', '/usr/local/lib/python23.zip', '/usr/local/lib/python2.3',
     '/usr/local/lib/python2.3/plat-linux2', '/usr/local/lib/python2.3/lib-tk',
    
     '/usr/local/lib/python2.3/lib-dynload', '/usr/local/lib/python2.3/site-packages',
     '/home/grimm']

    angesehen und modifiziert werden

    • der Environmentvariablen PYTHONPATH editiert werden

    Module lassen sich in vier Gruppen unterteilen.

    • Python Programme
    • C oder C++ Erweiterung in shared libraries
    • Packages von Modulen
    • built-in Module in C, die in den Python Interpreter gelinkt sind

    sys.modulesgibt alle Module aus, die der Python Interpreter geladen hat
    REFACTORLade das Modul aus einem anderen Verzeichnis.

    • passe den Modulsuchpfad mittels sys.pathoder der Umgebungsvariablen PYTHONPATH an
      • den Wert der Umgebungsvariablen PYTHONPATH erhälts du mittels os.getenv("PYTHONPATH")

    REFACTORLade das Modul nochmals, nachdem du das Modul um die Einschränkung

    __all__= ["function","var"]

    erweitert hast

    Packages

    • Packages sind Verallgemeinerungen von Modulen, den ein Package zeichnet sich durch die folgenden Punkte aus
      1. ein Packages wird durch ein gleichnamiges Verzeichnis repräsentiert
      2. das Packages besteht aus ein oder mehrern Modulen
      3. es muß eine Datei __init__.py je Package/Verzeichnis geben
    • folgendes Package soll der Veranschaulichung dienen
    Graphik/
        __init__.py
        Primitive/
    
            __init__.py
            lines.py
            fill.py
        2DGraph/
            __init___.py
            plot2d.py

    Einbinden

    1. import Graphik.Primitive.fill
      • das Untermodul Graphik.Primitive.fill wird geladen und kann explizt über Graphik.Primitive.fill.floodfill(image,x,y,color) angesprochen werden
    2. from Graphik.Primitive import fill
      • das Untermodul fill wird geladen und kann mittel fill.floodfill(image,x,y,color) angesprochen werden
    3. from Graphik.Primitive.fill import floodfill
      • lädt das Untermodul fill, macht aber die Funktion floodfill bekannt, so daß direkt mittels floodfill(image,x,y,color) angesprochen werden kann

    Besonderheiten

    • __init__.py
      • immer wenn ein Teil eines Packages importiert wird, wird die Initialisierungsdatei __init__.py des entsprechenden Packages prozessiert
        MOVED TO... im Fall import Graphik.Primitive, wird sowohl __init__.py im Graphik als auch im Primitive Verzeichnis abgearbeitet
      • durch Setzen der Variable __all__ in __init__.py kann man festlegen, welche Module des Packages durch die Anweisung from module import * geladen werden
    • import verhält sich nicht rekursiv
      • durch import Graphik werden nicht automatische alle Untermodule wie Graphik.Primitive.fill geladen, so daß folgender Aufruf scheitert
    import Graphik
    Graphik.Primitive.fill.flood(img,x,y,color)
    • um das Importieren zu erleichtern, bietet es sich daher an, die Untermodule über die Datei __init__.py zu laden
    # Graphik/__init__.py
    import Primitive, 2DGraph
    
    # Graphik/Primitive/__init__.py
    import lines, fill

    Importieren

    • Module im selben Verzeichnis können ohne voll qualifizierten Namen angesprochen werden
    # in Graphik/Primitive
    import lines        
    • Module eines Packages in anderen Verzeichnis müssen ( einschließlich Python 2.4 ) über den voll qualifizierten Namen angesprochen werden
    # in plot2d.py
    import Graphik.Primitive.fill
    
    Graphik.Primitive.fill.floodfill(image,x,y,color)
    • REFACTOR Python besitzt eine light-weight implementation of the Document Object Model
      • diese Schnittstelle um xml Dateien zu bearbeiten ist in dem Modul minidom.py definiert. right lade diese

     

  •  

     

    Objektorientierte Programmierung

    Einführung

    Python is a dynamic object-oriented programming language that can be used for many kinds of software development. ( www.python.org)

    Motivation

    Werkzeug für den Programmierer

    Python nennt sich eine Multiparadigmen Programmiersprache. Das heißt, Python stellt es dem Programmierer frei, die Lösung seines Problems auf verschiedene Arten zu formulieren.
    Der Programmierer kann funktionale, objekt-orientierte, strukturierte, aspektorientierte Techniken zum Codieren verwenden. Als mächtige Erweiterung der objekt-orientierten Programmierung unterstützt Python metaclass programming.

    Trotz dieser verschiedene Stilrichtungen versteht sich Python insbesondere als objekt-orientierte Programmiersprache.

    Strukturmittel für den Designer

    Die objektorientierte Programmierung bietet sehr mächtige Komponenten an um Software zu strukturieren. Examplarisch können die graphischen Bibliotheken Swing, QT oder die Frameworks Eclipse und ACE genannt werden. Der Siegeszug der objektorientieren Programmierung ist auch ein Siegeszug der Design Patterns.

    Begrifflichkeit für den Systemadministrator

    Die Begrifflichkeit und Funkionalität der objektorientierten Sichtweise wird für den Systemadministrator immer wichtiger.
    So setzen das Lightweight Directory Access Protocol LDAP und die Windows Power Shell auf OO-Konzepten auf.
    Auf http://de.wikipedia.org/wiki/Windows_PowerShellwird die Windows Power Shell folgendermassen charaktisiert.

    Die auf dem .NET-Framework in der Version 2.0 basierende Windows PowerShell verbindet die aus Unix-Shells 
    bekannte Philosophie von Pipes und Filtern mit dem Paradigma der objektorientierten Programmierung.

    Begrifflichkeit

    Objekt versus Klasse

    • ein Objekt ist eine Instanz einer Klasse, eine konkrete Ausprägung einer Klasse
    • so ist der Zahl 5 Instanz der Klasse Integer, die Liste [1,2,3,4] Instanz der Klasse Liste
    • alle Instanzen, alle Objekte vom Typ Liste zeichnen sich durch das gleiche Interface aus:
    >>> dir([])
    ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__',

    '__doc__', '__eq__', '__ge__','__getattribute__', '__getitem__', '__getslice__', '__gt__',

    '__hash__', '__iadd__', '__imul__', '__init__', '__iter__','__le__', '__len__', '__lt__',

    '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__',

    '__setattr__', '__setitem__', '__setslice__', '__str__', 'append', 'count', 'extend',
    'index', 'insert', 'pop', 'remove','reverse', 'sort']
    Klasse
    Eine Klasse beschreibt die Methoden und Variablen einer Menge gleichartiger Objekte.
    Objekt
    Ein Objekt ist eine konkrete Ausprägung einer Klasse zur Laufzeit eines Programmes.

    Konzepte

    • TIPObjektorientiertes Programmieren ist kein definierter Begrifflichkeit, dennoch lassen sich drei Paradigmen herausarbeiten, die für objektorientiertes Programmieren charakteristisch sind:
      • Kapselung, Vererbung und Polymorphie
    • jede Programmiersprache, die sich objektorientiert nennt, wird diese Paradigmen individuell umsetzen
    Kapselung
    Kapselung
    Ist die Bindung einer Datenstruktur mit den Methoden, die auf dieser Datenstruktur wirken.
    • die Datenstruktur (Klasse) stellt Methoden zur Verfügung, über die die Aussenwelt mit ihr kommunizieren kann
    • dies Preisgeben der Interna nach Aussen wird als Interface der Klasse bezeichnet, während das Interne der Klasse die Implementierung darstellt
    • TIP die Klasse kapselt sich von der Aussenwelt ab

    class HumanBeing(object):

    def __init__(self,na):
    self.__name= na
    def getName(self):

    return self.__name
    def changeName(self,newName):

    self.__name= newName

    huber= HumanBeing("Huber")

    maier= HumanBeing("Maier")
    # Huber heiratet Maier
    huber.changeName("Huber-"+maier.getName() )

     

    • mittels der öffentlichen Methoden getName() and changeName() kann mit Objekten vom Typ HumanBeing kommunizieren werden, während die Methode __init__ und die Variable self.__name nicht zugänglich ist
    • betont man bei der Kapselung das Verbergen von Attributen vor der Aussenwelt, so spricht man gerne von Information Hiding
    Vererbung
    Vererbung
    Bezeichnet die Spezialisierung einer Datenstruktur, wobei die neue Datenstruktur alle Eigenschaften der ursprünglichen Datenstruktur (Klasse) erbt.
    • die neue Unterklasse ist eine is-a Spezialisierung der Oberklasse
    • ein Mann ist ein menschliches Wesen, dies sollte sich doch in meiner Humen Being Klassenstruktur widerspiegeln

    class Man(HumanBeing):

    def getSex(self):
    return "male"

    class Woman(HumanBeing):

    def getSex(self):
    return "female"

    schmitt=Woman("Schmidt")

    schmitt.changeName("Schmitt")

     

    • die Klasse Woman kann auf die von HumanBeing geerbten Methoden zugreifen
    • TIP die Eigenschaft, ein Teil der Funktionalität durch Vererbung geschenkt zu bekommen, wird oft als das Vorteil von OO beschrieben: code reuse
    • das Klassendiagramm der Unified Modeling Language (UML) hat sich als die standardisierte Weise etabliert, Klassenstrukuren zu visualisieren

    Polymorphie
    Polymorphie
    Bezeichnet die Eigenschaft einer Methode sich abhängig von der Datenstruktur verschieden verhalten zu können.
    • eigentlich will ich kein gleichgeschlechtliche Ehe erlauben
    def couldMarry( firstHuman, secondHuman):
    return firstHuman.getSex() != secondHuman.getSex()


    if ( couldMarry( huber , maier ) ):
    huber.changeName("Huber-"+maier.getName() )

     

    • MOVED TO... zur Laufzeit werden die Methoden getSex() auf die richtige Klasse abgebildet, ändern ihre Gestalt

    REFACTORSei kreativ: Erzeuge ein paar Männer und Frauen, kommuniziere mit ihnen.

    Details

    Objektmodell

    Klassen

    • eine Klasse ist ein abstrakter Obergriff für die Beschreibung der gemeinsamen Struktur und des gemeinsamen Verhaltens von Objekten
    • die Attribute umfassen Variablen und Methoden
    • durch das Schlüsselwort class wird eine Klasse definiert
    • die Klasse Human Being besitzt folgende Attribute
      • HumanBeing.__init__
      • HumanBeing.getName
      • HumanBeing.changeName
    • Klasse versus Instanz
      • eine Klasse definiert das Aussehen einer Instanz
    • self
      • da in der Regel mehrere Instanzen einer Klasse instanziiert werden, ist es natürlich notwendig, die Instanzen (Objekte) auseinander zu halten
      • daher werden alle Attribute eines Objektes mit dem Argument self gebunden
      • die Aufrufsyntax in der Klassendefinition (Klassenname HumanBeing) ist demzufolge
        • für Variablen: self.name
        • für Methoden: self.changeName("newName") oder HumanBeing.changeName(self,"newName")
    • __init__
      • die Methode __init__() nimmt eine Sonderstellung ein, denn sie wird, insofern sie definiert ist, bei jedem Instanziieren eines Objekts prozessiert
    • Klassen- versus Instanzvariable
      • Variablen, den man in der Klassendefinition nicht über das Wort self qualifiziert, werden an die Klasse gebunden und daher auch Klassenvariablen genannt
      • um sie aus den Methoden heraus ansprechen zu können, muß explizit der Klassenname vorangestellt werden
      • gerne werden Klassenvariablen dazu verwendet, die Anzahl der Instanzen zu zählen
    class Student(object):
    number=0

    def __init__(self,n):
    Student.number += 1

    self.__name= n
    # Student.setID(self,str(n+Student.number))
    self.setID(n+str(Student.number))

    def getNumber(self):
    return Student.number
    def setID(self,id):

    self.__ID= id
    def getID(self):
    return self.__ID


    nun will ich die Klasse nutzen:

    >>> schmitt=Student("Schmitt")

    >>> schmitt.getNumber()
    1
    >>> huber=Student("Huber")
    >>> maier=Student("Maier")

    >>> grimm=Student("Grimm")
    >>> schmitt.getNumber()
    4
    >>> huber.getID()

    'Huber2'
    >>> del grimm
    >>> schmitt.getNumber()
    4



    REFACTOR Leider habe ich vergessen, die Anzahl der Studenten beim Ableben eines Studenten del grimm zu dekrementieren. Implementiere und teste eine Methode __del__mit der geschilderten Funktionalität, die vom Python Interpreter beim Löschen eines Objekts automatisch aufgerufen wird.

    Objekte

    • ein Objekt tritt durch das Instanziieren einer Klasse ins Leben
    • durch den Aufruf
    >>> schmitt= Student("Schmitt")


    wird das Objekt schmidt erzeugt und die __init__Methode der Klasse Student prozessiert

    • wir können nun mit dem Objekt interagieren
    >>> schmitt.getID()

    'Schmitt1'

     

    Konzepte vertieft

    Kapselung vertieft

    • Python besitzt eine freizügige Zugriffskontrolle, denn es existiert nur die Konvention, dass Methoden oder Variablen, die mit __ beginnen, privat sind
    • da Python intern keinen Unterschied zwischen Methoden und Variablen einer Klasse macht, spricht man von Attributen einer Klasse
    • für den Zugriff auf die Interna einer Klasse haben sich die Schlüsselwörter private, protected und publicsprachenübergreifend etabliert:
      • private : auf die Attribute kann nur aus der Klasse selbst heraus zugegriffen
      • protected : auf die Attribute kann darüber hinaus von den abgeleiteten Klassen zugegriffen werden
      • public : auf die Attribute existiert keine Zugriffskontrolle

    REFACTORDefiniere eine Klasse mit zwei Attributen.

    • Ein Attribut soll mit __ beginnen und das andere Attribut mit __ beginnen und enden.
    • Instanziere ein Objekt dieser Klasse und rufe die Attribute darauf auf.

    Nur mit __ beginnende Attribute einer Klasse werden vermangelt . Diese Namensmodifikation unterstreicht die Privatheit der Attribute. Attribute, die mit __beginnen und enden stellen die Implementierung der Klasse dar und sind für die Python Runtime reserviert.

    Vererbung vertieft

    • ist das Erzeugen einer neuen Klasse, die eine bestehende Klasse spezialisiert oder modifiziert
    • bei der Vererbung differieren die Programmiersprachen
      1. Mehrfachvererbung: Python und C++ unterstützen im Gegensatz zu Java Mehrfachvererbung
      2. abstrakte Klassen: Python 2.* kennt keine abstrakten Basisklassen
        • abstrakte Basisklassen oder auch Interface sind Klassen, die nicht instanziert werden, sondern lediglich das Verhalten der von ihr abgeleiteten Klassen festlegen
        • die abgeleiteten Klassen müssen das Interface implementieren um instanziiert werden zu können
    • die Ursprungsklasse wird Basisklasse oder Superklasse, die neue Klasse abgeleitete Klasse oder Unterklasse genannt
    • die abgeleitete Klasse erbt die Attribute der Basisklasse, definiert sie gegebenfalls neu und führt neue Attribute ein
    • Beispiel:
    class DesignStudent(Student):

    def __init__(self,n):
    Student.__init__(self,n)

    def getID(self):
    return "Designer: " + Student.getID(self)

     

    • der Aufruf des DesignStudenten führt zu folgender Ausgabe
    >>> des= DesignStudent("Frank")

    >>> des.getID()
    'Designer: Frank1'
    >>> des.getNumber()
    1

     

    • dem DesignStudent stehen die gleichen Methoden wie dem Student zur Verfügung
    >>> dir( DesignStudent )

    ['__doc__', '__init__', '__module__', 'getID', 'getNumber', 'number', 'setID']

     

    • sowohl im Initialisierer __init__, als auch in der Methode getID nutze ich explizit die Funktionalität der Basisklasse um
      • die Basisklasse richtig mit den Namen des Studenten zu initialisieren und den Zähler zu inkrementieren
      • die ID Darstellung der Basisklasse ausgeben zu lassen und sie zu verschönern

    REFACTOR Erweitere die Klassenstruktur um einen weiteren Studenttyp, damit dieser seine Methode getID überschreiben kann.

    • Aufruf von Basisklassenmethoden:
      • Während bei old style Klassen die aufzurufende Klasse explizit angegeben werden muß, findet bei new_style Klassen dies mapping durch die Angabe des Schlüsselworts super automatisch statt.
    class OldStyle:
    ... def __init__(self):

    ... print "OldStyle"
    ...
    >>> class OldStyle2(OldStyle):
    ... def __init__(self):

    ... OldStyle.__init__(self)
    ... print "OldStyle2"
    ...

    >>> OldStyle2()
    OldStyle
    OldStyle2
    >>> class NewStyle(object):
    ... def __init__(self):

    ... print "NewStyle"
    ...
    >>> class NewStyle2(NewStyle):
    ... def __init__(self):

    ... super(NewStyle2,self).__init__()
    ... print "New2Style"

    ...
    >>> NewStyle2()
    NewStyle
    NewStyle2
      • Python 3.* unterstützt nur noch new style Klassen

    Polymorphie vertieft

    Die Eigenschaft Pythons, zur Laufzeit zu entscheiden, von welchem Datentyp das konkrete Objekt ist, wird als dynamische Typisierung bezeichnet und bildet die Grundlage für die Polymorphie.
    Im Gegensatz zum dynamischen Typisieren von Python ist das statische Typisieren von Java oder C++ deutlich aufwändiger.
    Diese Sprachen, die zur Compilezeit typisieren wollen, müssen ihre Entscheidung bis zur Laufzeit aufschieben, um Polymorphie unterstützen zu können.
    REFACTOR Gegeben sei folgendes Klassendiagramm.


    Implementiere es so, daß jede Instanz einer Klasse ihren Klassennamen ausgibt. Verwende die Klasse anschließend in folgender Schleife.

    import random
    >>> for i in range(5):

    ... random.choice((Ball(),Handball(),Basketball())).getName()

    ...
    Basketball
    Handball
    Ball
    Basketball
    Ball


    Erst durch die Auswahl von random.choice() wird zu Laufzeit bestimmt, von welchem Typ das zu instanziierende Objekt ist, auf dem dann die Methode getName() aufgerufen wird.

     

    Operator Überladung

    Operator Überladung
    Ist die Definition von Operatoren auf Datentypen.
    • entsprechend der __init__ Methode, die bei der Objektinstanzierung prozessiert wird, gibt es für arithmetische Operationen Methoden, die automatisch prozessiert werden
    • Was nötig ist, um sich Integer zu nennen, zeigt der folgende Aufruf:
    >>> dir( 5  )
    ['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '

    __divmod__', '__doc__', '__float__', '__floordiv__', '__getattribute__', '__getnewargs__', '__hash__',

    '__hex__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__',

    '__new__', '__nonzero__','__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__',

    '__rdivmod__', '__reduce__','__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__',

    '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__',

    '__setattr__', '__str__', '__sub__', '__truediv__', '__xor__']

     

    • diese hooks müssen idealerweise in einem eigenen Datentyp definieren, damit dieser sich wie ein natürliche Zahl verhält

    Aperitif

    Mein Datentyp MyNumber unterstützt die Addition und Multiplikation von MyNumber-Typen und built-in Datentypen mit einer kleinen Einschränkung (Bindung der Methode an das Objekt).
    Ich biete die Funktionalität sowohl über Operator Überladung und über explizite Methodenaufufe an, um den Unterschied zu verdeutlichen.

    1. implizit: __add__ und __mul__ werden durch + und * von Python als hook Methoden aufgerufen
    2. explizt: add und mul

    Die weiteren Methoden dienen nur der Ausgabe und der Implementierung der arithmetischen Operationen. Obwohl die Semantik der Operationen identisch ist, unterscheidet sich doch der Aufruf deutlich.

    class MyNumber(object):

    "rigth operand must be instance of type MyNumber or built-in number"
    def __init__(self,num):
    "set the value for MyNumber"
    self.number= num
    def __implAddition( self, Num ):

    "implementation of addition"
    if ( isinstance( Num, MyNumber )) :

    return MyNumber( self.number + Num.number )
    else:

    return MyNumber( self.number + Num )
    def __implMultiplication( self, Num ):

    "implementation of multiplication"
    if ( isinstance( Num, MyNumber )) :

    return MyNumber( self.number * Num.number )
    else:

    return MyNumber( self.number * Num )
    def __add__(self,Num):

    "overloading additon"
    return self.__implAddition(Num)
    def __mul__(self,Num):

    "overloading multplication"
    return self.__implMultiplication( Num )
    def add(self,Num ):

    "support addition with explicite method"
    return self.__implAddition( Num )
    def mul(self,Num ):

    "support multiplication with explicite method"
    return self.__implMultiplication( Num )
    def __repr__(self):

    "support output of number"
    return str(self.number)


    Ein bißchen Arithmetik:

    >>> one=MyNumber.MyNumber(1)
    >>> four=MyNumber.MyNumber(4)

    >>> five=MyNumber.MyNumber(5)
    >>> sixty= MyNumber.MyNumber( 60 )

    >>> one + four
    5
    >>> one.add(four )
    5

    >>> one + 3.4
    4.4
    >>> one.add(3.4)
    4.4

    >>> five * 3.5
    17.5
    >>> five.mul(3.5)
    17.5

    >>> sixty * ( (five * 60) + 31 ) + four

    19864
    >>> sixty.mul( five.mul(60).add(31) ).add(four)

    19864


    Sowohl __add__ als auch add verwenden die Methode __implAddition. Durch das Überladen der hook Methode __add__ erreiche ich, daß beim Aufruf des + Operators der Klasse MyNumber diese Methode verwendet wird.

    Kommt dann noch ein Namensraum hinzu, kann Artithmetik leicht unübersichtlich werden.

    Trennung von Interface und Implementierung

    Die Magie von Operator Überladung besteht darin, daß der Python Interpreter Methodenaufrufe implizit auf hook Methoden abbildet. Diese hook Methoden besitzen die zwei Unterstriche am Anfang und Ende des Methodennamens.

    Diese Trennung der öffentlich verwendeten Methoden von der intern definierten Funktionalität ist die Trennung von Interface und Implementierung. So ist die Addition + das Interface, das durch die Methode __add__ angeboten wird.

    Durch die Implementierung definierter hook Methoden in einer Klasse, unterstützt diese ein definiertes Interface. Gerne spricht man daher davon, daß die Klasse ein Interface umsetzt bzw. ein Protokoll implementiert. Eine Klasse, die Methoden __iter__ und next definiert, implementiert das Iterator Protokoll.

    Die Trennung von Interface und Implementierung ist ein wichtiger ( der wichtigste ) Aspekt des OO-Entwurfs.

    Mächtigkeit

    Operator Überladung ist die Python Art das Verhalten eigener Datentypen zu definieren.
    Gleichzeitig zeigen diese Mechanismen die Funktionsweise des Python Interpreters auf und dienen dem tieferen Verständnis von Python.

    Es folgen die beliebtesten und wichtigstenhook Methoden.

    Indexzugriff
    • __getitem__ und __setitem__
    >>> class Indexer(object):
    ... def __init__(self):

    ... self.__values={}
    ... def __getitem__(self,index):

    ... return self.__values.get(index,2**index)

    ... def __setitem__(self,index,value):
    ... self.__values[index]=value

    ...
    >>> ind=Indexer()
    >>> ind[3]="THREE"
    >>> ind[7]="SEVEN"

    >>> ind[2]
    4
    >>> ind[3]
    'THREE'
    >>> for i in range(10):print ind[i],

    ...
    1 2 4 THREE 16 32 64 SEVEN 256 512



    Durch die zwei Methoden __getitem__ und __setitem__ unterstützt meine Klasse Indexerden lesenden und schreibenden Indexzugriff. Die Instanzvariable self.__values ermöglicht es mir einen Zustand aufzubauen. Objekte, die das Index Protokoll implementieren, sind implizit iterierbar.

    Iterator Protokoll
    • __iter__ und next
    >>> class Fak(object):

    ... def __init__(self):
    ... self.__fak=1

    ... self.__count=1
    ... def __iter__(self): return self

    ... def next(self):
    ... self.__fak *= self.__count

    ... self.__count += 1
    ... return self.__fak

    ...
    >>> fak=Fak()
    >>> for i in range(20): print fak.next(),

    ...
    1 2 6 24 120 720 5040 40320 362880 3628800 39916800 479001600 6227020800
    87178291200 1307674368000 20922789888000 355687428096000 6402373705728000 121645100408832000 2432902008176640000

    >>> fak.next()
    51090942171709440000L
    >>> fak.next()
    1124000727777607680000L
    >>>
     

     

    • __iter__ gibt ein Objekt zurück, das das Iteratorprotokoll unterstützt
    • next: gibt das nächste Item zurück

    Falls die Methoden __iter__ und next nicht implementiert sind, wird in einem Iteratorkontext die hook Methode __getitem__ von Python verwendet.

    TIP Python unterstützt Generator Funktionen. Dies sind Funktionen mit der Anweisung yield . Beim Aufruf einer Generator Funktion erzeugt die Python Laufzeitumgebung implizit einen Iterator mit einer Funktion next().

    Attributzugriff
    • __getattr__ und __setattr__
    >>> class CaseInsensitive(object):

    ... def __getattr__(self,attr):
    ... if ( not attr.upper() in self.__dict__):

    ... raise KeyError(attr)
    ... return self.__dict__[attr.upper()]

    ... def __setattr__(self,attr,val):
    ... self.__dict__[attr.upper()]=val

    ...
    >>> c=CaseInsensitive()
    >>> c.test
    Traceback(most recent call last):

    File "<stdin>", line 1, in <module>
    File "<stdin>", line 4, in __getattr__

    KeyError: 'test'
    >>> c.test=100
    >>> c.test
    100

    >>> c.TeSt
    100
    >>> c.TEST=1000
    >>> c.test

    1000
     


    Die Klasse CaseInsensitive kapselt die Zugriffe auf ihre Attribute.

    • lesender Zugriff
      • in der hook Methode __getattr__ wird in dem Objektdictionary self.__dict__ nach dem Attribut in Grossbuchstaben gesucht
      • existiert das Attribut nicht, wirft die Methode eine Exception mit dem unmodifizierten Attribut
    • schreibender Zugriff
      • in der __setattr__ Methode wird das Attribut in Grossbuchstaben in dem Objektdictionary self.__dict__ gesetzt
    Ausgabe von Daten
    • __repr__ und __str__
    >>> class Output(object):

    ... def __init__(self,num): self.__num= num

    ... def __repr__(self): return "__repr__ :" + repr(self.__num)

    ... def __str__(self): return "__str__: " + str(self.__num)

    ...
    >>> a= Output( 1.0/3.1 )
    >>> a

    __repr__ :0.32258064516129031
    >>> repr(a)
    '__repr__ :0.32258064516129031'
    >>> print a

    __str__: 0.322580645161


    Beide hook Methoden ermöglichen es, Objekte vom Typ Output auszugegeben.
    Während __repr__ das Datum in möglichst genauer Form ausgibt, liegt der Fokus von __str__ in einer möglichst lesbaren Form.
    Es gibt noch ein paar Besonderheiten:

    1. im Python Interpreter wird jede Ausgabe mittels repr, das implizit __repr__ verwendet, interpretiert
    2. bei der Ausgabe ist __repr__ ein Fallback für __str__ ; dies gilt aber nicht umgekehrt
    3. folgende Beziehung muß gelten: eval(repr(a))== a
    >>> eval( repr( 1.0/3.1) ) == 1.0/3.1

    True
    Funktoren
    • __call__

    Functoren sind Objekte, die wie Funktionen aufgerufen werden können. Sie besitzen einerseits die einfache Aufrufsemantik einer Funktion und andererseits den Zustand eines Objekt.
    Der Begriff Funktor ist ein bißchen C++ lastig. Ein Python Objekt, das den Operator __call__ implementiert, wird gerne als callable object bezeichnet.

    • Beispiel Accumulator
    class Accumulator(object):
    def __init__(self, n):

    self.__acc = n
    def __call__(self, n):

    self.__acc += n
    return self.__acc


    Instanzen vom Typ Accumulator verhalten sich wie Funktionen und besitzen, da sie Objekte sind, einen Zustand.

    >>> start5= Accumulator(5)
    >>> start5(10)

    15
    >>> start5(20)
    35
    >>> startDies= Accumulator("Dies")

    >>> startDies(" ist")
    'Dies ist'
    >>> startDies(" ein")
    'Dies ist ein'
    >>> startDies(" Test")

    'Dies ist ein Test'


    TIP Die built-in Funktion callable evaluiert, ob ein Objekt aufrufbar ist.

    >>> callable( Accumulator("" ))

    True
    >>> class Test(object): pass
    ...
    >>> callable(Test() )

    False
    callable( lambda x: x*x)
    True

    >>>

     

    Vergleich

    Um Klassen mit einer spezifischen Vergleichssemantik zu implementieren, existieren in Python zwei explizite, verschiedene Konzepte, rich comparison und comparison. Während die erste Variante erlaubt, die Vergleichssemantik für jeden einzelnen Operator zu definieren, definiert die zwei Variante einen Vergleichsoperator für alle Vergleichsoperationen.

    • rich comparison
      Operatorhook Methode
      < __lt__
      > __gt__
      <= __le__
      >= __ge__
      == __eq__
      != <> __ne__

     

    • nur die Operatoren können verwendet werden, die explizit definiert werden
    • die Operatoren sollten ein Boolean oder ein zu Boolean konvertierbaren Ausdruck zurückliefern
    • ist ein Operator nicht definiert wird implizit die built-in Funktion id verwendet, die die Speicheradresse des Objektes als Vergleichkriterium verwendet
    • Beispiel IdentityComparison
    >>> class IdentityComparison(object):
    ... def __eq__(self,other):

    ... print "equal"
    ... return True
    ...
    >>> a=IdentityComparison()

    >>> b=IdentityComparison()
    >>> a == b
    equal
    True
    >>> a != b

    True
    >>> a < b
    True
    >>> a > b
    False
    >>> print id(a),id(b)

    3085066604 3085068012

     

    • comparision
      • __cmp__
      • der Operator sollte folgende Semantik für die Typen a,b besitzen
        • a < b : -1
        • a == b: 0
        • a > b : 1
    • Zusammenspiel:
      • Das folgende Beispiel verdeutlicht das Zusammenspiel der rich mit der einfachen comparison.
      • Beispiel Comparison:
    >>> class Comparison(object):    

    ... def __eq__(self,other):
    ... print "equal"

    ... return True
    ... def __cmp__(self,other):

    ... print "cmp"
    ... return 0
    ...
    >>> a=Comparison()

    >>> b=Comparison()
    >>> a == b
    equal
    True
    >>> a != b
    cmp

    False
    >>> a < b
    cmp
    False
    >>> a > b
    cmp
    False

     

    • Die __eq__ Methode wird nur dann verwendet, wenn sie explizit definiert wird. Selbst __ne__ wird nicht auf __eq__ abgebildet.

     

    Lebenszeit
    • __init__ und __del__

    Die zwei hook Methoden erlauben es, Aktionen beim Initialisieren und beim Löschen des Objekts zu hinterlegen.

    >>> class Live(object):

    ... def __init__(self): print "hello"
    ... def __del__(self): print "good bye"

    ...
    >>> for i in range( 10 ): a=Live()

    ...
    hello
    good bye
    hello
    good bye
    hello
    good bye
    hello
    good bye
    hello
    good bye
    hello
    good bye
    hello
    good bye
    hello
    good bye
    hello
    good bye
    hello
    good bye


    Wieso wird __del__ nur einmal aufgerufen?

    >>> a=Live()
    hello
    >>> b=a
    >>> del a
    >>> del b
    good bye

     

    Besonderheiten

    Aufruf bei binären Operatoren

    Die Magie des Python Interpreters besteht darin, ein Aufruf der Form a operation b auf a.operation(b)abzubilden.

    • Num :
    >>> class Num(object):
    ... def __init__(self,n):

    ... self.__num= n
    ... def __add__(self,other): return self.__num + other

    ...
    >>> five= Num(5)
    >>> five + 3

    8
    >>> five.__add__(3)
    8

     

    • right das bedeutet insbesondere, das bei binären Operatoren der linke Operand entscheidet, welcher Operator verwendet wird
    >>> 3 + five
    Traceback(most recent call last):

    File "<stdin>", line 1, in <module>
     

     

    • Python bietet für diese Fälle, in den der rechte Operarand die Wahl des Operatoren vorgeben soll, spezielle hook Methoden an, die mit dem Buchstaben r beginnen
    • SymNum :

     

    >>> class SymNum(object):

    ... def __init__(self,n): self.__num=n

    ... def __add__(self,other): return self.__num + other

    ... def __radd__(self,other): return other + self.__num

    ...
    >>> four=SymNumb(5)
    >>> four=SymNum(5)

    >>> four=SymNum(4)
    >>> four + 3
    7
    >>> 3 + four

    7

     

    • in dem Fall, daß beide Operanden die entsprechende Operation überladen haben, bindet der linke Operand stärker
    • AddLeft und AddRight
    >>> class AddRight(object):
    ... def __radd__(self,other): print AddRight.__name__

    ...
    >>> class AddLeft(object):
    ... def __add__(self,other): print AddLeft.__name__

    ...
    >>> AddLeft() + AddRight()
    AddLeft

     

    • Die Vergleichoperatoren können nicht rechtsgebunden aufgerufen werden. Der Pythoninterpreter bildet die Paare (>,<) und (>=,<=) symmetrisch aufeinander ab.
    • Beispiel SymComp
    >>> class SymComp(object):

    ... def __gt__(self,other):
    ... print "greater then"

    ... return True
    ... def __lt__(self,other):

    ... print "lower then"
    ... return True
    ...
    ...
    >>> a=SymComp()

    >>> a < 3
    lower then
    True
    >>> 3 > a
    lower then
    True
    >>> a > 3

    greater then
    True
    >>> 3 < a
    greater then
    True

     

    Semantik der Operatoren

    Es gilt der einfache Grundsatz, daß die Semantik der Operatoren denen der built-inOperatoren entsprechen soll.

    • MyString :
    >>> class MyString(object):

    ... def __init__(self,s): self.__s= s

    ... def __add__(self,other): return float(self.__s)+float(other.__s)

    ...
    >>> one= MyString("1")
    >>> two=MyString("2")

    >>> one+two
    3.0
    >>> ONE= str("1")
    >>> TWO=str("2")

    >>> ONE+TWO
    '12'



    Die Klasse MyString widerspricht der Absicht der Operator Überladung.
    Sinn der Operator Überladung ist es, eigene Datentypen zu definieren, die sich wie built_in Datentypen verhalten.

    Operator Überladung ist ein mächtiges, aber auch mit Vorsicht anzuwendendes Programmierwerkzeug, das kontrovers diskutiert wird. rightKritik an Operator Überladung

    Übungsaufgabe: Bruch

    REFACTOR Entwerfe eine Klasse Bruch, die das Rechnen mit Brüchen umsetzt.

     

    >>> Fraction(1,3) + Fraction(3,4)

    13/12
    >>> Fraction(1,3) * Fraction(3,4)

    3/12
    >>> Fraction(1,3) / Fraction(3,4)

    4/9
    >>> Fraction(1,3) - Fraction(3,4)

    -5/12
    >>> Fraction(1,2) ** 4

    1/16
    >>> print Fraction(1,2) ** 4

    1/16
    >>> sixteenthPart= Fraction(2,4) ** 4

    >>> sixteenthPart
    16/256
    >>> sixteenthPart()
    >>> sixteenthPart
    1/16

    >>> toFraction(10.125)
    10125/1000
    >>> print toFraction(10.125)

    81/8
    >>> Fraction(1,8) < Fraction(1,9)

    True
    >>> 3 < Fraction(7,5)
    False
    >>> print Fraction(1,8) + 3

    25/8
    >>> print 3 + Fraction(1,8)

    25/8
    >>> 1/Fraction(3,7)
    70/30

    >>> -1/Fraction(3,7)
    -70/30
    >>> -Fraction(3,7)

    -3/7
    >>> one=3*Fraction(1,3)

    >>> one
    30/30
    >>> one()
    >>> one
    1/1

    >>> one= 3*Fraction(1,3)

    >>> print 3*Fraction(1,3)

    1
    >>> print ( 1+(3-Fraction(1,3))**2 )

    73/9

     

    1. Unter Bruchrechnung findet ihr die bekannten Regeln zu Bruchrechnen.
    2. Ein paar Hilfsfunktionen um die Klasse Bruch zu vervollständigen

    Die Funktione ggt ermittelt mit Hilfe der Primfaktorzerlegung den größten gemeinsamen Teiler zweier natürlicher Zahlen.

     

    def ggt(first, second):

    "get the ggt of first and second"
    while second:
    first, second = second, first%second
    return first


    Die Funktion toFraction wandelt eine float Zahl in einen Bruch um

     

    def toFraction(fl):

    "convert the float to an fraction"
    #floating point decimal
    fl=float(fl)
    #get both parts of the divison

    whole,frac= divmod( fl,1 )
    # number of digits after the decimal point, rounded with str

    num= len( str(frac).split(".")[1] )

    #increase nominator and denominator
    factor= pow(10,num)
    return Fraction(int(whole*factor + frac*factor),int(factor))

    Methodenbindung

    Python Methoden können nicht nur an Objekte, sondern auch an Klassen gebunden werden. Darüber hinaus unterstützt Python statische Methoden, die an kein Objekt - sei es eine Klasse oder die Instanz einer Klasse - gebunden sind. Während statische Methoden in OO-Programmiersprachen wie Java und C++ auch angeboten werden, stellen Klassenmethoden ein neues Konzept dar.
    Sowohl Klassenmethoden als auch statische Methoden setzen die sogenannten New-Style_Classes von Python voraus.

    Objekt

    Die Bindungen einer Methode geschieht über den Namen self. Die Methode kann nun als gebunden und ungebundene Methode aufgerufen werden.

    >>> class Methode(object):
    ... def bound(self): print "bound"

    ... def unbound(self): print "unbound"
    ...
    >>> a=Methode()

    >>> a.bound()
    bound
    >>> Methode.unbound(a)
    unbound

    Klasse

    Analog zu der Objektreferenz bei Objektmethoden müssen Klassenmethoden mit einer Klassenreferenz aufgerufen werden. Für diese Klassenreferenz hat sich die Namenskonvention cls entsprechend der Namenskonvention self für Instanzreferenzen etabliert.

    >>> class ClassBase(object):
    ... def helloClass(cls): print "Hello from %s" % cls.__name__

    ... helloClass= classmethod( helloClass )
    ...
    >>> class ClassDerived(ClassBase): pass

    ...
    >>> hello= ClassBase()
    >>> hello.helloClass()
    Hello from ClassBase

    >>> der=ClassDerived()
    >>> der.helloClass()
    Hello from ClassDerived

    >>> ClassBase.helloClass()
    Hello from ClassBase
    >>> ClassDerived.helloClass()

    Hello from ClassDerived


    Eine Klassenmethode muß explizit deklariert werden. Beim Aufruf von helloClass findet die Klasse dynamisch heraus, von welchem Typ sie ist. Klassenmethoden können über die Instanz oder die Klasse aufgrufen werden.

    Namensraum (statisch)

    Im Gegensatz zu den Objekt- und Klassenmethoden sind statische Methoden an kein Objekt gebunden. Sie können ohne Objekt- oder Klasseninstanz aufgerufen werden. Ihr Unterschied zu freien Funktionen besteht darin, daß sie an den Namensraum der Klasse gebunden sind.
    Statische Methoden bieten sich zum Schreiben von freien Funktionen an, da sie den globalen Namensraum nicht verschmutzen.

    >>> class StaticBase(object):
    ... def helloStatic(): print "hello from StaticBase"

    ... helloStatic= staticmethod( helloStatic )
    ...
    >>> class StaticDerived( StaticBase ): pass

    ...
    >>> StaticBase.helloStatic()
    hello from StaticBase
    >>> StaticDerived.helloStatic()

    hello from StaticBase
    >>> base= StaticBase()
    >>> base.helloStatic()

    hello from StaticBase
    >>> der= StaticDerived()
    >>> der.helloStatic()

    hello from StaticBase


    Statische Methoden werden explizit deklariert. Sie können über den Klassenqualifier oder eine Instanz aufgerufen werden. Bei der Definition von StaticBase bzw. StaticDerived ist weder self noch cls notwendig.
    Mit Python 2.4 wurde das mächtige Dekoratorkonzept eingeführt. Durch dies lässt sich eine Klassenmethode oder statische Methode eleganter deklarieren.

      @classmethod
    def helloClassMethod(cls):

    ...
    @staticmethod
    def helloStaticMethod():
    ...

     

    •  der Schöpfungsprozeß für Menschen hat sich verselbstständigt
    >>> import random
    >>> for i in range(10000):

    ... random.choice((Man,Woman))("anonymous")

    ...
    .
    .
    .
    <humanBeing.Man instance at 0xb7e2c5ec>
    <humanBeing.Man instance at 0xb7e2c5cc>

    <humanBeing.Man instance at 0xb7e2c5ec>
    <humanBeing.Woman instance at 0xb7e2c5ec>
    >>>


    REFACTOR Wieviel Menschen, Frauen und Männer wurden geschaffen ? Erweitere die Klassenhierachie Menschen um die Mächtigkeit, über die Anzahl der Männer und Frauen Buch zu führen.Verwende dazu Klassenmethoden oder statische Methoden.

    >>> import random

    >>> for i in range(10000):
    ... random.choice((Man,Woman))("anonymous")

    ...
    .
    .
    .
    <humanBeing.Man instance at 0xb7e2c5cc>
    <humanBeing.Woman instance at 0xb7e2c5ec>

    >>> HumanBeing.mankind
    {'Woman': 49851, 'Man': 50149}

    >>> Man("Rainer")
    <humanBeing.Man instance at 0xb7e2c5ec>
    >>> HumanBeing.mankind

    {'Woman': 49851, 'Man': 50150}
    >>>

     

    Introspektion

    Attribute

    Sowohl die Klasse Student als auch den Student schmitt können wir zu Laufzeit fragen, welche Attribute sie zur Verfügung stellen und wie diese Werte gesetzt sind. Dies erlaubt es uns ohne die Defition der Klasse mit den Datentype Student zu arbeiten.
    Diese Introspektionsfähigkeit unterscheidet Python von Java und insbesondere C++

    • die Attribute der Klasse
    >>> dir( Student )
    ['__doc__', '__init__', '__module__', 'getID', 'getNumber', 'number', 'setID']

    >>> Student.__dict__
    {'__module__': 'Student',
    '__init__': <function __init__ at 0x810dd8c>,

    'getNumber': <function getNumber at 0x819cc0c>,
    'setID': <function setID at 0x8191684>,'number': 4,

    'getID': <function getID at 0x81a30b4>, '__doc__': None}

     

    • die Attribute des Objekts
    >>> schmitt=Student("Schmitt")

    >>> dir( schmitt )
    ['_Student__ID', '_Student__name', '__doc__', '__init__', '__module__',

    'getID', 'getNumber', 'number', 'setID']
    >>> schmitt.__dict__

    {'_Student__name': 'Schmitt', '_Student__ID': 'Schmitt1'}
     


    Worin unterscheiden sich die Ausgaben der built-in Funktion dir und dem Objektdictionary __dict__ .

    Klassen und Objekte werden in Python über Dictionaries implementiert

    Objektmodell

    Die folgende Klassenhierachie und Objektinstanzen setze ich im folgenden voraus.

    class A(object): pass
    class B(A): pass

    class C(object): pass

    a=A()
    b=B()

    c=C()
    Typen

    Typinformationen erhält man mit type bei Klassen und Objekten, aber auch bei built-in Typen.

    >>> type(A) == type(B)
    True

    >>> type(a) == type(b)
    True
    >>> type(A)

    <type 'type'>
    >>> type(a)
    <class '__main__.A'>
    >>> type(5)

    <type 'int'>
    >>> type("")
    <type 'str'>
    Klassen

    Mit issubclass kann die Klassenhierachie abgefragt werden.

    >>> issubclass(B,A)

    True
    >>> issubclass(A,B)
    False
    >>> issubclass(A,A)

    True
    >>> issubclass(B,(C,A))
    True
    Objekte

    Durch isinstance erhält man Information über alle Objekte.

    >>> isinstance(a,A)

    True
    >>> isinstance(b,A)
    True
    >>> isinstance(b,C)

    False
    >>> isinstance(b,(C,B))
    True
    >>> import types

    >>> isinstance(3,types.IntType)
    True
    >>> isinstance(3,types.FloatType)

    False

    Design

    Beschränkt sich unsere Sicht auf objekt-orientiertes Programmieren im wesentlichen darauf, welche Werkzeuge Python zur Verfügung stellt um OO-Konzepte umzusetzen, werden wir uns in den weiterführenden Konzepten dem Design mit OO-Mitteln widmen.

    Die folgenden Konzepte sind keine pythonspezifischen Konzepte.

    Entwurf mit Klassen

    Klassen erlauben auf vielfältige Art eigene Typen zu implementieren. Die Mächtigkeit des Klassenentwurfs besteht darin, neue Typen aus bestehenden Typen aufzubauen.
    Dies kann, vereinfachend gesprochen, durch das Ableiten, das Zusammenstellen, das Delegieren und schließlich das Zusammenführenbestehender Typen erfolgen.

    Ableitung is-a

    Typbildung durch Ableitung wird auch gerne als Spezialisierung bezeichnet, da im Allgemeinen ein bestehender Typ eingeschränkt wird. Der abgeleitete, neue Typ ist daher auch ein Basistyp.
    Um ein dictionary zu entwickeln, das nur einmalig initialisiert werden kann und danach konstant ist, bietet es sich an vom built-in Datentyp dict abzuleiten.

    • ConstDict
    class ConstDict(dict):

    """ConstDict is a dictionary which could only once initilisized.
    Afterward it's constant."""
    def __init__(self, dictionary= None, **kwds):

    if ( not self ): self.__initOnce(dictionary, **kwds)
    def __setitem__(self, key, value): pass

    def update(self , dictionary= None ,**kwds):

    if ( not self ):self.__initOnce(dictionary,**kwds)

    def __initOnce(self, dictionary= None ,**kwds):

    if dictionary is not None:
    dict.update(self,dictionary)

    if ( len( kwds )):
    dict.update(self,kwds)


    Das dictionary verhält sich wie der built-in Datentyp dict.

    Durch den Ausdruck (not None) wird sichergestellt, das MyDict nur einmal initialisiert werden kann. In ihm überprüfe ich, ob das Objekt tatsächlich initialisiert wurde. Dies geschieht durch den Konstruktoraufruf oder durch die update Methode.
    Das explizite setzen bzw. modifizieren eines Wertes wird in der Implementierungsmethode __setitem__ ignoriert. Naheliegender ist es einen TypeError zu werfen statt den zu setzenden Wert nicht anzunehmen.
    Die update Methode muß ich explizit überschreiben, da für sie keine interne Implementierungsfunktion ( __update__ ) existiert.

    >>> a=ConstDict( {"4":"testMal"}, b=4 )

    >>> import types
    >>> isinstance( a, types.DictType )

    True
    >>> a
    {'b': 4, '4': 'testMal'}

    >>> a.update( b=4711)
    >>> a
    {'b': 4, '4': 'testMal'}

    >>> c=ConstDict()
    >>> c.update( {"4":"testMal"}, b=4 )

    >>> c
    {'b': 4, '4': 'testMal'}
    >>> c["b"]=4711

    >>> c
    {'b': 4, '4': 'testMal'}


    In Anwendungsbereichen, in denen es eine hierachische Strukturierung der Typen gibt, bietet sich Ableitung zum Modellieren des Gesamtentwurfes an. Insbesondere in GUI-Bibliotheken drängt sich diese Sicht auf. Übersteigt die Ableitungstiefe 3 oder 4 Stufen, wird er Entwurf zunehmend komplex.

    Hier setzt die Idee an, Untertypen als Zusammenstellung bestehender Typen zu modellieren.

    Kompositum has-a

    Das Kompositum besitzt - has-a - seine Untertypen, während die abgeleitete Klasse insbesondere - is-a - eine Basisklasse ist. Die abgeleitet Klasse besitzt das Interface der Basisklasse, das Kompositum hingegen schafft ein neues Interface.

    Bei vielen Abfragen eines Dictionaries ist von Interesse, nicht nur die Zuordnung Schlüssel right Wert in konstanter Zeit aufzulösen, sondern auch die dazu inverse Abfrage. Leider ist beim Standard Dictionary der Zugriff vom Wert zu Schlüssel linear abhängig von der Länge des Dictionaries.
    MultiSymDict stellt ein Dictionary dar, in dem nicht nur die Zuordnung Schlüssel right Wert, sondern auch die Zuordnung Wert right Schlüssel als Dictionaryzugriff möglich ist. Baut man eine solche Struktur aus zwei dictionaries auf, stellt sich bald das Problem, das die Zuordnung Wert right Schlüssel nicht eindeutig ist. Daher werden ich die Werte, seien es nun die ursprünglichen Werte oder Schlüssel, durch Listen repräsentieren.

    Für jede Sicht auf das MulitSymDict verwende ich ein eigenes Dictionary. Modifikationen auf MulitSymDict werden auf die Untertypen automatisch abgebildet.

    • MultiSymDict
    class MultiSymDict(object):
    """MultiSymDict is a symmetric dictionary with multi values;

    MultiSymDict holds two dictionaries;
    first: the classical key -> value dictionary;
    second: the value -> key dictionary;
    you can init it with a dictionary
    with a sequence of pairs"""

    def __init__(self,pairs= None ):
    self.__first= {}
    self.__second= {}
    if (pairs): self.__addToDict(pairs)

    def add(self, pairs):
    self.__addToDict(pairs)

    def remove(self, pairs):
    self.__removeFromDict(pairs)

    def change(self, origPairs, newPairs):

    self.__removeFromDict(origPairs)
    self.__addToDict(newPairs)

    def first(self): return self.__first
    def second(self): return self.__second
    def __addToDict(self, pairs):

    if (type(pairs) in ( type( [] ),type( (0,0) ) )):

    self.__assertionPairs(pairs)
    self.__addPairs(pairs)

    elif (type(pairs) == type({})):

    self.__addPairs(pairs.items())
    def __removeFromDict(self, pairs ):

    if ( type( pairs ) in ( type([]),type((0,0)))):

    self.__assertionPairs(pairs)
    self.__removePairs(self.__first, pairs)

    self.__removePairs(self.__second ,map( lambda i: ( i[1],i[0] ), pairs ) )

    elif ( type(pair ) == type({})):

    self.__removePairs( self.__first,pairs.items() )

    self.__removePairs( self.__second ,map( lambda i: ( i[1],i[0] ), pairs.items() ) )
    def __addPairs( self, pairs ):

    self.__first= self.__generateMultiMapFromPairs( self.__first, pairs )

    self.__second= self.__generateMultiMapFromPairs( self.__second ,

    map( lambda i: ( i[1],i[0] ), pairs ))

    def __removePairs(self,dic,pairs):
    for pair in pairs:

    seq= dic[pair[0]]
    seq.remove( pair[1] )

    if ( not seq ): dic.pop(pair[0])

    def __generateMultiMapFromPairs( self, dic, pairs ):
    for pair in pairs:

    if dic.has_key(pair[0]): dic[pair[0]].append(pair[1])

    else: dic[pair[0]]= [pair[1]]

    return dic
    def __assertionPairs( self, pairs ):
    dict( pairs )


    MultiSymDict läßt sich entsprechend einem Dictionary initialisieren. Darüber hinaus können weitere Dictionarys hinzugefügt, entfernt und ausgetausch werden. Die Unterdictionaries self.__first und self.__second habe ich mit zwei öffentlichen Methoden gekapselt, um die direkte Manipulation dieser Datentypen deutlich zu erschweren.

    • Initialisierung
    >>> myDict= MultiSymDict([(1,2),("a","b"), ("1",2)])

    >>> myDict.first()
    {'a': ['b'], 1: [2], '1': [2]}

    >>> myDict.second()
    {2: [1, '1'], 'b': ['a']}

    >>> myDict= MultiSymDict({1:2,"a":"b","1":2})

    >>> myDict= MultiSymDict(zip((1,"a","1"),(2,"b",2)))

     

    • Anwendung: Worthäufigkeit in einem String
      • die Funktion wordCount zählt die Häufigkeit der Wörter in einem String
    import re
    re_word= re.compile(r"(\w+)")
    def wordCount(s):

    allParts= re_word.split(s)
    wordDict= {}

    for word in allParts[1::2]:
    wordDict[word]= wordDict.setdefault(word,0) + 1

    return wordDict

     

    • wende ich diese Funktion an, ergibt sich das erwartete Ergebnis
    >>> import urllib                         
    >>> wordCount(urllib.urlopen("http://www.heise.de").read())

    ...
    , 'Phisher': 1, 'Komponenten': 2, 'Zugang': 2, 'var': 3, 'function': 1,

    'size120': 1, 'Der': 7, 'delivery': 4, 'meldet': 1, 'Monate': 1, 'ho': 20,

    'Den': 1, 'he': 3, 'Kaesten': 1, 'seit': 2, 'Radiosender': 1,

    'menschliche': 1, 'CDATA': 1, 'sein': 1, '728': 2, 'braucht': 1, 'SRC': 7,

    'innerhalb': 1, '4251454a44774d4568362f6273513d3d': 1, 'resale': 3,

    'Nachricht': 1, '46': 2, '45': 7, '42': 1, 'detail': 1, 'mittelstandsblog': 1,

    'oas': 17, 'sich': 2, 'Banner': 1, 'strengeren': 1, 'Zeittarif': 1,

    'Identifizierung': 1, 'Demo': 1, 'Site': 2, 'write': 10, 'gewordene': 1}

    >>>
     

     

    • initialisiere ich MultiSymDict mit der Ausgabe des letzten Ausdruckes, dann besitze ich ein wesentlich mächtigeres Interface auf das initiale Dictionary
    >>> import urllib

    >>> heiStat= MultiSymDict( wordCount(urllib.urlopen("http://www.heise.de").read()) )

    >>> heiStat.first()
    ...
    '45': [1], '42': [1], 'detail': [1], 'mittelstandsblog': [1], 'oas': [19], 'sich': [3],

    'u': [1], 'Banner': [1], 'strategische': [1], 'strengeren': [1], 'Zeittarif': [1],

    'Bildschirmschoner': [1], 'Identifizierung': [1], 'Demo': [1], 'Site': [1],

    'write': [10], 'hinzuf': [1]}
    >>> heiStat.second()

    ...
    38: ['table'], 39: ['und'], 40: ['www'], 171: ['href'], 44: ['img', 'option', 'span'],

    48: ['src', 'border'], 50: ['width', 'http', 'h3'], 51: ['tr'], 53: ['1'],

    54: ['meldung'], 138: ['p'], 64: ['heise'], 66: ['newsticker'], 69: ['de'], 75: ['br'],

    82: ['b'], 89: ['0'], 166: ['class'], 317: ['a']}

    >>> print sorted(heiStat.first().items() )
    ...

    , ('wobei', [1]), ('write', [10]), ('wurde', [1]), ('www', [40]), ('wwwheise', [19]),

    ('wwww', [2]), ('xml', [3]), ('zaehler', [6]), ('zahlen', [1]), ('zeitschriften', [1]),

    ('zivilisierten', [1]), ('zivilrechtlichen', [1]), ('zoneid', [2]), ('zu', [10]),

    ('zudem', [1]), ('zum', [4]), ('zur', [1]), ('zusammen', [3]), ('zuvor', [1]),

    ('zwischen', [3])]
    >>> print sorted( heiStat.second().keys())

    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25,

    26, 27, 29, 30, 32, 35, 36, 38, 39, 40, 44, 48, 50, 51, 53, 54, 64, 66, 69, 75, 82, 89,

    128, 138, 166, 171, 317]
    >>> print heiStat.second()[18]

    ['_blank', 'navi_links']
    >>> print sorted( heiStat.second().items() )

    ...
    ['ads']), (35, ['gif']), (36, ['title', 'alt']), (38, ['table']), (39, ['und']),

    (40, ['www']), (44, ['img', 'option', 'span']), (48, ['src', 'border']),

    (50, ['width', 'http', 'h3']), (51, ['tr']), (53, ['1']), (54, ['meldung']),

    (64, ['heise']), (66, ['newsticker']), (69, ['de']), (75, ['br']), (82, ['b']),

    (89, ['0']), (128, ['td']), (138, ['p']), (166, ['class']), (171, ['href']), (317, ['a'])]

    Delegation

    Die Delegation ist dem Kompositum sehr ähnlich. Während in dem Kompositum das Zusammenstellen von Typen aus Subtypen unter dem Strukturaspekt betrachtet wird, liegt der Blickwinkel bei der Delegation auf dem Verhalten des neuen Types. Die Klasse delegiert Aufgaben an ihre Subtypen, die sie dann in ihrer spezifischen Form erfüllen.
    Die Delegation als Strukturmittel bietet sich insbesondere bei Typen an, in denen sich Strategien zur Arbeitung von Unteraufgaben identifizieren lassen.

    • car.py
    class Car(object):
    """get the price from a assembled car"""
    def __init__(self, wh, mo, bo ):
    self.wheel= wh
    self.motor=mo
    self.body=bo
    def getCar(self):
    print "Offer : " + str(self.__getPrice())
    print self.__getName()
    print
    def __getPrice(self):
    return 4*self.wheel.getPrice() + self.motor.getPrice() + self.body.getPrice()
    def __getName(self):
    return """assembled from Wheels: %s
    Motor : %s
    Body : %s """%( self.wheel.getName(), self.motor.getName(), self.body.getName())

    class CarPart(object):
    @classmethod
    def getName(cls): return cls.__name__

    class VWwheel(CarPart):
    def getPrice(self): return 100

    class VWmotor(CarPart):
    def getPrice(self): return 500

    class VWbody(CarPart):
    def getPrice(self): return 850

    class BMWwheel(CarPart):
    def getPrice(self): return 300

    class BMWmotor(CarPart):
    def getPrice(self): return 850

    class BMWbody(CarPart):
    def getPrice(self): return 1250

    class Trabiwheel(CarPart):
    def getPrice(self): return 30

    class Trabimotor(CarPart):
    def getPrice(self): return 350

    class Trabibody(CarPart):
    def getPrice(self): return 550


    vw= Car(VWwheel(),VWmotor(),VWbody())

    bmw= Car(BMWwheel(),BMWmotor(),BMWbody())

    trabi= Car(Trabiwheel(),Trabimotor(),Trabibody() )

    def assembleCars( num ):
    "assemble randomly num cars"
    import random
    wheels= (VWwheel(),BMWwheel(),Trabiwheel())

    motors= (VWmotor(),BMWmotor(),Trabimotor())

    bodies= (VWbody(),BMWbody(),Trabibody())

    for i in range(num):
    randomCar= Car(random.choice(wheels),random.choice(motors),random.choice(bodies ))

    randomCar.getCar()


    Der Typ Car setzt sich aus drei Untertypen wheel, motor und body zusammen. Jeder dieser Untertypen weiß, wie es die spezifischen Anfragen bezüglich des Namens und Preises zu beantwortet hat. Car delegiert die Aufrufe an seine Bestandteile und stellt sie dar.

    >>>>>> bmw.getCar()

    Offer : 3300
    assembled from Wheels: BMWwheel

    Motor : BMWmotor
    Body : BMWbody


    >>>>>> vw.getCar()

    Offer : 1750
    assembled from Wheels: VWwheel
    Motor : VWmotor

    Body : VWbody


    >>>>>> trabi.getCar()

    Offer : 1020

    assembled from Wheels: Trabiwheel
    Motor : Trabimotor
    Body : Trabibody


    >>>>>> # assemble a scCar

    ...

    >>>>>> scCar= Car( VWwheel(), BMWmotor(), Trabibody() )

    >>>>>> scCar.getCar()

    Offer : 1800
    assembled from Wheels: VWwheel

    Motor : BMWmotor
    Body : Trabibody


    >>>>>> # assemble randomly cars

    ...

    >>>>>> assembleCars( 4 )

    Offer : 2550

    assembled from Wheels: BMWwheel
    Motor : VWmotor
    Body : VWbody

    Offer : 2100
    assembled from Wheels: VWwheel
    Motor : BMWmotor

    Body : VWbody

    Offer : 2950
    assembled from Wheels: BMWwheel

    Motor : VWmotor
    Body : BMWbody

    Offer : 2220

    assembled from Wheels: Trabiwheel
    Motor : BMWmotor
    Body : BMWbody



    Die Erzeugung von neuen Typen durch Komposition von Untertypen und die Delegation der Strategien an diese Untertypen ist wesentlich mächtiger als die Bildung von Untertypen durch Vererbung. Dies betrifft im wesentlichen zwei Aspekte:

    1. Definition der Untertypen:
      • Delegation zu Laufzeit (dynamisch)
      • Unterklassenbildung beim Source schreiben (statisch)
    2. kombinatorische Betrachtung der Unterklassenanzahl (am Beispiel Car)
      • Delegation : Basisklasse + 3 * ( wheel + motor + body ) = 10
      • Ableitung: Basisklasse + 3*wheel * 3*motor * 3*body= 28

    Die Delegation als Strukturprinzip, um Verhalten zu modellieren, ist unter dem Namen StrategyPattern oder Policy Based Design ein feststehender Begriff.

    Mehrfachvererbung mix-in

    Während die bisherigen Klassenmodellierungen mit den Standard OO Konstrukten möglich waren, setzen die mix-in Klassen Mehrfachvererbung voraus. Die Grundidee besteht darin, eine neue Klasse zu erzeugen, die aus mehreren Basisklassen zusammengemischt wird.
    Das klassische Beispiel ist ein http Server, der eine statische Seite an den Aufrufer übermittelt. Beim http Server registriert man in diesem Fall einen Handler, der auf http-GET Anfragen reagiert. Mein Handler rechnet die Zeit bis zum Ende der Python Schulung um 17:00 aus und gibt eine html-Seite zurück.

    import BaseHTTPServer
    class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(self):
    import datetime
    import os
    import threading
    actTime= datetime.datetime.now()

    endTime= datetime.datetime(datetime.datetime.now().year, datetime.datetime.now().month ,

    datetime.datetime.now().day ,17)
    diffSeconds= (endTime-actTime).seconds
    self.send_response(200)
    self.send_header('Content-type', 'text/html')

    self.end_headers()
    self.wfile.write("""<?xml version="1.0" ?>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>

    <title>Wie lange dauerts noch?</title>
    </head>

    <body>

    <h1 align="center"><font size="10" color="#FF0000">Count down</font></h1>

    <p align="center"> <font size="6"> %s Sekunden noch bis zum Ende der Python Schulung.</font> </p>

    <p align="right"> <font size="4"> <br> <br> <br> <br> served by(process,threads):(%s,%s )</font> </p>

    </body>
    </html>""" %(str(diffSeconds),os.getpid(),str(threading.enumerate()) ))


    Um ihn zu nutzen, muß ich noch die Anwendungslogik beim http Server registrieren und diesen starten.

    >>> import BaseHTTPServer
    >>> seqServ = BaseHTTPServer.HTTPServer(("",4711),RequestHandler)

    >>> seqServ.serve_forever()
    localhost - - [22/Jul/2007 00:02:51] "GET / HTTP/1.1" 200 -

    localhost - - [22/Jul/2007 00:03:02] "GET / HTTP/1.1" 200 -


    BaseHTTPServer.HTTPServer(("",4711),RequestHandler) ist ein einfacher sequentieller Server. Er nimmt einen Request an, macht für diesen einen neuen Handler auf und bearbeitet in diesem Handler den Request.

    Um den Durchsatz des Server zu erhöhen, ist es naheliegend, die Handler in eigenen Prozessen oder Threads zu erzeugen. Der Server muß dann lediglich noch sequentiell die Request entgegennehmen, ist aber dafür von der zeitaufwändigen Bearbeitung des Request frei.
    Durch das hinzumischen eines ForkingMixIn bzw. ThreadingMixIn Klasse kann unser sequentieller Server parallelisiert werden.

    >>> class ForkServer(SocketServer.ForkingMixIn, BaseHTTPServer.HTTPServer): pass

    ...
    >>> forkServer= ForkServer(("",4712),RequestHandler)
    >>> forkServer.serve_forever()

    localhost - - [22/Jul/2007 00:22:47] "GET / HTTP/1.1" 200 -

    ...
    >>> class ThreadServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): pass

    ...
    >>> threadServer= ThreadServer(("",4713),RequestHandler)
    >>> threadServer.serve_forever()

    localhost - - [22/Jul/2007 00:29:31] "GET / HTTP/1.1" 200 -

    ...


    Die feinen Unterschiede der Server sieht man in der Statuszeile served by (process,threads) der HTTP Datei sehr gut.

    ServertypProzeß IDaktive ThreadsBeschreibung
    BaseHTTPServer identisch ein Thread alle Request werden sequentiell abgearbeitet
    ForkServer verändert sich ein Thread für jeden Request wird ein neuer Prozeß erzeugt
    ThreadServer identisch mindesten ein Thread für jeden Request wird ein neuer Thread erzeugt



    Wie funktioniert nun das ganze?
    Alle drei Klassen, BaseHTTPServer.HTTPServer, SocketServer.ForkingMixIn und SocketServer.ForkingMixIn sind Unterklassen von SocketServer. Um eine Clientanfrage in einem Requesthandler zu beantworten, rufen sie jeweils ihre spezifische Methode process_request() auf. Diese Methode ruft den Requesthandler nun im gleichem, in einem neuen Prozeß oder ein einem Thread auf. Durch das Voranstellen des SocketServer.ThreadingMixIn in der Klassendefinition von

    class ThreadServer (SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer)

    wird die process_request() Methode des ersten Basisklasse verwendet.
    Dies Verhalten kann leicht nachgestellt werden.

    >>> class HTTPServer(object):
    ... def process_request(self):
    ... print self.__class__.__name__
    ...
    >>> class ThreadingMixIn(object):
    ... def process_request(self):
    ... print self.__class__.__name__

    ...
    >>> class ThreadingServer(ThreadingMixIn, HTTPServer): pass

    ...
    >>> class OneProcessServer(HTTPServer, ThreadingMixIn): pass

    ...
    >>> thr= ThreadingServer()
    >>> one= OneProcessServer()
    >>> thr.process_request()

    ThreadingServer
    >>> one.process_request()
    OneProcessServer


    Mix-in Klassen in dieser Form sind eine Besonderheit von Python, den zum Beispiel in C++ ist es ein Syntaxfehler, Methoden mehrfach in der gleichen Ableitungshierachien anzubieten.

    Ausblick

    Gegenüberstellung von C++, Java und Python

    Ein paar interessanten Aspekte, in denen sich die drei objektorientierten Programmiersprachen unterscheiden.

    EigenschaftC++JavaPython
    explizite Instanzbindung choice-no choice-no choice-yes
    Klassenmethoden choice-no choice-no choice-yes
    Überladen von Methoden choice-yes choice-yes choice-no
    Mehrfachvererbung choice-yes choice-no choice-yes
    Operator Überladung choice-yes choice-no choice-yes
    Zugriffskontrolle choice-yes choice-yes choice-no
    Zugriffskontrolle der Basisklassen choice-yes choice-no choice-no
    Virtualität choice-yes choice-no choice-yes choice-yes
    Interfaces choice-yes choice-no choice-yes choice-no
    Garbage Collection choice-no choice-yes choice-yes
    Multiparadigmen Programmiersprache choice-yes choice-no choice-yes


    Nach dieser tabellarischen Gegenüberstellung folgen nun die Details.

    Kapselung

    Operator Überladung

    Operator Überladung erlaubt es, eigene Datentypen mit built-in Verhalten zu implementieren.
    C++ und insbesondere Python besitzen sehr mächtige Operator Überladungs Konzepte. Selbst Funktionen sind in Python nur Objekte, auf denen spezifische Methoden überladen sind. Java unterstützt kein Operator Überladung, sieht man von Strings ab.
    Operator Überladung ist in Python ein wichtiges Architekturmittel um das Interface von der privatenImplementierung zu trennen.

    Zugriffskontrolle
    Variablen und Methoden

    Python bietet im Gegensatz zu C++ und Java keine Zugriffskontrolle auf seine Attribute an. Das Verbergen der Attribute einer Klasse vor der Aussenwelt mittels privat und die Sichtbarmachung der Klasseninternas für abgleitete Klassen mittels protected kennt Python nicht.
    Python verwendet pseudo-private Attribute. Dies sind Attribute, die mit zwei Unterstrichen __ beginnen. Diese Attribute werden gemangelt, damit sie nicht mehr so einfach ansprechbar sind.

    >>> class NameMangling(object):
    ... def __privateMethode(self): pass

    ...
    >>> nm= NameMangling()
    >>> dir( nm )
    ['_NameMangling__privateMethode', '__doc__', '__module__']

    >>> nm._NameMangling__privateMethode()


    Das Name mangling mit einem führenden Unterstrich und dem Klassennamen ist in dieser Form definiert, so daß man von aussen auf die privatenAttribute eines Objekts in definierter Weise zugreifen kann.

    Basisklassen

    C++ Klassen werden per default von der Basisklasse private abgeleitet. Dies bedeutet, das nur die public Methoden und Variablen in der abgeleiteten Klasse sichtbar sind.
    Im folgenden beschränke ich mich auf die Methoden der Klasse.

    • Durch eine public Basisklasse, werden public und protected Methoden zu public und protected Methoden in der abgeleiteten Klasse.
    • Durch eine protected Basisklasse, werden public und protected Methoden zu protected Methoden in der abgeleiteten Klasse.
    • Durch eine private Basisklasse, werden public und protected Methoden zu privaten Methoden in der abgeleiteten Klasse.

    Zur Veranschaulichung soll die folgende Klassenstruktur dienen.

    • Child leite ich public von A, protected von B und private von C ab.
    • GrandChild ist eine öffentliche Unterklasse von Child.
    • Jede Basisklasse bietet drei Methoden jeweils public, protected und private an.
    #include <iostream>

    #include <string>

    class A{

    public: std::string getAPublic() { return "APublic "; }

    protected: std::string getAProtected() { return "AProtected ";}

    private: std::string getAPrivate() { return "APrivate ";}

    };

    class B{

    public: std::string getBPublic() { return "BPublic "; }

    protected: std::string getBProtected() { return "BProtected ";}

    private: std::string getBPrivate() { return "BPrivate ";}

    };

    class C{

    public: std::string getCPublic() { return "CPublic "; }

    protected: std::string getCProtected() { return "CProtected ";}

    private: std::string getCPrivate() { return "CPrivate ";}

    };

    class Child: public A, protected B, private C {

    public:
    std::string inChild(){
    return getAPublic() + getAProtected() + "\n" +

    getBPublic() + getBProtected() + "\n" +

    getCPublic() + getCProtected();
    }
    };

    class Grandchild: public Child{

    public:
    std::string inGrandChild(){
    return getAPublic() + getAProtected() + "\n" +

    getBPublic() + getBProtected();
    }
    };

    int main(){

    Child child;
    std::cout << "--------- in child.inChild-------------- " << std::endl;

    std:: cout<< child.inChild() << std::endl;

    Grandchild grandChild;
    std::cout << "-------- in grandChild.inGrandChild------ " << std::endl;

    std::cout << grandChild.inGrandChild() << std::endl;
    std::cout << "------------ from child ----------------- " << std::endl;

    std::cout << child.getAPublic() << std::endl;
    std::cout << "---------- from grandChild -------------- " << std::endl;

    std::cout << grandChild.getAPublic() << std::endl;


    };

    Die maximale Sichbarkeit auf die Methoden der Basisklasse ergibt folgende Ausgabe:

    --------- in child.inChild-------------- 
    APublic AProtected
    BPublic BProtected
    CPublic CProtected
    -------- in grandChild.inGrandChild------
    APublic AProtected
    BPublic BProtected
    ------------ from child -----------------
    APublic
    ---------- from grandChild --------------
    APublic

    Durch public inheritance ist die abgeleitete Klasse immer auch eine Basisklasse und kann daher im gleichen Kontext wie die Basisklasse verwendet werden. Die abgeleitete Klasse is_a Basisklasse.

    Die öffentlich abgeleitete Klasse leitet das Interface ab, während die privat abgeleitete Klasse die Implementierung ableitet.
    Instanzen der privat abgeleiteten Klasse können weder das Interface der Basisklasse nützen noch sind sie interfacekompatibl zur Basisklasse.

    class Base{};

    class Derived: private Base{};

    void allowOnlyBase( const Base& base ){};

    int main(){

    const Derived& d= Derived();
    allowOnlyBase(d);

    }

    Dieser Code führt beim Übersetzen zu der erwarteten Fehlermeldung, da Derived nicht interfacekompabil zu Base ist.

    Fehler: Base ist eine nicht erreichbare Basis von Derived

    Eine typische Anwendung für das private Ableiten ist das AdapterPattern. Ziel des Musters ist es, eine bestehende Klasse mit einem neuem Interface auszustatten. Dazu leitet der Programmierer public von dem neuem Interface und private von der ursprünglichen Klasse ab.

    Mehrfachvererbung

    In Python wird Mehrfachvererbung gerne in Form von mix-in Klassen verwendet. Der Pythoninterpreter geht nach dem Prinzip first-match vor, um sein Attribut aufzulösen. Im Gegensatz hierzu werden alle Methoden einer Ableitungshierachie in C++ als gleichwertig angesehen. Daher können in C++ die Methoden nicht eindeutig aufgelöst werden - insofern sie die gleiche Signatur besitzen - und führen zum Abbruch des Compilevorgangs.

    >>> class BaseA(object):
    ... def name(self): print "BaseA"

    ...
    >>> class BaseB(object):
    ... def name(self): print "BaseB"

    ...
    >>> class Derived(BaseA,BaseB): pass
    ...
    >>> d=Derived()

    >>> d.name()
    BaseA

     

    • das äquivalente Programm in C++
    #include <iostream>

    class BaseA{
    public:
    void name() const {

    std::cout << "BaseA" << std::endl;
    }
    };

    class BaseB{
    public:
    void name() const {

    std::cout << "BaseB" << std::endl;
    }
    };

    class Derived: public BaseA, public BaseB {};


    int main(){

    Derived d;
    d.name();

    }

     

    • erzeugt nicht das gewünsche Ergebnis
    grimm@ackbar test $ g++ derived.cpp
    derived.cpp: In function 'int main()':

    derived.cpp:23: error: request for member 'name' is ambiguous
    derived.cpp:12: error: candidates are: void BaseB::name() const

    derived.cpp:5: error: void BaseA::name() const

     

    Interfaces

    Ein Interface ist im objektorientierten Entwurf eine spezielle Basisklasse, die die Schnittstelle für alle von ihr abgeleiteten Klassen vorgibt. In Java bestehen Interfaces aus Methodendeklaration und Konstanten. Interfaces sind Schnittstellendefinitionen, die in allen abgeleiteten Klassen implementiert werden müssen. Erst die abgeleiteten Klassen können instanziiert werden.

    public interface HumanBeing {

    String getSex();
    }

    public class Man implements HumanBeing {

    public String getSex() {
    return "male"
    }


    }


    Weder C++ noch Python unterstützen Interfaces.
    C++ und Python 3.* kennen das Konzept der rein virtuellen Methode. Klassen, die rein virtuelle Methoden enthalten, können nicht instanziiert werden und werden insofern als abstrakte Klassen bezeichnet. Abstrakte Klassen, die auch von Java angeboten werden, gehen über die reine Deklaration hinaus.

    #include <string>

    #include <iostream>

    class HumanBeing{
    public:
    virtual std::string getSex()=0;

    std::string decorateMan(){
    return "I'm ";
    }

    };

    class Man: public HumanBeing{
    public:

    std::string getSex() {
    return decorateMan() + "male";

    }
    };

    int main(){

    HumanBeing* hb=new Man();

    std::cout << hb->getSex() << std::endl;

    }

     

    • erzeugt die Ausgabe
      I'm male

    Die Vorgabe, die Interfaces oder abstrakten Klassen an die abgeleitete Klasse setzen, ist ein sehr wichtiges Entwurfsprinzip. Klassen werden gegen das Interface programmiert.
    Das Interface ist der stabile Faktor im Enwurf, während die Implementierung ausgetauscht werden kann.

    interface Instrument {
    void play();

    }

    class Percussion implements Instrument {
    public void play() {

    System.out.println("Percussion.play()");
    }
    }

    class Brass implements Instrument {

    public void play() {
    System.out.println("Brass.play()");

    }
    }

    public class music{
    public static void main(String[] args) {

    Instrument ins= new Percussion();
    ins.play();

    ins= new Brass();

    ins.play();
    }


    }
    • erzeugt folgende Ausgabe
    Percussion.play()
    Brass.play()


    Dieser Interface orientierten Entwurf - Implementiere gegen das Interface - der statisch typisierten Sprachen, steht dem duck-typing Entwurf der dynamisch typisierten Sprachen - wie Python - entgegen:
    If it walks like a duck and quacks like a duck, I would call it a duck. 

    Polymorphie

    In Python und Java sind Methoden immer virtuell. In beiden Sprachen wird erst zu Laufzeit des Programms bestimmt, von welchem Typ das Objekt ist. Dies Verhalten wird gerne späte oder dynamische Bindung genannt und muss in C++ explizit über das Schlüsselwort virtual bei der Methodendeklaration ausgedrückt werden. Falls die Methode nicht als virtual deklariert ist, besitzt sie den statischen Typ zur Compilezeit.
    Die Klasse Base und Derived sollen den Unterschied zwischen virtuell und nicht virtuell, bzw. zwischen später und früher Bindung verdeutlichen.

    #include <iostream>

    class Base{

    public:

    virtual std::string getVirtual() const { return "Base"; }

    std::string getNotVirtual() const { return "Base"; }

    };


    class Derived: public Base{

    public:
    virtual std::string getVirtual() const { return "Derived"; }

    std::string getNotVirtual() const { return "Derived"; }

    };


    int main(){

    Base* b=new Derived();

    std::cout << "b->getVirtual() : " << b->getVirtual() << std::endl;

    std::cout << "b->getNotVirtual() : " << b->getNotVirtual() << std::endl;

    }


    Abhängig von der Virtualität der Methode wird die Methode aus Base oder Derived aufgerufen:

    b->getVirtual() : Derived
    b->getNotVirtual() : Base

     

    Erweiterungen des OO-Konzepts

    Multiparadigmen Programmierung

    Mit Einschränkungen von Generics, die Java seit 1.5 unterstützt, ist Java streng objekt-orientiert. Java kennt keine freien Funktionen, so daß main als Einstiegssprung in das Programm eine statische Methode sein muß.
    C++ und Python werden als multiparadigmen Programmiersprachen bezeichnet. Neben dem prozeduralen prozeduralen unterstützen sie das funktionale und das aspektorientierte Programmierparadigma.
    Python erweitert die objektorientierte Programmierung um das Konzept Klassen zu instanziieren. ( metaclass programming)
    Die C++ Umsetzung der generischen Programmierung mit Templates, ermöglicht darüber hinaus das generativeProgrammierparadigma.

    Klassenmethoden

    Python bietet seit Einführung des Descriptorprotokolls Klassenmethoden. Dies sind im Gegensatz zu Instanzmethoden Methoden, die an die Klasse gebunden sind. Klassenmethoden können mit einer Klasseninstanz aufgerufen werden. Statische Methoden, die auch Java und C++ kennen, werden auf dem Klassenqualifier aufgerufen, bedürfen daher keiner Klasseninstanz.
    Python kennt drei Arten von Methodenbindungen, die an die Instanz, die Klasse oder den Namensraum der Klasse (statisch).

    Besonderheiten

    Garbage Collection

    Sowohl Python als auch Java unterstützen Garbage Collection .

    Java verwendet in der Regel tracing collectors zur Speicherbereinigung. In diesem Verfahren werden von den aktiven Objekten ausgehend alle Objekte markiert, die erreicht werden können. Für die weitere Vorgehensweise existieren zwei Alternativen:

    1. mark and sweep: alle Objekte, die nicht erreicht werden können, werden gelöscht
    2. mark and compact: Objekte, die erreicht erreicht werden können, werden kompakt zusammenkopiert, so daß die verbleibenden Objekte gelöscht werden können

    Beide Algorithmen können zirkuläre Referenzen auflösen. Mit mark und compact wird darüberhinaus noch der Speicher defragmentiert. Der tatsächlich verwendete Speicherfreigabealgorithmus hängt von der Implementierung der Java Virtual Machine ab.

    Python verwendet einen reference countingAlgorithmus zur Speicherfreigabe. Dieser Algorithmus zeichnet sich dadurch aus, das jedes Objekt mitzählt, wie oft es referenziert wird. Fällt der Referenzzähler auf 0, kann das Objekt gelöscht werden. Pythons Implementierung des Referenzzählers entdeckt auch zyklische Referenzen.

    explizite Instanzbindung

    Die Zugehörigkeit der Attribute zu einem Objekte wird in Python explizit über den Bezeichner self gesetzt. Dabei bezeichnet self kein Schlüsselwort sondern eine Namenskonvention. self referenziert die Objektidentität.
    Entscheidend für die Objektreferenz ist nicht der Namen, sondern die Position des Names bei der Definition der Methode. Ob ich nun die Objektidentität mit self oder rainer bezeichne, tatsächlich wird immer das gleiche Objekt referenziert.

    >>> class MyClass(object):
    ... def __init__(self):

    ... self.test=5
    ... print id(self)

    ... def printMe(rainer):
    ... rainer.test=5

    ... print id(rainer)
    ...
    >>> a=MyClass()

    3085067564
    >>> a.printMe()
    3085067564
    >>> print id( a)

    3085067564
    >>> a.test
    5


    Entsprechend der Namenskonvention self für die Instanzmethoden, hat sich cls für Klassenmethodenetabliert.

    Überladen von Methoden

    Überladen von Methoden bezeichnet die Möglichkeit, mehrere gleichnamige Methoden in einer Klasse mit verschiedenen Parameterlisten (Signaturen) zu definieren. Zur Laufzeit des Programms entscheidet dann der Compiler oder Interpreter, welche Methode am besten passt.
    Da Python die Methoden und Attribute zu einer Instanz in einem Instanzdictionary hält, und der Name der Methode oder des Attributes den Schlüssel im Dictionary darstellt ist der letzte Eintrag in das Dictionary der gültige.
    Kurz und gut, Python unterstützt kein Überladen von Methoden.

    >>> class MethodenUeberladung(object):

    ... test=5
    ... def test(self, a): print "one"

    ... def test(>>> class MethodenUeberladung(object):

    ... test=5
    ... def test(self, a): print "one"

    ... def test(self): print "zero"
    ...
    >>> a=MethodenUeberladung()

    >>> dir( a )
    ['__doc__', '__module__', 'test']

    >>> a.test()
    zero

  • Öffentliche Vorträge

     
    JahrTitelVeranstaltungOrtDatumLängeMaterial
    2012 C++11: Quo vadis? CeBIT Hannover 10.03.2012 40 min video
      C++11: Quo vadis? Gesellschaft für Informatik Heidelberg 19.09.2012 90 min pdf
      C++11: Quo vadis? Gesellschaft für Informatik Karlsruhe 17.10.2012 90 min pdf
      C++11: Quo vadis? Gesellschaft für Informatik Dortmund 05.11.2012 90 min pdf
      C++11: An overview Meeting C++ Neuss 09.11.2012 90 min pdf video
      Functional Programming in C++11 Meetting C++ Neuss 10.11.2012 45 min pdf video
      C++11: Quo vadis? Gesellschaft für Informatik Ostwestfalen 04.12.2012 90 min pdf
    2013 Python, die Sprache für den Systemadministrator? CeBIT Hannover 05.03.2013 30 min  
      Embedded programming with C++11 Meeting C++ Düsseldorf 09.11.2013 60 min pdf
    2014 Embedded Programmierung in C++ Advanced Developers Konferenz Garching 29.04.2014 80 min pdf
      Funktionale Programmierung in C++ Advanced Developers Konferenz Garching 30.04.2014 80 min pdf
      Functional Programming in C++ C++ User Group Russia Saratov 24.10.2014 60 min pdf video
      Embedded Programmierung - die Domäne von C++? Embedded Software Engineering Kongress Sindelfingen 02.12.2014 40 min pdf
      Multithreading done right? Meeting C++ Berlin 02.12.2014 60 min pdf video
    2015 Multithreading done right? C++ Conference Moskau 26.02.2015 60 min pdfvideo
      Programmierung zur Compilezeit Advanced Developers Konferenz Erding 06.05.2015 80 min pdf
      Multithreading, richtig gemacht? Advanced Developers Konferenz Erding 06.05.2015 80 min pdf
      Funktionale Programmierung mit C++ Linuxtag Tübingen 13.06.2015 60 min pdf
      Functional Programming in C++ Central-European Functional Programming School Budapest 08.07.2015 90 min pdf
      Programmierung zur Compilezeit Embedded Software Engineering Kongress Sindelfingen 01.12.2015 40 min pdf
    2016 Das C++-Speichermodell Parallel 2016 Heidelberg 06.04.2016 75 min pdf
      15 Tipps (oder warum es nur 10 wurden) Advanced Developers Konferenz Erding 26.04.2016 80 min pdf
      Das C++-Speichermodell Advanced Developers Konferenz Erding 27.04.2016 80 min pdf
      Das C++-Speichermodell C++ Usergruppe München Planegg 28.04.2016 80 min pdf
      15 Tipps (oder warum es nur 10 wurden) Linuxtag Tübingen 11.06.2016 60 min pdf
      15 Tipps (oder warum es nur 10 wurden) oose Abendvortrag Hamburg 29.09.2016 70 min pdf
      The C++ memory model Meeting C++ Berlin 18.11.2016 60 min pdfvideo
      Funktionale Programmierung mit modernem C++ Embedded Software Engineering Kongress Sindelfingen 29.11.2016 45 min pdf
    2017 Funktionale Programmierung in C++ C++ Usergruppe Karlsruhe Karlsruhe 11.01.2017 60 min

    pdf audioTalk audioDiscussion

      Parallelism and Concurrency in C++17 and C++20 Multicore@Siemens Nürnberg 08.02.2017 45 min pdf
      Programming at Compile Time emBO++ Bochum 18.02.2017 45 min pdf
      Programming at Compile Time C++ Conference Moskau 24.02.2017 60 min pdf video
      Funktionale Programmierung in C++ sodge IT GmbH Balingen 13.03.2017 60 min pdf
      Gleichzeitigkeit und Parallelität in C++17 und C++20 Parallel 2017 Heidelberg 30.03.2017 70 min pdf
      Gleichzeitigkeit und Parallelität in C++17 und C++20 C++ Usergruppe München München 03.05.2017 70 min pdf
      Quo vadis Multithreading in C++ Advanced Developers Konferenz München 16.05.2017 70 min pdf
      C++17: Was gibts Neues? Advanced Developers Konferenz München 17.05.2017 70 min pdf
      Threads and Locks must go Meeting C++ Berlin 09.11.2017 60 min pdf video
      Secret Lightning Talk Meeting C++ Berlin 11.11.2017 10 min pdf video
      C++17 Embedded Software Engineering Kongress Sindelfingen 05.12.2017 45 min pdf
    2018 Best Practices für Concurrency Parallel 2018 Heidelberg 07.03.2018 70 min pdf
      Best Practices for Concurrency C++ Russia St. Petersburg 21.04.2018 60 min pdf
      Best Practices für Concurrency C++ Usergruppe Karlsruhe Karlsruhe 10.05.2018 70 min pdf
      Best Practices für Concurrency sodge IT GmbH Balingen 16.05.2018 70 min pdf
      Best Practices für Concurrency Linuxtag Tübingen 09.06.2018 60 min pdf
      Concurrency and Parallelism in C++17 and C++20/23 CoreHard Minsk 03.11.2018 50 min pdf
      The Core Guidelines for Safer Code Meeting Embedded 2018 Berlin 14.11.2018 30 min pdf
      Best Practices for Concurrency Meeting C++ Berlin 17.11.2018 60 min pdf
      Die C++ Core Guidelines für sicheren Code Embedded Software Engineering Kongress Sindelfingen 04.12.2018 40 min pdf
      Migration auf Python 3 Embedded Software Engineering Kongress Sindelfingen 04.12.2018 40 min pdf
    2019 Die bekanntesten (Online-)Compiler im Vergleich Parallel 2019 Heidelberg 21.02.2019 50 min pdf
      Concurreny und Parallelität mit C++17 und C++20/23 Parallel 2019 Heidelberg 21.02.2019 50 min pdf
      Concurrency and Parallelism in C++17 and C+20/23 Cpp Europe Bukarest 26.02.2019 60 min pdf
      Concurrency and Parallelism with C++17 and C++20/23 C++ Russia Moskau 20.04.2019 60 min pdf
      Concepts C++ Italia Mailand 15.06.2019 50 min pdfvideo
      C++20 - Die Revolution geht weiter Linuxtag Tübingen 06.07.2019 50 min pdf
      Concepts CppCon Aurora 16.09.2019 60 min pdfvideo
      Atomics, Locks, and Tasks (Back to Basics) CppCon Aurora 17.09.2019 2 * 60 min

    pdfexamples

    Video1 Video2

      C++20 - The Big Four C++ Russia St. Petersburg 01.11.2019 60 min pdf
      Concepts Meeting C++ Berlin 14.11.2019 60 min pdf video
      Die bekanntesten (Online-)Compiler im Vergleich Embedded Software Engineering Kongress Sindelfingen 03.12.2019 40 min pdf
     2020 Concepts C++ Usergruppe München  Online 26.03.2020 80 min  pdf
      Migration auf Python 3 enterPy Online 26.05.2020 45 min pdf
      Concepts Cpp Europe Online 23.06.2020 60 min pdf 
      Concepts C++ Usergruppe Karlsruhe/Dresden Online 10.07.2020 60 min pdf 
      From Functions to Coroutines CppCon Online 15.09.2020 60 min pdfvideo
      Smart Pointers (Back to Basics) CppCon Online 17.09.2020 60 min pdfvideo
      From Functions to Coroutines Meeting C++ Online 14.11.2020 60 min pdf
      C++20 - Die Revolution geht weiter Embedded Software Engineering Kongress Online 01.12.2020 40 min pdf
    2021 Erweitern und Einbetten von Python enterPy Online 15.04.2021 45 min pdf
      C++20 - Die Revolution geht weiter Advanced Developer Konferenz Online 18.05.2021 60 min pdf
      C++20 - Die Revolution geht weiter Uni Zwickau Online 17.06.2021 80 min pdf
      Concurrency Patterns CppCon Online 25.10.2021 60 min pdf video
      const and constexpr (Back to Basics) CppCon Online 26.10.2021 60 min pdf video
      Object Oriented Programming (Back to Basics) CppCon Online 27.10.2021 60 min pdf video
      C++20: The Small Pearls CppCon Online 28.10.2021 60 min pdf
      C++20: The Hidden Pearls Meeting C++ Online 11.11.2021 60 min pdf video
      Erweitern und Einbetten von Python Embedded Software Engineering Kongress Online 30.11.2021 40 min pdf
    2022 const and constexpr Meeting C++ Online 25.01.2022 60 min pdf video
      Extend and Embed Python Embo++ Online 26.03.2022 50 min pdf
      C++20: The Small Pearls ACCU Bristol 06.04.2022 90 min

    pdfvideo

      Ranges C++20 Techniques for Algorithmic Trading Online 26.04.2022 25 min pdf video (2:32)
      Concurrency Pattern Cpp Europe Online 24.05.2022 60 min pdf
      Extend and Embed Python C++ North Toronto 18.07.2022 60 min pdf video
    2023            
  • Online-Schulungen

     

      Medialinx IT-Academy
    TitelInhaltLängeDatum
    Python für Systemadministratoren Ein kompakter Einstieg in Python für Systemadministratoren 5 Stunden 04/2013

     

      video2brain
    TitelInhaltLängeDatum
    Moderne Software-Entwicklung mit C++11 Ein Überblick über C++11 3 1/2 Stunden 07/2013
    C++ - Das große Training Die aktuelle C++-Kernsprache 10 Stunden 06/2014-08/2014
         
         
         
         
         
  • Python-Schulung

    In den letzten Jahren hielt ich cirka 20 mal eine 3-tägige Python Schulung. In diesem Zusammenhang entstand das folgende Tutorial, das durchaus zum Selbststudium geeignet ist.

    Ich setze keine tieferen Programmierkenntnisse voraus. Lediglich das Modul zur Objektorientierung besitzt einen deutlich höheren Anspruch. Grundlage für das Tutorial ist Python 2.*, es finde sich aber öfters Verweise auf die Python 3.*

    Die einzelnen Module sind in sich abgeschlossen und folgen der typischen Reihenfolge eines Pythontutorials. Am Ende jedes Moduls ist der verwendete Sourcecode, dies betrifft auch die Lösungen zu den Übungsaufgaben, angeheftet.

  •  

    Reactor Pattern

    Um was gehts

    Das Reaktor Pattern erlaubt es, einer ereignis-getriebenen Anwendung eine oder mehrere Client Anfragen gleichzeitig anzunehmen und auf verschiedene Serviceanbieter zu verteilen ( demultiplex and dispatch ) .

    Auch bekannt unter

    • Dispatcher
    • Notifier

    Beispiel

    • Ein Logging Server erhält mehrere Anfragen gleichzeitig und muß sie auf die verschieden Ausgabegeräte verteilen:
      NetworkLogging.gif
    • Eventhandling ins GUIs

    Anforderungen

    Ein Server soll mehrere Clientanfragen gleichzeitig beantworten können. Jede Clientanfrage besitzt eine eindeutige Indikation, die sie einem Serviceprovider zuordnen läßt. Folgende Bedingungen müssen für die Applikation gewährleistet sein:
    • sie soll nicht blockieren
    • sie soll auf maximalen Durchsatz ausgelegt sein und somit unnötiges Kontext wechseln, Daten synchronisieren oder Daten kopieren vermeiden
    • sie soll einfach um neue und verbesserte Service erweitert werden können
    • sie soll ohne komplexe multithreading und synchronisations Mechanismen auskommen

    Lösung

    Führe für jeden Servicetyp, den die Applikation anbietet, einen Handler ein. Dieser Eventhandler verarbeitet die spezifische Clientanfragen. Registriere den Handler beim Reaktor, der einem synchronen Event Verteiler (event demultiplexer) unterhält um auf eingehende Events zu reagieren. Wenn ein Event im synchronen event demultiplexerauftritt, benachrichtigt dieser den Reaktor, der das Event auf den angefragen Service verteilt.

    Struktur

    Handles

    • identifizieren Eventquellen wie Sockets, Filehandles oder Timersignale des OS-Systems
    • die Eventquellen erzeugen Events wie connect, read, time-out an, die auf den assoziierten Handle geschoben werden
    • der Handle kann nur die entsprechende Operation vollziehen

    synchrone event demultiplex

    • der Verteiler (demultiplexer) wartet auf Indikatoren (indication events), die auf einer Menge von Handles auftreten
    • bis die indication events abgeholt werden, blockiert der event _demultiplexer
    • auf dem assozierten Handle kann nun das eintreffende Ereignis aufgerufen

    Event Handler

    • definiert das Interface um indication events zu prozessieren
    • deklarieren die Services der Applikation

    Konkrete Event Handler

    • verarbeiten indication events in einer applikationsspezifischen Art
    • definieren die Services der Applikation

    Reaktor

    • stellt ein Interface zu Verfügung, damit die Event Handler inklusiver ihrer assozierten Handles registrieren und entfernen kann
    • der Reaktor benützt den synchronen Verteiler (event demultiplexer) um auf die Indikatoren (indicaton events) der Handles zu warten
    • beim Auftreten eines Indikators (indication events) ordnet der Reaktor dies Ereugnis dem entsprechenden Ereignis Handler zu
    • nach der Zuordnung des Ereignis an den entsprechenden Ereignis Handler, ruft ( dispatch ) der Reaktor die assozierte Methode auf dem Event Handler auf
    • der Reaktor startet und unterhält die event loop der Applikation
    Nicht die Applikation, sonder der Reaktor wartet auf indication events, die er auf die entsprechenden konkreten Event Handler verteilt ( demultiplex ) und dann deren assozierte Hook Methode aufruft ( dispatch ). Als Applikationsentwickler gilt es die spezifischen Event Handler zu implementieren und sie beim Reaktor zu registrieren.
    Der Reaktor als Framework stellt eine Ablaufumgebung für die Eventverarbeitung bereit. Diese inversion of control - die Applikation wird durch den Reaktor gesteurt - wird als Hollywood Prinzip bezeichnet.
    Don't call me, we call you.

    • Reaktor Klassendiagramm:
      reactorClassDiagram.gif

     

    Umsetzung

    Timer mit twisted

    Der Reaktor reagiert auf Zeittakte. Diese Ereignisse werden auf die registrierten Handler abgebildet. Sobald die Eventloop des Reaktors mittels reactor.run() gestartet wird, können die Ereignisse verarbeitet werden.
    import time

    from twisted.internet import task
    # http://twistedmatrix.com/trac/browser/trunk/twisted/internet/task.py
    from twisted.internet import reactor

    # http://twistedmatrix.com/trac/browser/trunk/twisted/internet/reactor.py

    # define handler as object
    class Handler():

    def __init__(self, Id ):

    self.__id= Id

    def __call__(self):

    print "Handler with id %s" % self.__id
    print "at %d:%d:%d%s\n" %(time.localtime()[3:6] + ( str(time.time() % 1)[1:] ,))


    # register handler as callable object
    l1 = task.LoopingCall(Handler(1))
    # start the task

    # start calls implicit reactor.callLater(... ) to reschedule the task ( fire the time event )
    l1.start(0.3) # call every 0.3 seconds

    l2 = task.LoopingCall(Handler(2))

    l2.start(1) # call every second

    # running the event loop
    reactor.run()

    Timer mit ACE

    • starte zwei Timer cb1 und cb2, die durch Signale SIGTSTP und SIGINT um den Faktor 10 abgebremst werden können
    Die ACE Variante ist deutlicher verboser, da hier auf Time- und Signalevents mit den entsprechenden CB und Signalhandler reagiert wird. Insbsondere sorgt der TimerDispatcher für das explizite Feueren der Timeevents.
    Durch die schedule Methode des Timers bzw. die register_handler des Reactors werden die Handler registriert. Während der CB Handler auf Timeevents mit handle_timeout reagiert, reagiert der Signalhandler auf Signalevents mit handle_signal. Beide Eventhandler werden durch die gleichen Verteiler bedient ( select ). Die ACE_Timer_Queue bzw. der konkrete Implementierung ACE_Timer_Heap merkt sich die die zukünftigen Zeitpunkte, zu denen sie expire an den Verteiler schickt.
    Die Methode wait_for_event startet die Eventloop, die dann auf die Timer- und Signalevents reagiert.
    #include "ace/Timer_Queue.h"
    #include "ace/Timer_Heap.h"
    #include "ace/Reactor.h"
    #include "CB.h"

    #include "SignalHandler.h"
    #include "TimerDispatcher.h"

    int main()
    {

    CB cb1, cb2;
    cb1.setID(1);
    cb2.setID(2);

    int arg1 = 1, arg2 = 2;

    ACE_Timer_Queue *timer_queue;

    ACE_NEW_RETURN(timer_queue, ACE_Timer_Heap, -1);

    // setup the timer queue

    Timer::instance()->set(timer_queue);

    ACE_Time_Value curr_time = ACE_OS::gettimeofday();

    ACE_Time_Value threeSeconds = curr_time + ACE_Time_Value(3L);
    ACE_Time_Value fourSeconds = curr_time + ACE_Time_Value(4L);

    // start in 3 seconds, each second
    long timerId1= Timer::instance()->schedule(&cb1, &arg1, threeSeconds, ACE_Time_Value(1));

    // start in 4 seconds; each 0.3 secondcs
    long timerId2=Timer::instance()->schedule(&cb2, &arg2, fourSeconds, ACE_Time_Value(0,300000));


    // Strg c
    SignalHandler *mutateTimer1= new SignalHandler( timerId1 );

    // Strg z
    SignalHandler *mutateTimer2= new SignalHandler( timerId2 );

    ACE_Reactor::instance()->register_handler( SIGINT, mutateTimer1);
    ACE_Reactor::instance()->register_handler( SIGTSTP, mutateTimer2);


    // "run" the timer.
    Timer::instance()->wait_for_event();

    return 0;

    }

     

    Dynamische Aspekte

    • Die Applikation registriert einen konkreten Eventhander beim Reaktor. Der Eventhandler drückt durch sein Implementierung aus, auf welche Art von Events er reagieren will. Typischerweise heißen die hook-Methoden handle_*, wie handle_input, handle_timeout, handle_put,... .
    • Durch eine get_handle des konkreten Eventhandlers Methode holt sich der Reaktor den spezifischen Handler.
    • Wenn alle Handles registriert sind, startet die Applikaton die Eventloop des Reaktors. Der Reaktor überwacht nun die Menge aller registrierten Handler auf das Eintreffen von indication Events .
    • Sobals ein Event auftritt, übergibt der synchrone event demultiplexer die Kontrolle an den Reaktor.
    • Der Reaktor benützt die Handles als Schlüssel, um die entsprechenden Eventhandler aufzurufen (demultiplex) und auf die hook Methode auszurufen (dispatch).
    • Die spezifische Methode des Eventhandler bearbeitet die Anfrage direkt auf dem Handle.

    Sichten des Programmierers

    • Reactor Pattern:
      reactor.jpg

    Anwendungsentwickler

    Ich will netzwerktransparent wissen, wie lange die Design Pattern Runde noch dauert? Oder ein bißchen formaler:
    Implementiere einen Server, der auf eine Browser Anfrage ( HTTP-GET ) ein html Seite schickt, die die verbleibende Zeit bis zum Ende der Design Pattern Runde darstellt. Noch formaler
    Client macht eine HTTP-GET Request right Server nimmt Request an und dispatcht sie auf den Event Handler right Event Handler schickt die Antwort zum Client Dazu müssen drei Schritte als Anwendungsentwickler und Nutzer der Reaktor Struktur implementiert werden.
    Ich verwende den BaseHTTPServervon Python.

    Request Handler implementieren

    class RequestHandler(BaseHTTPRequestHandler):


    def do_GET(self):
    import datetime
    actTime= datetime.datetime.now()

    endTime= datetime.datetime( 2007,5,22,9)

    diffSeconds= (endTime-actTime).seconds
    self.send_response(200)
    self.send_header('Content-type', 'text/html')

    self.end_headers()
    self.wfile.write("""<?xml version="1.0" ?>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>

    <title>Wie lange dauerts noch?</title>
    <script src="https://scwiki.science-computing.de/twiki/pub/TWiki/ChecklistPlugin/itemstatechange.js"
    language="javascript" type="text/javascript"></script></head>

    <body>

    <h1 align="center"><font size="10" color="#FF0000">Count down</font></h1>

    <p align="center"> <font size="6"> %s Sekunden noch bis zum Ende der Design Pattern Runde.</font> </p>

    </body>
    </html>""" %(str(diffSeconds)))

    Request Handler registrieren

    srvr = HTTPServer(("",4711),RequestHandler)

    Event loop laufen starten

    srvr.serve_forever()  

    Frameworkentwickler

    Ich will ein Reaktor Framework in Python entwickeln, das auf Input Events reagiert. Exemplarisch werden vier verschiedene Typen von Input Events gleichzeitig prozessiert und die entsprechenden Nachrichten in ähnlichnamige Dateien ins home geschrieben.
    1. stdin
      • jede stdin Eingabe erzeugt ein Event, das zur Prozessierung des Events führt
    2. lesen einer Datei
      • nach dem Einlesen der Datei wird der Handler wieder entfernt
    3. lesen einer url
      • nach dem Einlesen der Internetressource wird der Handler wieder entfernt
    4. HTTP - GET Anfrage
      • bei jedem stdin Event will ich wissen, wie lange die Design Pattern Runde noch dauert; dazu müssen periodisch folgende Schritte vollzogen werden
        • registriere den Event Handler, um den ReactorPattern Server zu fragen
        • darstellen des Ergebnisses auf stderr
        • deregistrieren des Event Handlers
    ##################
    #Event Handlers
    ##################

    import os
    import socket
    import sys
    import time


    class EventHandler:

    def handle_input(self,device):
    raise NotImplementedError

    def handle_output(self,device):
    raise NotImplementedError

    def handle_exception( self,device):
    raise NotImplmentedError

    def getHandle( self ):
    return NotImplementedError

    class InputEventHandler( EventHandler ):

    def __init__(self,device,dest):

    self.handleDevice= device
    self.outFile=open(dest,"a")

    firstLine= "Read from %s with handle %s \n" % ( device.name , device.fileno() )

    self.outFile.writelines([firstLine,"\n"])


    def handle_input(self,device):


    inp= device.readline().strip()
    self.outFile.write( inp + "\n" )

    self.outFile.flush()
    myReactor.registerHandler( InputToStderrEventHandler( urllib.urlopen("http://ackbar:4711"),

    os.getenv("HOME")+"/test.WieLangNoch" ) , "r")

    def getHandle( self ): return self.handleDevice

    class FileInputEventHandler( InputEventHandler ):

    def __init__(self,device,dest):

    self.handleDevice= device
    self.outFile=open(dest,"a")

    name=""
    try:
    name= device.name
    except:

    name= device.geturl()

    firstLine= "Read from %s with handle %s \n" % ( name , device.fileno() )

    self.outFile.writelines([firstLine,"\n"])


    def handle_input( self, device ):

    for line in device.readlines():
    self.outFile.write( line.strip() + "\n" )

    self.outFile.flush()
    Reactor().removeHandler( self ,"r" )

    class InputToStderrEventHandler( FileInputEventHandler ):

    def handle_input( self, device ):

    for line in device.readlines():
    self.outFile.write( line.strip() + "\n" )

    if line.startswith("<p align"):
    message= line.split(">")[2].split("<")[0]

    sys.stderr.write( message )
    self.outFile.flush()

    Reactor().removeHandler( self,"r")





    #############

    # Reactor
    #############

    class Singleton(object):
    def __new__(cls, *args, **kwds):

    it = cls.__dict__.get("__it__")
    if it is not None:

    return it
    cls.__it__ = it = object.__new__(cls)

    it.init(*args, **kwds)
    return it
    def init(self, *args, **kwds):

    pass



    import select

    class Reactor( Singleton ):

    readHandles={}
    writeHandles={}
    exceptHandles={}


    def registerHandler( self, eventHandler,eventTypes):

    handle= eventHandler.getHandle()
    handleId= handle.fileno()


    if "r" in eventTypes: Reactor.readHandles[handleId]= (handle,eventHandler)

    if "w" in eventTypes: Reactor.Reactor.writeHandles[handleId]= (handle,eventHandler)

    if "e" in eventTypes: Reactor.Reactor.exceptHandles[handleId]= (handle,eventHandler)


    def removeHandler( self, eventHandler ,eventTypes ):

    handle= eventHandler.getHandle()
    handleId= handle.fileno()


    if "r" in eventTypes: del Reactor.readHandles[handleId]

    if "w" in eventTypes: del Reactor.writeHandles[handleId]

    if "e" in eventTypes: del Reactor.exceptHandles[handleId]


    def handleEvents( self):

    while ( 1 ):

    rHandle, wHandle,eHandle= select.select( Reactor.readHandles.keys(), Reactor.writeHandles.keys(),
    Reactor.exceptHandles.keys() )

    print "all ready handle: Reactor.readHandles: %s Reactor.writeHandles: %s Reactor.exceptHandles: %s "
    %(rHandle,wHandle,eHandle)

    for han in rHandle:
    handleDevice= Reactor.readHandles[han][0]

    eventHandler= Reactor.readHandles[han][1]
    eventHandler.handle_input( handleDevice )

    for han in wHandle:
    handleDevice= Reactor.writeHandles[han][0]

    eventHandler= Reactor.writeHandles[han][1]
    eventHandler.handle_output( handleDevice )


    for han in eHandle:
    handleDevice= Reactor.exceptHandles[han][0]

    eventHandler= Reactor.exceptHandles[han][1]
    eventHandler.handle_exception( handleDevice )

    print "Reactor:handleEvents: waiting for input "

    import urllib

    myReactor=Reactor()
    myReactor.registerHandler( InputEventHandler( sys.stdin ,
    os.getenv("HOME")+"/test.stdin" ) ,"r")

    myReactor.registerHandler( FileInputEventHandler( open("/etc/services"),
    os.getenv("HOME")+"/test.services" ) ,"r")

    myReactor.registerHandler( FileInputEventHandler( urllib.urlopen("http://www.heise.de"),
    os.getenv("HOME")+"/test.heise" ) , "r")

    myReactor.handleEvents()

    Aspekte des Reaktor Frameworks

    Event Handler Interface festlegen
    class EventHandler:        

    def handle_input(self,device):
    raise NotImplementedError

    def handle_output(self,device):
    raise NotImplementedError

    def handle_exception( self,device):
    raise NotImplmentedError

    def getHandle( self ):
    return NotImplementedError
    Reaktor implementieren
    import select

    class Reactor( Singleton ):

    readHandles={}
    writeHandles={}
    exceptHandles={}


    def registerHandler( self, eventHandler,eventTypes):

    handle= eventHandler.getHandle()
    handleId= handle.fileno()


    if "r" in eventTypes: Reactor.readHandles[handleId]= (handle,eventHandler)

    if "w" in eventTypes: Reactor.Reactor.writeHandles[handleId]= (handle,eventHandler)

    if "e" in eventTypes: Reactor.Reactor.exceptHandles[handleId]= (handle,eventHandler)


    def removeHandler( self, eventHandler ,eventTypes ):

    handle= eventHandler.getHandle()
    handleId= handle.fileno()


    if "r" in eventTypes: del Reactor.readHandles[handleId]

    if "w" in eventTypes: del Reactor.writeHandles[handleId]

    if "e" in eventTypes: del Reactor.exceptHandles[handleId]


    def handleEvents( self):

    while ( 1 ):

    rHandle, wHandle,eHandle= select.select( Reactor.readHandles.keys(), Reactor.writeHandles.keys(),
    Reactor.exceptHandles.keys() )

    print "all ready handle: Reactor.readHandles: %s Reactor.writeHandles: %s Reactor.exceptHandles: %s "
    %(rHandle,wHandle,eHandle)

    for han in rHandle:
    handleDevice= Reactor.readHandles[han][0]

    eventHandler= Reactor.readHandles[han][1]
    eventHandler.handle_input( handleDevice )

    for han in wHandle:
    handleDevice= Reactor.writeHandles[han][0]

    eventHandler= Reactor.writeHandles[han][1]
    eventHandler.handle_output( handleDevice )


    for han in eHandle:
    handleDevice= Reactor.exceptHandles[han][0]

    eventHandler= Reactor.exceptHandles[han][1]
    eventHandler.handle_exception( handleDevice )

    print "Reactor:handleEvents: waiting for input "
    Um ein Eventhandler zu registrieren wird zusätzlich der Event Typ benötigt, für den sich der Event Handler interessiert.
    Durch die Registratur des Eventhandler ist es möglich, über den Filehandle ( z.B.: stdin = 0 ) sowohl das Fileobject, die Eventsource und den Eventhandler, die Anwendungslogik zu erhalten.
    In handle Events bedient sich der Reaktor dem nativen select Befehl um auf relevante Events registrieren zu können. handleEvents stellt die Eventloop des Reaktor dar, die, einmal gestartet, immer auf eingehende Events lauscht.

    Ausgabe, abhängig von den registrierten Event Handles

    • stdin wird registriert
      python reactorInput.py
      4
      all ready handle: Reactor.readHandles: [0] Reactor.writeHandles: [] Reactor.exceptHandles: []
      Reactor:handleEvents: waiting for input
    Erst durch die Eingabe der Zahl 4 wird die Eventloop prozessiert. Ein Eingabe Event Reactor.readHandles: [0]liegt nun vor.
    • stdin, Datei und Url Request werden registriert
      python reactorInput.py
      all ready handle: Reactor.readHandles: [4, 6] Reactor.writeHandles: [] Reactor.exceptHandles: []
      Reactor:handleEvents: waiting for input
      4
      all ready handle: Reactor.readHandles: [0] Reactor.writeHandles: [] Reactor.exceptHandles: []
      Reactor:handleEvents: waiting for input
    Die Ressourcen file und url sind beim Starten der Eventloop registriert, daher werden sie sofort prozessiert all ready handle: Reactor.readHandles: [4, 6] Reactor.writeHandles: [] Reactor.exceptHandles: [] . Da ich sie explizit deregistriere sind sie bei in dem Eintreten eines stdin-Events nicht mehr vorhanden all ready handle: Reactor.readHandles: [0] Reactor.writeHandles: [] Reactor.exceptHandles: []. Der Reaktor prozessiert nun nur noch den Filedescriptor 0, also stdin.
    • stdin, Datei , Url und HTTP-GET Request werden registriert
      all ready handle: Reactor.readHandles: [4, 6] Reactor.writeHandles: [] Reactor.exceptHandles: []
      Reactor:handleEvents: waiting for input
      4
      all ready handle: Reactor.readHandles: [0] Reactor.writeHandles: [] Reactor.exceptHandles: []
      Reactor:handleEvents: waiting for input
      all ready handle: Reactor.readHandles: [4] Reactor.writeHandles: [] Reactor.exceptHandles: []
      49661 Sekunden noch bis zum Ende der Design Pattern Runde.Reactor:handleEvents: waiting for input
    Die stdin Abfrage registriet nun einen neuen Eventhandler, der die mir die Frage beantwortert: Wie lange dauert noch die Design Pattern Runde? Dieser Request erhält wieder den Filedescriptor 4. all ready handle: Reactor.readHandles: [4] Reactor.writeHandles: [] Reactor.exceptHandles: []

    Implementierung

    Die Implementierung des Reaktor Patterns lässt sich in zwei Schichten unterteilen. Die Frameworkschicht, die die applikationsunabhängige demultiplex/dispatch Infrastruktur zur Verfügung stellt und die Applikationschicht, die die konkreten Eventhandler liefert. In der klassischen, einfachsten Form, geschieht das ganz Eventhandling in einem Prozeß.

     Definiere das Event Handler Interfaces

    Die Methoden des Event Handlers legen das Servcie-Interface des Reaktor Frameworks fest.
    1. bestimme den Typ des dispatchingZiels
      • verwende eine Event Handler Objekt oder eine Event Handle Funktion
    2. bestimme die Event Handling dispatching Strategie
      1. dispatch auf eine einzelne Methode
        ...
        virtual void handlle_event( Handle handle, Event_Type et)= 0;
        ...
      2. dispatch auf mehrere Methoden
        ...
        virtual void handle_input( Handle handle )=0;
        virtual void handle_output( Handle handle )=0;
        virtual void handle_timeout( Handle handle )=0;
        ...
      • das eine Methode Interface erlaubt es einfach, das Framework um neue Eventtypen zu erweitern
      • während bei handle_event die ganze Verteilungsstrategie mittels Bedingungen auf Applikationsebene definiert werden muß, geschieht der dispatch auf dem reichhaltigeren Interface automatisch auf Frameworkebene
      • insbesondere ist bei feingranularen Dispatch möglich, spezielle hook Methoden in konkreten Event Handlern zu überschreiben

     Definiere das Reaktor Interface

    Die Applikation nutzt einerseits das Reaktor Interface um die spezifischen Event Handler zu de/registrieren und andererseits die Event Loop zu starten. Gerne wird das Reaktor Interface als Singleton implementiert, das die Anfragen an die Reaktor Implementierung delegiert. Neben dem Event Handler erhält erhält die register_handler als zweites Argument den Event Type als Argument, für den sie sich interessiert.
    void Select_Reactor_Implementation::register_handler( EventHandler* eventHandler, Event_Type event_type )

     Implementiere das Reaktor Interface

    • entkopple das Reaktor Interface von seiner Implementierung durch eine Brücke right mehrere verschiedene Implementierung können unterstützt werden ( select, poll, WaitFormMultipleObjects , GuiEventLoops ,... )
    • wähle einen synchronen event demutliplexer aus
    int select( u_int max_handle_plus_1 , 
    fd_set *read_fds, fd_set * write_fds, fd_set *except_fds,

    timeval *timeout);
    • implementiere ein demultiplexing table
      • ein Eintrag soll von der Form < handle, event_handle, indication event type > sein, wobei handle als Schlüssel für den Event Handler bei einem indication event ( connect, expire, read,... ) verwendet wird
    • definiere die Reaktor Implementierung
    # select Server( only demultiplexing ) 
    def get_request(self):

    while self._running:
    log.info('select on listen socket')
    # demultiplex

    rs, ws, es = select.select([self.socket], [], [], 10)

    if rs:
    log.info('accepting new connection')
    # socketobject and address
    return self.socket.accept()
    log.info('ending request loop')

    return (None, None)

     Bestimme die Anzahl der Reaktoren, die man benötigt

    • in der Regel sollte der Reaktor ein Singleton sein, jedoch erlaubt win32 nur 64 Handles pro Reaktor
    • aus Echtzeitforderungen kann es nötig sein mehrere Reaktoren gleichzeitig laufen zu lassen; Trennung der Reaktoren nach Eventtypen

     Implementiere die konkreten Eventhandler

    • sie stellen die Anwendungslogik dar, im Gegensatz zu dem bisher vorgestellten Reaktor-Framework
    • statte die Eventhandler gegebenfalls mit einem Zustand aus; vgl. Beispiel Timer mit ACE
    • implementiere die Eventhandler Funktionalität

    Kritik

    • Vorteile
      • klare Trennung von Framework- und Applikationslogik
      • Modularität von eventgetriebenen Anwendungen durch verschieden Eventhandler
      • Portabilität durch Trennung von Interface und Implementierung des Reaktors
      • einfache Parallelität durch den synchronen event demutliplexers
    • Nachteile
      • setzt einen event demultiplexer voraus
      • Durchsatzprobleme bei lang laufenden Event Handler in single Threaded Applikation, den der Event Handler blockiert den Reaktor
      • schwierig zu debuggen und zu testen durch die inversion of control

    Verwendete Patterns - Techniken

    • ObserverPattern
      • der Event Handler wird informiert, sobald ein für ihn spezifisches Event auftritt
    • BridgePattern
      • der Reaktor hält sich eine spezifische Reaktor Implementierung, an die er die Aufrufe delegiert
    • TemplateMethodePattern
      • die handle_* Methoden als hook Methoden werden wohl in statisch typisierten Programmiersprachen in einer definierten Reihenfolge prozessiert
    • double Dispatch: registerHandler right getHandle

     

  •  

     

    Standard Library


    Da der aktuelle Python Library Index als auch die Python Library Referenzsehr reichhaltig ist, kann ich nur einzelne Aspekte herausgreifen.

    Operating System

    sys

    • Systemspezifische Parameter und Funktionen in sys
      • die Kommandozeile: sys.argv
      • Ausstieg aus Python: sys.exit([arg])
      • Suchpfad für Module: sys.path
      • Plattforminfo: sys.platform und insbesondere getwindowsversion()
      • die Ausgabekanäle: sys.stdin, sys.stdout und sys.stderr

    os

    • betriebssystemunabhängige Funktionalität rund ums operating system in os
      • Information zum aktuellen Prozeß und Benutzer
        • lesen und setzen von Umgebungsvariablen: os.getenv("PATH") und os.putenv("LANG","de_DE")
        • lesen der Prozess ID's
        • setzen der os.umask(mask)
      • Arbeiten mit Dateien und Direktories
        • erhalte das aktuelle Arbeitsverzeichnis: os.getcwd()
        • setzen des Modus und Owner/Group: os.chmod(path, mode) und chown(path, uid, gid)
        • Links anlegen: os.symlink(src, dst) und os.hardlink(src,dst)
        • Manipulation von Dateien und Verzeichnissen wie umbenennen und löschen
        • Traversieren des Dateibaumes: os.listdir(path) und os.walk(top[,topdown=True])
      • Prozessmanagment
        • Einfluß auf Prozesse: os.kill..., os.nice(add) und os.fork...
        • auf Kindprozesse warten : os.wait...
      • Verschieden Systeminformationen
        • low level Konstantenen gekapselt: os.curdir, os.sep, os.defpath , os.linesep und os.devnull

    os.path

    • Funktionen für Pfadnamen os.path
      • Info über den Pfadnamen: os.path.basename(path), os.path.dirname(path) und os.path.split(path)
      • Info über das referenzierte Ziel: os.path.exists(path)
      • Manipulation des Pfadnames: os.path.abspath(path), os.path.join(path), os.path.normpath() und os.path.normcase(path), os.path.isabsolut(path), os.path.istfile(path), os.path.isdir(path) und os.path.ismount(path)

    subprocess

    • high level Funktionen für den Umgang mit Subprozessen in subprocess
      • ersetzt os.system, os.spawn..., os.popen..., popen2... und commands...

    Threads

    thread

    • thread bietet ein sehr einfaches Interface zu Threads und Locks

    threading

    • threading erlaubt den komfortablen Umgang Threads
    • bietet Locks, Condition Variablen, Semaphoren, Events und Timer Objekte an

    queue

    • queue ist für viele Konsumer und Producer ausgelegt
    • dient insbesondere der Kommunikation von Threads

    multiprocessing

    • neu mit Python 2.6
    • multiprocessing besitzt das gleiche Interface wie threading, startet aber eine Prozess statt eines Thread
    • ist auch auf Windows verfügbar

    Kommandozeile

    optparse

    • optparse ist eine mächtiger Kommandozeilenparser, der dem älteren Kommandozeilenparser getopt vorzuziehen ist
    • er erzeugt Usage- und Hilfenachrichten inklusive

    Datum und Zeit

    time

    • time ermöglicht den einfachen Zugriff auf die Uhrzeit
    • time.time() gibt die Sekunden seit der Epoche, time.asctime() die Zeit als String zurück
    • darüber hinaus existieren weitere Methoden zur formatierten Ausgabe der Zeit

    datetime

    • datetime erlaubt komfortable Datum und Zeit Manipulationen
    • Datum und Zeit Objekte können verrechnet und mittels Formatstrings ausgegeben werden
    • das Modul unterstützt Zeitzonen

    Dateien

    shutil

    • high level Datei Operationen in shutil
      • Kopiere Dateien: shutil.copy...
      • Manipuliere Dateibäume: shutil.copytree(src,dst), shutil.rmtree(src) und shutil.move(src, dst)

    fnmatch

    • Unix filename pattern matching fnmatch
      • prüfe, ob ein Dateinamen einem gegeben Pattern genügt, und gib entweder den Dateinamen fnmatch oder eine Boolean fnmatchcase zurück
      • belasse die Dateien in der Sequenz, die den Filter filter erfüllen

    Persistenz

    pickle / cpickle

    • Serialisieren von Python Objekten um sie in Dateien zu schreiben in pickle bzw. cpickle
      • Objekt MOVED TO... Bytestrom: pickle.dump(object,fd)
      • Bytestrom MOVED TO... Objekt: pickle.load(fd)
      • TIP cpickle sollte pickle vorgezogen werden, das es schneller als pickle ist
      • sqlite3 sollte [http://docs.python.org/lib/module-pickle.html][pickle]] vorgezogen werden

    sqlite3

    • neu mit Python 2.5
    • leichtgewichtige Datenbank sqlite3, die keinen Serverprozess benötigt
    • öffnen der Datenbank conn = sqlite3.connect(filename)
    • Zugriff mittels eines Cursors: c=conn.cursor()
    • absetzten von SQL Statements: c.execute(SQL-Statement)
    • abspeichern der Modifikations: c.commit()

    Strings

    • die String Services zeichnen sich nicht durch ein klares Design aus
    • ein Teil der String Funktionalität ist als built-in vorhanden, der Rest ist im gleichnamigen Modul enthalten

    built-in

    string

    • in stringfinden sich weitere Funktionen rund um Strings
      • Konstanten, die das Leben leichter machen: string.ascii_letters, ascii.digits, string.letters, string.lowercase, string.printable und string.uppercase
      • erzeugen eines Übersetzungsstrings: string.maketrans(from, to)

    re

    • die mächtigste Manipulationsmöglichkeiten stehen mit regulär Expressions rebereit
      • kompilieren von regulär Expressions um ein reguläre Expression Objekt zu erhalten: re.compile(str)
      • Übereinstimmungen finden und ein Match Objekt erhalten: re.match(str), re.search(str), re.findall() und re.finditer()
      • Auswerten des Match Objekts: group(), start(), end() und span()
      • den String manipulieren: re.split(), re.sub() und re.subn()

    Mathematik

    math und cmath

    math und cmath für complexe Zahlen erhalten die bekannten mathematischen Grundoperationen über die built-inFunktionen hinaus

    random

    • in randomsind die Funktionen um Zufallszahlen zu erzeugen
      • Initialisieren den Zufallszahlengenerator: random.seed()
      • Erhalte Zufallszahlen: random.randrange(start,stop) und random.choice(seq)
      • Zufällige Permutation einer Liste: random.shuffle(x)
      • einige Verteilungen aus der Stochastik

    Internet

    socket

    • in socket werden die low level Funktionen zur Socketmanipulation zur Verfügung gestellt
    • die bekannte Socket API für den Server socket(), bind(), listen(), accept() und den Client socket(), connect() steht zur Verfügung um TCP/UDP Verbindungen zu instanziieren

    urllib

    • urllib erlaubt den high level Umgang mit www Ressourcen
    • urlopen(url) gibt ein Dateihandle auf eine Ressource zurück
    • urlretrieve(url) gibt den Inhalt der Ressource zurück
    • Python unterstützt eine Vielzahl von Internet Protokollen und Technologien Internet Protokolle, die socket vorraussetzen
    • den Umgang mit den verschiedenen Datenformaten wird durch Datenformate im Internet abgedeckt

    ssl

    • mit Python 2.6 ist die ssl Unterstützung vollständig
    • sowohl Python Clients wie auch Server können ssl sprechen und mit Zertifikaten umgehen

    Server

    SocketServer

    • SocketServer bietet ein Framework für verschiedene Server (TCP und UDP ) und deren Request-Handler
    • beim Instanziieren des Servers muß der Request Handler registriert werden, die die seine Funktionaliät in der handle() Methode anbietet
    • die Request können auf einfache Weise mit den ThreadingMixIn bzw. ForkingMixIn in Threads oder Prozessen prozessiert werden

    BaseHTTPServer

    Beispiele

    • die Methoden string.maketrans und translate als built-in Methode für Strings bieten eine sehr komfortable und performante Schnittstelle, um Charakter eines Strings zu manipulieren;
      schreibe ein Programm, das
      • HAND das Charakter eines String auf andere umsetzt
      • die gegebenen Charakter aus einem String entfernt
      • nur die gegebenen Charakter in einem String behält
    • eine sehr mächtige Methode ist os.walk;
      • HAND Wie kann die Ausgabe angepasst werden?

     

  •  

     

    Typen und Operatoren

    Konzepte

    Typklassen

    • Immutabel: Zahlen, Strings und Funktionen
      • können nicht verändert werden
      • können nur auf der rechten Seite (rvalue) einer Zuweisung stehen
      • so ist
    a="test"
    b=a[2:]

    erlaubt,
    ALERT!hingegen führt

    a[0]="T"

    zur Exception

    • Mutabel: Dateien, Listen, Dictionaries, Klassen und deren Instanzen
      • können verändert werden
      • können auch auf der linken Seite (lvalue) einer Zuweisung stehen
    a={}
    a["1"]=1
    • TIP daß Klassen mutabel sind, unterscheidet Python von Java und C++
      MOVED TO... zur Laufzeit kann die Definition einer Klasse modifizieren werden
    class A:
    pass

    A.b= 3
    A.test = lambda self : 10
    • Aufrufbar: Funktionen und Klassen
    • Indizierbar: Tupel, Strings, Listen und Dictionaries
    • Sequenzen: Tupel, Strings und Listen

    Zuweisung

    ALERT! Die Unterscheidung zwischen Referenz- und Kopiersemantik bei der Zuweisung ist in Python insbesondere auch eine Unterscheidung zwischen mutablen und immutablen Datentypen.

    Durch einen Ausdruck der Form

    y=x


    wird nicht der Speicherinhalt der Variable kopiert, sondern nur eine neue Bindung an den Speicherinhalt erzeugt.
    Da hier nur eine neue Referenz (Alias) auf das von x referenzierte Speicherobjekt erzeugt wird, spricht man von der Referenzsemantik beim Kopieren.
    Wird hingegen der Speicherinhalt mitkopiert, nennt man dies Kopiersemantik .

    • Kopiersemantik:
      • C, Pascal, C++
    • Referenzsemantik:
      • Python, Java, C++, Fortran

    Folgendes Bild soll dies noch verdeutlichen:

    • Referenz- versus Kopiersemantik:
      Referenz versus Kopiersemantik
    • REFACTOR  der  Unterschied zwischen zuweisen und kopieren
    a=5
    b=a
    b=4

    print a,b

    aber

    a=[5]

    b=a
    b[0]=100
    print a,b

    im ersten Fall wird ein (immutables) Objekt neu zugewiesen, hingegen im zweiten Fall ein (mutables) Objekt modifiziert
    HELP Der klassische swap Algorithmus setzt call by value voraus und führt daher bei Python call by objectnicht zu dem gewünschten Ergebnis.

    def swap1(a,b):

    tmp=a
    a=b
    b=tmp
    • hier werden Aliase für die Aufrufargumente a,b erzeugt, die im Funktionskörper vertauscht werden
    • ALERT! das Vertauschen der Objektreferenzen findet nur im Funktionskörper statt
    • führt man hingegen eine Operation auf dem referenzierten Objekte aus, so bleibt die Modifikation bestehen
    def swap2(a,b):
    tmp=a
    a=b
    b=tmp
    b[0]=10
    • TIP um Daten zu vertauschen, muß man auf den Objekten arbeiten
    a,b=b,a

    Freie Funktionen versus Methoden

    • alles in Python ist ein Objekt
    • Methoden sind an Objekte gebunden und können daher nur mit einem Objekt aufgerufen werden. Im Gegensatz hierzu erhalten freie Funktionen das Objekt als Argument.
    obj=[1,2,3]

    print len(obj)
    obj.count(2)
    • während bei len(obj) die freie Funktion obj als Parameter bekommt, ruft der Ausdruck obj.count() die Methode count auf dem Objekt auf

    Lebensdauer

    Namensraum

    • vereinfachend gesprochen gibt es drei Namensräume lokal, global und built in, in denen Namensbezüge in der Suchreihenfolge
      lokal MOVED TO... global MOVED TO... built in aufgelöst werden

    Datentypen

    • REFACTOR um zu sehen, welche Operationen ein Datentyp zur Verfügung stellt, hilft die Introspektionsfähigkeit von Python und eine interaktive Pythonshell
    dir(5)

    dir( " " )
    dir( [] )
    dir( () )

    dir( {} )

    Einfache Datentypen - Zahlen

    Folgende Datentypen stehen zur Verfügung:

    • Boolean Typ bool mit den Werten True und False seit Python 2.3
    • ganze Zahlen
      • int: ganze Zahlen fester Länge, abhängig von der Python Implementierung
      • long int: ganze Zahlen fast beliebiger Länge
    • float: Fließkommazahlen fester Länge, abhängig von der Python Implementierung
    • complex: Paare von floats
    • Python besitzt keinen Datentyp char 

    Numerische Operationen

    • Die Schreibweise und Präzedenz von Literalen entspricht der von C bzw. Java.
    • Die Zuweisung kann auch in der kompakteren - performanteren - Form geschrieben werden.
      So sind die beiden Ausdrücke und die konkreten Beispiele äquivalent:
      <var>= <var><op><exp>
    <var><op>=<expr>

    a=a+1
    a+=1
     

     

    OperationstypOperationenganze ZahlenfloatscomplexErläuterung
    Grundoperationen + - * / ** % // choice-yes.gif choice-yes.gif choice-yes.gif ** (Potenzieren) % (Modulo) // (Floor Division)
    Standardfunktionen abs divmod pow choice-yes.gif choice-yes.gif choice-yes.gif pow(x,y[,m])= (x ** y % m )
    divmod(x,y)= (int(x/y),x%y)
    bitweise shift << >> choice-yes.gif     << bitweise verschieben nach links
    bitweise Operation & | ^ ~ choice-yes.gif     & (and) | (or) ^ (xor) ~ (Negation)
    Runden round   choice-yes.gif   round(a,b) wobei b die Tiefe darstellt

    Logische Operatoren

    • folgende Ausdrücke evaluieren zu False, wenn sie in logischen Ausdrücken verwendet werden
      • die Integer 0 und die float 0.0
      • der empty Placeholder None, vergleichbar mit den Null Pointer in C
      • die leere List [], das leere Tupel () und das leere Dictionary {}
      • MOVED TO... alle anderen Werte evaluieren zu True
    • logische not, and und orOperatoren
    False and (False or True)


    schon durch das erste False bestimmt

    • Vergleichsoperatoren: is, ==, <>, !=, <, >, <=, >=
      • TIP is testet die Objektidentität, die anderen Vergleichsoperatoren den Wert des Objekts auf Gleichheit

    Zusammengesetzte Datentypen

    Sequentielle Typen - Strings, Tupel und Listen

    1. alle Sequenzen unterstützen folgende Operationen

      OperationBeschreibung
      s[i] i-te Elemente
      s[i:j] Slice von s[i] auschließlich s[j]
      s[i:j:k] vom Slice s[i:j] jedes k-te Element beginnend bei s[i]
      e in s ist e Element von s
      len(s) Anzahl der Elementen von s
      min(s) Minimum von s
      max(s) Maximum von s
      • bei den Operationen auf den Sequenzen sollte man folgendes beachten:
        • die Indizes beginnen bei 0
        • für die Parameter i,j,k eines Slices s[i:j:k]gilt:
          • i ist immer einschließlich
          • j ist immer ausschließlich zu sehen
          • k besitzt den Defaultwert 1
          • fehlende Parameter werden abhänig von k bestimmt
            1. k > 0 :
              • fehlende Parameter werden auf -sys.maxint und sys.maxint gesetzt, so daß gilt:
                s[::k] ist äquivalent zu s[-sys.maxint:sys.maxint:k]
            2. k < 0 :
              • fehlende Parameter werden auf sys.maxint und -sys.maxint gesetzt, so daß gilt:
                s[::k] ist äquivalent zu s[sys.maxint:-sys.maxint:k]
        • Strings und Tupel sind immutabel, d.h. alle Operationen sind im Gegensatz zu Listen nur lesend möglich
        • während der Indexzugriff gegebenfalls ein Exception wirft, gibt der Slicezugiff auf eine Sequenz eine leeren Slice zurück
    2. mutable Sequenzen unterstützen noch die zwei zusätzlichen Löschfunktionen

      OperationBeschreibung
      del s[i] lösche das i-te Elemente
      del s[i:j] lösche den Slice von s[i] auschließlich s[j]
      • REFACTORerzeuge eine Liste
        • erzeuge Schnitte dieser Liste
          • ab einer Position
          • vorwärts/rückwärts
          • nur jedes i-te Element
        • lösche einzelne Elemente/Bereiche
    Strings
    • immutable Folgen von ASCII- oder Unicode-Zeichen
    • Schreibweise:
      • " ": native String
      • r" ": raw String
      • u" ": Unicode String
    • neben den Operationen für Strings als immutabler Sequenz, gibt's für Strings noch weitere Methoden

      OperationBeschreibung
      s.capitalize() Schreibe den ersten Buchstabe von s groß
      s.center( w ) stelle s mittig in einem Feld der Länge w dar
      s.count(sub[,start[,end]]) Zähle das Vorkommen eines Substrings sub
      s.encode([encoding[,errors]]) Gib eine encodierte Version von s zurück
      s.endswith( suffix[,start [,end]] ) Prüfe das Ende von s auf den Substring suffix
      s.expandtabs( [tabsize] ) Expandiere tabs
      s.find(sub[,start [,end]] ) Finde das erste Vorkommen von sub in s
      s.index(sub [,start [,end]] ) Finde das erste Vorkommen von sub in s oder wirf eine Exception
      s.isalnum() Prüfe, ob alle Zeichen alphanumerisch sind
      s.isalpha() Prüfe, ob alle Zeichen alphabetisch sind
      s.isdigit() Prüfe, ob alle Zeichen Zahlen sind
      s.islower() Prüfe, ob alle Zeichen Kleinbuchstaben sind
      s.isspace() Prüfe, ob alle Zeichen whitespace sind
      s.istitle() Prüfe, ob s ein Titel ist
      s.isupper() Prüfe, ob alle Zeichen Großbuchstaben sind
      s.join(t) Verbinde die Strings in t mit s als Trenner
      s.ljust(w) Links-align s in einem String der Länge w
      s.lower() Gibt den String von s in Kleinbuchstaben zurück
      s.lstrip() Entferne führende Leerzeichen
      s.replace( old, new [,maxreplace]) Ersetze von Substring von old mit new
      s.rfind(sub [,start [,end]]) Finde das letzte Vorkommen von sub in s
      s.rindex(sub [,start [,end]] ) Finde das letzte Vorkommen von sub in oder wirf eine Exception
      s.rjust(w) Rechts-align s in einem String der Länge w
      s.rstrip() Entferne endende Leerzeichen
      s.split([sep [,maxsplit]]) Splitte s mit sep als Trenner
      s.splitlines([keepend]) Splitte eine String in eine Liste von Zeilen.
      Falls keepend auf 1 gesetzt ist, werden folgende newlines berücksichtigt
      s.startswith(prefix[,start [,end]]) Prüfe das Anfang von s auf den Substring prefix
      s.strip() Entferne führende und endende Leerzeichen
      s.swapcase() Gib einen String zurück, indem Groß- und Kleinzeichen vertauscht sind
      s.title() Gib eine Titelversion des Strings zurück
      s.translate( table (, deletechars]) Gibt eine mit table übersetzten String zurück
      s.upper() Gibt den String von s in Großbuchstaben zurück
    Besonderheiten

    rawString

    • durch
     r"mein String

    wird ein rawString definiert

    • in diesem wird ein mit \ beginnender Escapeausdruck nicht interpretiert
    • insbesondere beim regulären Ausdrücken und dem Öffnen von Dateien unter Windows sollten rawStrings verwendet werden:
      • "C:\new\test.dat" wird sonst als "C:(newline)ew(tab)est.dat" interpretiert

    unicodeString

    • Python unterstützt Unicode Strings
    • Unicode Strings werden durch
    u"test"

    definiert

    • für unicode Strings steht die ganze String Funktionalität zur Verfügung
    • wird ein String mit einem unicode String verknüpft, ist der resultierende String ein unicode String
    • durch str(u"test") bzw. unicode("test") kann zwischen den Codierungen konvertieren werden
    • REFACTORtestString="Dies ist ein Teststring."
      • zerlege den String in seine Worte und baue ihn wieder zusammen
      • alignden String "test" auf 40 Zeichen( links, rechts und center )
        • speichere diese Strings ab
        • schneide die Leerzeichen wieder ab
      • besitzt testString das Zeichen T
      • besitzt testString das Zeichen A
    • ALERT!Veränderung mit Python 3.*
      UmsetzungPython 2.*Python 3.*
      8Bit String "string" b"string"
      Unicode String u"string" "string"
    Tupel
    • immutable Folgen von beliebigen Objekten
    • Schreibweise: ()
    Listen
    • mutable Folgen von beliebigen Objekten
    • Schreibweise: []
    • im Gegensatz zu immutablen Sequenzen, die das Ergebnis der Operation zurückgeben, wirken Listenoperationen auf der Liste
      • ALERT! folgender Code soll die Problematik verdeutlichen
    a=[1,2,3]
    a.reverse()

    print a
    a=a.reverse()
    print a

     

    MethodeBeschreibung
    range(n) erzeugt eine Liste [0,1,2,..,n-1]
    range(n,m) erzeugt eine Liste [n,n+1,...,m-1]
    range(n,m,d) reduziert die Liste range(n,m) auf jedes d-te Element
    list(s) Konvertiert die Sequenz s zu einer Liste
    s.append(x) hängt das Element x an die Liste an
    s.extend(t) erweitert die Liste um die Elemente von t
    s.count(x) zählt vorkommen von x in s
    s.index(x) gibt den kleinsten Index i zurück, für den gilt s[i] == x
    s.insert(i,x) fügt x am Index i ein
    s.pop([i]) gibt das Element s[i] zurück und entfernt es aus der Liste.
    Falls i nicht angegeben wird, wird die Operation auf das letzte Argument angewandt
    s.remove(x) sucht nach x und entfernt es aus s
    s.reverse() dreht die Reihenfolge der Elemente von x in place um
    s.sort([cmpfunc]) sortiert die Elemente von s gegebenfalls mit Hilfe von cmpfunc in place

     

    Assoziative Typen - Dictionaries

    • Datencontainer von Paaren (key,values)
    • Schreibweise: { }
    • auch bekannt als: Dictionary, Map, Hash oder Assoziatives Array
    • der Zugriff auf ein Element ist konstant.
    • das Interface des Dictionaries

      MethodeBeschreibung
      len(m) Anzahl der Elemente von m
      m[k] Gibt den Wert m[k] zurück
      m[k]= x Setze m[k] auf x
      del m[k] Entferne m[k] von m
      m.clear() Entferne alle Elemente von m
      m.copy() Gibt eine copy von m
      m.has_key(k) Existiert der Schlüssel k?
      k in m Existiert der Schlüssel k?
      m.items() Gibt eine Liste aller Paare (key,values) von m zurück.
      m.keys() Gibt eine Liste aller Schlüssel von m zurück.
      m.update(b) Füge alle Elemente vom Dictionary b zu m hinzu.
      m.values() Gibt eine Liste aller Werte von m zurück.
      m.get(k[,v]) Gib m[k] zurück, falls es existiert, anderfalls v (default None)
      m.setdefault(k[,v]) Gib m[k] zurück, falls es existiert, anderfalls v und setzte m[k]=v.
      m.pop(k[,v]) Gib m[k] zurück, entferne das Element, falls es existiert.
      Wenn es nicht existiert oder kein Defaultargument angegeben wird, wirf ein Ausnahme.
      m.popitem() Entferne ein zufälliges Element (key,value) von m und gib es als Tupel zurück.

     

    • REFACTORd={"red":1, "green":2, "blue":3}
      • Welcher Wert hat red?
      • Gibt es white?
      • Falls white nicht existiert, gib 0 zurück.
      • Falls white nicht existiert, gib 0 zurück und trage das Paar in d ein.
      • d2={"white":0,"red":1,"purple":4}: Aktualisiere d mit d2
      • HAND Gib d nach Schlüsseln und nach Werten sortiert aus.
      • HAND Gib d nach Schlüsseln und nach Werten rückwärts sortiert aus.

    Konvertierungsoperationen

    KonvertierungBeschreibung
    int(x [,base] ) Konvertiert x nach int
    long(x [,base] ) Konvertiert x nach long int
    float(x) Konvertiert x nach float
    complex(real [,imag]) Erzeugt eine komplexe Zahl
    str(x) Konvertiert x zu einer Stringrepräsentation
    repr(x) Konvertiert x zu einem expression string
    Kurzform `x`
    eval(str) Wertet einen String aus und gibt ein Objekt zurück
    tuple(s) Konvertiert eine Sequenz in ein Tupel
    list(s) Konvertiert die Sequenz s in eine Liste
    chr(x) Konvertiert ein Integer zu seinem Zeichen
    unchr(x) Konvertiert ein Integer zum einem Unicode Zeichen
    ord(x) Konvertiert ein Zeichen in eine natürliche Zahl
    hex(x) Konvertiert eine natürliche Zahl in einen hexadezimal String
    oct(x) Konvertiert eine natürliche Zahl in eine oktalen String
    • die Konvertierungsoperationen repr und eval sind zueinander invers, so dass gilt: a=eval(repr(a))
    • HAND Gib die Strings test20, test18,....., test-4 aus

    built-ins

    • Python bringt ein Fundus an built-in Funktionen und Klassen mit
    • einen einfachen Überblick ergibt
    >>> dir()
    ['__builtins__', '__doc__', '__name__']
    >>> dir( __builtins__ )

    ['ArithmeticError', 'AssertionError', 'AttributeError', 'DeprecationWarning','EOFError', 'Ellipsis', 'EnvironmentError',

    'Exception', 'False', 'FloatingPointError', 'IOError', 'ImportError', 'IndentationError', 'IndexError', 'KeyError',

    'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError',

    'OSError', 'OverflowError', 'OverflowWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError',

    'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError',

    'UnboundLocalError', 'UnicodeError', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__',

    '__doc__', '__import__', '__name__', 'abs', 'apply', 'bool', 'buffer', 'callable', 'chr', 'classmethod', 'cmp', 'coerce',

    'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'eval', 'execfile', 'exit', 'file',

    'filter', 'float', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance',

    'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min', 'object', 'oct', 'open', 'ord',

    'pow', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'round', 'setattr', 'slice','staticmethod',

    'str', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
  •  

     

     

    Wissenswertes rund um Python

    Dokumentation

    built-in Dokumentation

    Python wartet mit einem reichhaltigen Fundus an built-inDokumentation auf.

    dir Funktion

    • eignet sich als erste Suche in die Breite
    • listet das Dictionary von Objekten - alles ist ein Objekt in Python - auf
    >>> dir([])

    ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__',

    ...
    'reverse', 'sort']
    >>> dir()
    ['__builtins__', '__doc__', '__name__']

    >>> import sys
    >>> dir( sys )
    ['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__', '__stdin__',
    ...

    'stdin', 'stdout', 'version', 'version_info', 'warnoptions']
    >>> dir(5)

    ['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__',

    ...
    '__truediv__', '__xor__']

    Docstring:

    Jedem Objekt ist ein __doc__String zugeordnet, der zwei Funktionen erfüllt:

    • durch Introspektion kann das Objekt nach seiner Dokumentation gefragt werden
      • Strings, die am Anfang einer Objektdefinition stehen, werden automatisch als __doc__ Strings verwendet
      • per Default ist der __doc__ Docstring leer
    >>> class A:
    ... "Klasse A"
    ... def foo():

    ... "Methode foo"
    ... def bar():
    ... pass
    ...

    >>> A.__doc__
    'Klasse A'
    >>> A.foo.__doc__

    'Methode foo'
    >>> A.bar.__doc__
    >>> print sys.__doc__

    This module provides access to some objects used or maintained by the
    interpreter and to functions that interact strongly with the interpreter.
    exit() -- exit the interpreter by raising SystemExit

    ....
    getdlopenflags() -- returns flags to be used for dlopen() calls
    getrefcount() -- return the reference count for an object(plus one :-)

    getrecursionlimit() -- return the max recursion depth for the interpreter
    setcheckinterval() -- control how often the interpreter checks for events

    setdlopenflags() -- set the flags to be used for dlopen() calls
    setprofile() -- set the global profiling function

    setrecursionlimit() -- set the max recursion depth for the interpreter
    settrace() -- set the global debug tracing function

    >>> print sys.exit.__doc__
    exit([status])

    Exit the interpreter by raising SystemExit(status).
    If the status is omitted or None, it defaults to zero(i.e., success).

    If the status is numeric, it will be used as the system exit status.
    If it is another kind of object, it will be printed and the system
    exit status will be one(i.e., failure)
    • durch den __doc__Docstring kann automatisch Dokumentation erzeugt werden

    PyDoc

    • help in der Interpreter Shell aufgerufen, erzeugt die Dokumentation in einer manpage ähnlichen Art
    >>> import sys
    >>> help( sys )
    Help on built-in module sys:

    NAME
    sys

    FILE
    (built-in)

    DESCRIPTION

    This module provides access to some objects used or maintained by the
    interpreter and to functions that interact strongly with the interpreter.

    Dynamic objects:

    argv -- command line arguments; argv[0] is the script pathname if known
    path -- module search path; path[0] is the script directory, else ''

    modules -- dictionary of loaded modules
    ...
    >>> help(sys.exit)
    Help on built-in function exit:

    exit(...)
    exit([status])

    Exit the interpreter by raising SystemExit(status).

    If the status is omitted or None, it defaults to zero(i.e., success).

    If the status is numeric, it will be used as the system exit status.
    If it is another kind of object, it will be printed and the system
    exit status will be one(i.e., failure).

    Bücher

    Die folgenden Bücher halte ich für sehr empfehlenswert.

    Grundlagen

    • Python Essential Reference von David M. Beazley
      PythonEssentialReferenceSmall.jpg
      • sehr gutes, kompaktes Tutorial um mit C Kenntnissen Python zu lernen
      • das Standardwerk bei science-computing
      • mittlerweile existierte die 3-te Auflage
    • Learning Python von Mark Lutz und David Ascher
      LearningPythonSmall.jpg
      • sehr gutes Tutorial, das weniger Programmierkenntnisse als Python Essential Reference voraussetzt
    • Core Python Programming von Wesley J. Chun
      CorePythonProgramming.jpg
      • umfassendes Grundlagenbuch
      • geht auch auf fortgeschrittene Konzepte ein

    Praxis

    • Python Cookbook von Alex Martelli und David Ascher
      PythonCookbookSmall.jpeg
      • bewährte Rezepte, um das tägliche Arbeiten mit Python zu erleichtern
      • geht weit über das Anbieten von Lösungen hinaus, da die Rezepte entwickelt und diskutiert werden
      • fokussiert weniger die Syntax von Python, sondern die Frage:
        • Wie kann ich Python sinnvoll einsetzen?
      • die Rezepte stammen aus dem Fundus Python Cookbook
    • Dive Into Python von Mark Pilgrim
      DiveIntoPython.jpg
      • ausgehend von einem sinnvollen Pythonscript, werden die Komponenten von Python erklärt
      • Dive Into Python führt einen schnell in die Tiefen von Python
      • dies Buch ist sowohl für den Einsteiger als Tutorial als auch für den Pythonkenner zur Vertiefung seiner Kenntnisse sehr empfehlenswert
      • Dive Into Python gibts auch online http://www.diveintopython.org/toc/index.html
    • Text Processing in Python von David Mertz
      TextProcessingInPython.gif
      • Python als das Mittel der Wahl, um Text zu verarbeiten, steht bei David Mertz im Mittelpunkt
      • als wichtigste Tools für das Textprozessieren erklärt er detailiert Strings, Regular Expressions und Parser
      • David Mertz hat einen sehr kurzen und prägnanten, funktional orientieren Programmierstil

    Web

    • Python Web Programming von Steve Holden
      PythonWebProgramming.jpg
      • How to build web systems in Python : ( Grundlagen, Datenbanken, XML Verarbeitung und Web Applikation mit Python )
    • Foundations of Python Network Programming von John Goerzen
      foundation-python-net-prog.jpg
      • alles über Client Server Programmierung mit Python ( Grundlagen, Web Services, E-Mail Services, Protokolle, Frameworks und Multitasking )

    Objektorientierte Programmierung

    • Objektorientierte Softwareentwicklung - Analyse und Design mit der UML von Bernd Oesterreich
      ObjektOrientierteSoftwareentwicklung.jpeg
      • sprachunabhängige, sehr gute Einführung in die objektorientierte Denkweise und deren Visualisierung mit Hilfe der Unified Modeling Language
    • Design Patterns. Elements of Reusable Object-Oriented Software. von Erich Gamma, Richard Helm, Ralph E. Johnson und John Vlissides
      DesignPattern.jpeg
      • objektorientiertes Programmierung beschäftigt sich immer auch mit dem strukturieren und modellieren von Programmen
      • dies Buch ist der Klassiker zum objektorientierten Entwurf von der Gang Of Four

    Onlinequellen

    python.org:

    The Official Python Programming Language Websitemit vielen interessanten Topics

    python.de

    Informationen, Artikel, News und Links zu Python

    Python Cookbook

    • diese Seite ist eine hervorragende Quelle für Lösungen und Ideen rund um die Python Programmierung
    • Editor in Chief ist David Ascher
    • die bewährtesten der Rezepte finden sich ausführlich diskutiert in dem Buch Python Cookbook wieder

    effbot.org

    Eine von Fredrik Lundh initiierte Seite Rund um Python Stuff.

     

    Python Quick Reference Card

    • alles, was man über Python wissen sollte pdf

    Guis

    Entwicklungsumgebungen

    • IDLE
      • ist in der Standardinstallation dabei
      • unterstützt kein Refaktoring
      • Einführung
    • Eric3
      • sehr umfangreich
      • setzt pyqt voraus
      • hat eine Designer - sehr ähnlich dem qt-designer integriert - um qt Applikationen zu erstellen
      • schreibe qt Applikationen mit Python
      • screenshots
    • Pydev
       

     

    Introduction

    Get into Python ist eine interaktive Lernplattform mit integrierter REPL.

     

    Styleguides

    • The Zen of Python
      >>> import this
      The Zen of Python, by Tim Peters

      Beautiful is better than ugly.
      Explicit is better than implicit.
      Simple is better than complex.
      Complex is better than complicated.
      Flat is better than nested.
      Sparse is better than dense.
      Readability counts.
      Special cases aren't special enough to break the rules.
      Although practicality beats purity.
      Errors should never pass silently.
      Unless explicitly silenced.
      In the face of ambiguity, refuse the temptation to guess.
      There should be one-- and preferably only one --obvious way to do it.
      Although that way may not be obvious at first unless you're Dutch.
      Now is better than never.
      Although never is often better than *right* now.
      If the implementation is hard to explain, it's a bad idea.
      If the implementation is easy to explain, it may be a good idea.
      Namespaces are one honking great idea -- let's do more of those!
    • http://www.python.org/dev/peps/pep-0008/: Guido van Rossum Styleguide

    Tools

    • um alle referenzierten Module eines Programms zu finden, existiert die undokumentierte Bibliothek modulefinder
      python <path to modulefinder.py> main.py   
      ergibt alle verwendeten Module einer Applikation

    Python Executable erzeugen

     

Mentoring

Stay Informed about my Mentoring

 

Rezensionen

Tutorial

Besucher

Heute 734

Gestern 3357

Woche 12297

Monat 39614

Insgesamt 3892328

Aktuell sind 31 Gäste und keine Mitglieder online

Kubik-Rubik Joomla! Extensions

Abonniere den Newsletter (+ pdf Päckchen)

Beiträge-Archiv

Sourcecode

Neuste Kommentare