Seite 1 von 1

anfängerfrage: das @

Verfasst: Sonntag 6. August 2006, 12:39
von murph
hi!
ich habe letztens irgendwo (es war bei den decoratoren) ein @ gelesen, dessen bedeutung ich nicht verstanden habe.
nun dachte ich mir, das schaue ich mir mal im wiki an, aber: unter "@" lässt sich nichts finden!
könnte das ins wiki gestellt werden?

Verfasst: Sonntag 6. August 2006, 13:13
von mitsuhiko
Das @ gibt es nur für Dekoratoren. Und die Fausregel ist:

Code: Alles auswählen

@foo
def bar():
 pass
ist das gleiche wie

Code: Alles auswählen

def bar():
 pass
bar = foo(bar)
und folgedem ist auch

Code: Alles auswählen

@foo()
def bar():
 pass
das gleiche wie

Code: Alles auswählen

def bar():
 pass
bar = foo()(bar)
Und mehr gibts dazu nicht zu sagen. Wenn du das noch nie gebraucht hast wird dich auch das "@" nicht interessieren.

Verfasst: Montag 7. August 2006, 07:48
von CM
Na ja, hast recht, wobei solch eine Syntax für Classmethods schon ganz nützlich ist ...

Gruß,
Christian

Verfasst: Montag 7. August 2006, 09:15
von murph
aber was bringt ein decorator?
ich habe zwar http://soiland.no/software/decorator gelesen, konnte dem aber keine informationen entnehmen, stehe irgendwie da auf dem schlacuh.

Verfasst: Montag 7. August 2006, 10:14
von beewee
aber was bringt ein decorator?
Wie gesagt, du kannst eine Funktion auf die Funktion anwenden. Zum beispiel sinnvoll bei sowas:

Code: Alles auswählen

class abc:
    @property
    def enabled(self):
        return True

b = abc()
b.enabled # b.enabled, nicht b.enabled()
BeeWee

Verfasst: Montag 7. August 2006, 10:41
von keppla
Sinnvoll sind Dekoratoren zb, um funktionen zu "wrappen".

Gegeben sei zB die Situation, dass eine Art Zugriffsgenehmigung auf Funktionsebene gebraucht ist (hat man häufig in webframeworks).

Man hat viele recht einfache Funktionen, die vom Endbenutzer eines Frameworks geschrieben werden sollen:

Code: Alles auswählen

def pageA(request):
  print "Hello world!"

def pageB(request):
  print "Hello Again!"
Jetzt möchte der Entwickler der Bibliothek diesen Benutzern recht einfach ermöglichen, Sicherheitschecks zu machen. Er macht also einen Dekorator, der in etwa so aussieht:

Code: Alles auswählen

def permissionRequired(unsafeFunction):
  def wrapper(*args, **kwargs):
    if user_has_permission:
      unsafeFunction(*args, **kwargs)
    else:
      print "Breakin' the Law!"
  return wrapper
Es ist eine Funktion, der einen Wrapper für die übergebene Funktion erzeugt.
Diese Funktion kann nun als Dekorator genutzt werden:

Code: Alles auswählen

@permissionRequired
def pageB(request):
  print "Hello Again!"
Das sorgt dafür, dass an den Namen "pageB" nicht mehr die eigentliche funktion pageB gebunden ist, sondern das resultat von permissionRequired(pageB), welches "im prinzip" die funktion B mit einem sicherheitscheck ist.

Es gibt noch einen ganzen Haufen anderer Verwendungen für Dekoratoren, aber diese ist mir bis jetzt am häufigsten untergekommen.

Verfasst: Montag 7. August 2006, 11:35
von birkenfeld
murph hat geschrieben:aber was bringt ein decorator?
Ein Dekorator ist nur "syntactic sugar", der statt

Code: Alles auswählen

def bar():
 pass
bar = foo(bar)
folgende Form erlaubt:

Code: Alles auswählen

@foo
def bar():
 pass
Man spart sich dabei, den Funktionsnamen zweimal zu wiederholen und rückt die Dekoration näher an die Funktionsdefinition, wo man sie schneller erfassen kann.

Verfasst: Montag 7. August 2006, 19:59
von murph
an sich also ganz gut, wenn man bei jedem aufruf einer funktion bestimmte sachen testen möchte etc.

Code: Alles auswählen

def permissionRequired(unsafeFunction):
  def wrapper(*args, **kwargs):
    if user_has_permission:
      unsafeFunction(*args, **kwargs)
    else:
      print "Breakin' the Law!"
  return wrapper
in diesem fall wird beim aufruf einer beliebigen funktion, die nach dem dekorator genannt wird, erst der dekorator aufgerufen, dann überprüft, ob der user die rechte hat, und dann vom wrapper die gewünschte funktion ausgeführt...aber die funktion wird doch automatisch ausgeführt, wenn man sie für einen aufruf benutzt!?! oder wenn man dieses nicht tut, dann sind aber alle argumente in einem tuple, d.h., wenn das ganze auf etwas noch anderem basiert, muss man erstmal den kladderadatsch aus dem tuple holen...oder?

Verfasst: Samstag 26. August 2006, 17:24
von murph
kennt wer ein gutes tutorial dazu?
habe grade genau das richtige für einen wrapper, muss aber bisschen daran rumschrauben.
Danke im Voraus!

Verfasst: Sonntag 27. August 2006, 12:53
von midan23
Hier wäre eventuell was brauchbares ...

Ist zwar kein richtiges Tutorial (eher ein HowTo) dafür aber echt brauchbar.

Für Beispiele kann man ja mal hier vorbeischauen ...

Verfasst: Sonntag 27. August 2006, 19:46
von murph
thx

Verfasst: Montag 28. August 2006, 12:43
von keppla
in diesem fall wird beim aufruf einer beliebigen funktion, die nach dem dekorator genannt wird, erst der dekorator aufgerufen,
jein, das stimmt so nicht ganz. es wird nicht "erst" der dekorator, sondern _nur_ die funktion, die der dekorator zurückgibt, aufgerufen.

man stelle sich vor, man riefe diesen dekorator von hand auf

Code: Alles auswählen

def innocentFunction(a, b, c):
  print a, b, c

# punkt 1

innocentFunction= permissionRequired(innocentFunction)

# punkt 2
man sieht hier: bei punkt 1 ist an den Namen "innocentFunction" eine Funktion gebunden, die a, b, c ausgibt (logisch).
Allerdings wird dieser name nach punkt 1 neu gebunden, und zwar an das resultat der funktion permissionRequired, die auch eine funktion zurückgibt.
dann überprüft, ob der user die rechte hat, und dann vom wrapper die gewünschte funktion ausgeführt...aber die funktion wird doch automatisch ausgeführt, wenn man sie für einen aufruf benutzt!?!
nein, weil diese funktion ja nicht mehr mit diesem namen verknüpft ist.
der name "innocentFunction" ist an den wrapper gebunden, den "permissionRequired" zurückgegeben hat, das ganze ding ist in jeder hinsicht einfach nur eine funktion.
oder wenn man dieses nicht tut, dann sind aber alle argumente in einem tuple, d.h., wenn das ganze auf etwas noch anderem basiert, muss man erstmal den kladderadatsch aus dem tuple holen...oder?
ich verstehe nicht ganz, was du hier meinst.

edit: ich bastele gerade im Pythonwiki an einer Einführung. Anregungen oder Korrekturen sind erwünscht :)