Eingebettete Functionen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Steffo
User
Beiträge: 45
Registriert: Sonntag 24. Mai 2009, 19:38

Hallo,
in Scala ist das problemlos möglich:

Code: Alles auswählen

def __pattern_dissambler(self, string):
	pattern = string[:self._DIGITS_OF_VENDOR_ID]
	
	def is_vendor_pattern():
		return pattern.isdigit()
	
	def get_vendor_name():
		return pattern
		
# ...

dissambler = self.__pattern_dissambler(line)  
if dissambler.is_vendor_pattern():
	vendor_name = dissambler.get_vendor_name()
In Python bekomme ich den Fehler "AttributeError: 'NoneType' object has no attribute 'is_vendor_pattern'".
Kann ich so etwas in Python nicht machen? Müsste doch gehen!

L. G.
Steffo
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Mir erschließt sich nicht ganz wofür das gut sein sollte. Das self deutet ja darauf hin, dass das ganze eh Teil einer Klasse ist. Dann kann, je nachdem was sich schöner abbilden lässt, das ganze wieder eine Klasse sein (dann würde es dein Verhalten abbilden), oder lässt das alles von der bereits vorhandenen Klasse verwalten.
Steffo
User
Beiträge: 45
Registriert: Sonntag 24. Mai 2009, 19:38

sparrow hat geschrieben:Mir erschließt sich nicht ganz wofür das gut sein sollte. Das self deutet ja darauf hin, dass das ganze eh Teil einer Klasse ist. Dann kann, je nachdem was sich schöner abbilden lässt, das ganze wieder eine Klasse sein (dann würde es dein Verhalten abbilden), oder lässt das alles von der bereits vorhandenen Klasse verwalten.
Das stimmt, aber weißt du, wie umständlich das ist?
Ich könnte folgendermaßen vorgehen:
1. Für jedes Pattern eine eigenes Objekt erzeugen. Das Pattern wird als Attribut abgespeichert. --> Objekterzeugung auf Kosten von Performance wenn das in die Tausende geht.
2. Ich erzeuge nur ein Objekt, dafür brauche ich aber bei jedem neuem Pattern eine set_pattern() Funktion.
3. is_vendor_pattern() und get_vendor_name() sind voneinader unabhängig, dafür muss ich aber bei beiden Funktionen folgendes aufrufen:

Code: Alles auswählen

pattern = string[:self._DIGITS_OF_VENDOR_ID]
Und dies kostet wiederum Performance, was kein unerheblicher Teil in meiner Anwendung ist, weil ich eine ziemlich große Datei öffne.

L. G.
Steffo
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

`dissamble` ist der Rueckgabewert von der Funktion, in dem Fall `None`, da du nichts explizit zurueckgibst, und nicht die Methode selbst. Darum kann es _so_ nicht funktionieren.

Leider kann es ueberhaupt nicht funktionieren, da die inneren Funktionen lokal zur aeusseren sind. Soll heissen: Sie existieren nur, wenn die Methode/Funktion ausgefuehrt wird. Du kannst natuerlich beide innere Funktionen zurueckgeben:

Code: Alles auswählen

def __pattern_dissambler(self, string):
        pattern = string[:self._DIGITS_OF_VENDOR_ID]
       
        def is_vendor_pattern():
                return pattern.isdigit()
       
        def get_vendor_name():
                return pattern
        return is_vendor_pattern, get_vendor_name

is_vendor_pattern, get_vendor_name = self.__pattern_dissambler(...)
Ich verstehe in dem Fall allerdings nicht so recht, warum man das haben will, statt das direkt auszuwerten und zurueckzugeben.
Steffo
User
Beiträge: 45
Registriert: Sonntag 24. Mai 2009, 19:38

cofi hat geschrieben:Ich verstehe in dem Fall allerdings nicht so recht, warum man das haben will, statt das direkt auszuwerten und zurueckzugeben.
Du meinst direkt den Namen zurück zu geben? Das will ich ja nur, wenn der String auch wirklich ein vendor pattern ist.
Aber danke, dass du mir die Möglichkeit aufgezeigt hast, wie ich das auf meine Art machen kann. :-)

L. G.
Steffo
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Und wenn du es bei deiner bisherigen Schreibweise assen möchtest, könnte man das ganze auch als Klasse in der Klasse definieren:

Code: Alles auswählen

class Test(object):

    def __init__(self):
        self.line = "Hallo"

    class Pattern_dissambler(object):
        def __init__(self, s):
            self.pattern = s[2:4]
       
        def is_vendor_pattern(self):
            return self.pattern.isdigit()
       
        def get_vendor_name(self):
            return self.pattern
               
    
    def test(self):
        dissambler = self.Pattern_dissambler(self.line)  
        if dissambler.is_vendor_pattern():
            vendor_name = dissambler.get_vendor_name()

t = Test()
t.test()
Steffo
User
Beiträge: 45
Registriert: Sonntag 24. Mai 2009, 19:38

@sparrow: Dann kommt wie schon geschildert das Problem von Fall 1 zum Vorschein: Da ich für jedes Pattern ein Objekt erstelle, kostet mich das bei tausenden Objektinstanziierungen, Performance.

L. G.
Steffo
BlackJack

@Steffo: Du solltest in Python auch Python programmieren und nicht Scala. Und das Argument mit der Performance ist Unsinn, denn auch in Scala wird da ja offensichtlich ein Objekt erstellt und zurück gegeben, denn es sind ja Attribute eines Objekts auf die Du zugreifen möchtest. Um dieses Objekt kannst Du also gar nicht herum kommen, wenn Du es so machen möchtest wie in Scala. Und wenn Du zwei Funktionen zurück gibst, dann werden diese *zwei* Funktionsobjekte bei jedem Aufruf erstellt. Es werden also doppelt so viele Objekte erzeugt als wenn Du selbst explizit ein Objekt von einer Klasse erstellst.

Falls Du Dir solche Gedanken um Leistung machst, bist Du bei Python vielleicht sowieso falsch. Es werden in Python ständig Unmengen an Objekten erstellt und verworfen. Jeder Funktionsaufruf erzeugt mindestens mal ein Frame-Objekt was danach wieder verworfen wird. Jeder Methodenaufruf erzeugt in CPython eine gebundene Methode, und zwar für jeden Aufruf ein neues Objekt. Alles in Python was man an einen Namen binden kann ist Objekt, selbst Zahlen.

Hast Du denn *messbar* ein Leistungsproblem? Wenn nicht, dann machst Du Dir unnötig Gedanken die am Ende dazu führen das Programm komplexer oder „unpythonischer” zu machen ohne tatsächlich ein Problem zu lösen. Im Gegenteil, könnte die neue Lösung genau so schnell oder langsam sein, wie eine „normale” und in einigen Fällen sogar langsamer, weil man falsche Annahmen getroffen hat. Wie zum Beispiel die Anzahl der erzeugten Objekte wie weiter oben dargelegt. Du erwähnst eine grosse Datei — da ist zum Beispiel immer die Frage ob die Datenübertragung von der Platte nicht sowieso die Laufzeit dominiert und damit sämtliche Mikrooptimierungen am Verarbeitungscode überhaupt nichts bringen.

Eine eigene Klasse mag umständlicher sein, aber das wäre der Weg den man in Python geht. „Objekte” mit mehreren Methoden per Closure zu erstellen ist nicht der Weg, den man in Python gehen würde. Dafür ist ``class`` vorgesehen.

Ad 2.: Den Punkt verstehe ich nicht. Wo kommt eine `set_pattern()`-Funktion ins Spiel?

Ad 3.: Wie können die voneinenader unabhängig sein, wenn sie sich `pattern` teilen? Die beiden Funktionen gehören doch offenbar semantisch zusammen‽

Die führenden doppelten Unterstriche bei `__pattern_dissambler()` würde ich durch einen einzigen ersetzen, es sei denn Du hast wirklich berechtigte Bedenken es könnte bei abgeleiteten Klassen zu Namenskollisionen kommen. *Dafür* ist das „name mangling” nämlich gedacht. Es ist *nicht* das Gegenstück zu ``private`` in anderen Programmiersprachen.
Steffo
User
Beiträge: 45
Registriert: Sonntag 24. Mai 2009, 19:38

OK, ich werde das im Hinterkopf behalten und das demnächst klassisch programmieren. :)
Antworten