Seite 1 von 1

Problem mit Funktionsparametern bzw. Filehandling in FKT

Verfasst: Mittwoch 10. Mai 2006, 09:05
von keboo
Hallo Leute!

Mein Skript funktioniert soweit einwandfrei. Hab mir aber sagen lassen, dass es sinnvoll ist, größere Skripts in Funktionen oder sogar Klassen zu unterteilen. Nun hab ich damit begonnen, den Code zu unterteilen in Funktionen.

Code: Alles auswählen

def FORMULA_INPUT():
    
    in_data = False # boolsche Variable 
        
    for line in file(input): # Kann es sein, dass das nicht funktioniert?
            
        if ("< FORMULAS_ >") in line:
            in_data = True
            line = line[line.find("< FORMULAS_ >" )+13:]
              
        if ("<FORMULAS_>") in line:
            in_data = True
            line = line[line.find("<FORMULAS_>" )+13:]
              
        if in_data:
            str_list1.append(line)

        if "<_FORMULAS>" in line:
            break 
            
        if "< _FORMULAS >" in line:
            break

        print str_list1
        
    return str_list1

for input in glob.glob(SOURCE_PATH):
    FORMULA_INPUT()

Kann man Listenvariablen "returnen"?
Bzw. kann es sein, dass ich in die Funktion den Ausdruck:

Code: Alles auswählen

for line in file(input):
nicht verwenden darf?

Danke für eure Hilfe,
Johannes

Re: Problem mit Funktionsparametern bzw. Filehandling in FKT

Verfasst: Mittwoch 10. Mai 2006, 10:08
von gerold
keboo hat geschrieben:Nun hab ich damit begonnen, den Code zu unterteilen in Funktionen.
Hi Johannes!

Es hat sich bewährt, Funktionen (wenn möglich) so zu programmieren, dass sie eigenständig lauffähig sind. Also, so weit wie möglich, unabhängig von außenstehenden Variablen sind.

Das erleichtert den Programmaufbau, da man Funktion für Funktion einzeln testen kann. Weiters hat man dadurch einzelne, abgeschlossene Bereiche die ihre eigene Logik besitzen und durchschaubar bleiben.

Wenn du in einer Funktion aber davon ausgehen musst, dass eine bestimmte Variable, mit einem bestimmten Namen, irgenwo bereits existiert bevor du die Funktion erfolgreich aufrufen kannst, dann geht diese Abtrennung der Logik 'flöten' und dein Programm wird zunehmend komplizierter.

Besser -- mehrere kleine, überschaubare Teile als ein großes Teil, das in sich verwoben ist.

Deshalb hier meine Tipps für die einfache Verwendung von Funktionen:

- Übergebe **alle** benötigten Variablen als Parameter an die Funktion.

- Verändere bzw. verwende **keine globale Variablen**, da dadurch das Verhalten der Funktion nach außen hin komplizierter wird.

- Gib veränderte Werte/Variablen mit der Anweisung "return" zurück.

- Wenn du mehrere Werte zurück geben möchtest, dann kannst du diese ebenfalls mit **return** zurück geben.

Code: Alles auswählen

def meine_funktion(parameter1, parameter2):
    ...
    return (wert1, wert2, wert3) 

(ret1, ret2, ret3) = meine_funktion(argument1, argument2)

print ret1
print ret2
print ret3
Hier ein Beispiel:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-


def multipliziere(wert, multi):
    """
    Multipliziert den Parameter "wert" mit "mult".
    
    wert
        Fließkommazahl, die mit dem Multiplikator multipliziert wird.
    multi
        Fließkommazahl, mit der der Wert multipliziert wird.
        
    Rückgabe: Fließkommazahl
    """
    
    # Gehe niemals davon aus, dass du auch wirklich den richtigen
    # Type übergeben bekommst.
    wert = float(wert)
    multi = float(multi)
    
    # Berechnen
    ergebnis = wert * multi
    
    # Ergebnis zurück geben
    return ergebnis

# Wie man sieht, ist die Funktion ein in sich abgeschlossenes Teil.
# Sie erwartet sich keine spezielle Variable und verändert nichts.


#----------------------------------------------------------------------
# Beispiel 1
#----------------------------------------------------------------------
for i in range(1, 11):
    # Dieser Aufruf der Funktion, übergibt Werte und zeigt das Ergebnis
    # an. Dabei wird von der Funktion selbst, **nichts** verändert.
    print multipliziere(i, i)



#----------------------------------------------------------------------
# Beispiel 2
#----------------------------------------------------------------------
print
print

ii = 1
for i in range(1, 11):
    # Diese Anweisung verändert die Variable `ii`. Der Vorteil dieser
    # Schreibweise ist, dass **jeder** sofort mitbekommt, dass die Variable
    # `ii` verändert wird. Die Funktion `multipliziere` bleibt weiterhin
    # in sich abgeschlossen. Es wird nichts von der Funktion selbständig
    # verändert. Allein die Form des Aufrufes entscheidet (sichtbar), dass
    # jetzt ein Wert verändert wird. Das schafft Transparenz und erleichtert
    # das Lesen des Codes ungemein.
    ii = multipliziere(i, ii)
    print ii
lg
Gerold
:-)

Verfasst: Mittwoch 10. Mai 2006, 10:17
von gerold
Und hier ein Beispiel, das vielleicht ein paar andere Fragen von dir beantworten sollte.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import os.path

def formula_input(filename):
    """
    Liest die Datei aus und gibt irgendetwas zurück
    
    filename
        Dateiname und Pfad zur auszulesenden Datei
    """
    
    # Prüfen ob Datei existiert
    if not os.path.isfile(filename):
        return "DIE DATEI '%s' EXISTIERT NICHT!" % filename
    
    # Datei öffnen
    f = file(filename, "rU") # Lesen mit Standardzeienumbrüchen
    
    # Variable zum Sammeln der Zeilen
    linelist = []
    
    # Jede Zeile durchlaufen und korrekte Zeilen in Liste sammeln
    for line in f:
        if "irgendetwas" in line:
            linellist.append(line)
            
    # Datei schließen
    f.close()
    
    # Liste zurück geben
    return linelist
    
    
# Aufruf
for dateiname in glob.glob(SOURCE_PATH):
    print formula_input(dateiname)
lg
Gerold
:-)

Verfasst: Mittwoch 10. Mai 2006, 11:02
von keboo
Hi Gerold!

Danke für deine Antworten!
Ich werde das jetzt mal studieren :)

Ich denke, dass ist ein wichtiger Schritt beim weiteren Vertiefen von Python, dass ich lerne mit Funktionen umzugehen.

LG
johannes

Verfasst: Mittwoch 10. Mai 2006, 13:09
von keboo
Hallo!

Noch eine Frage bzgl. Funktionshandling:

Code: Alles auswählen

for dateiname in glob.glob( SOURCEPATH ):
    Coeff_Filter( formula_input(dateiname) )
Die Funktionen, in meinem Programm sind nacheinander von einander abhängig.

In dem Fall benötigt Coeff_Fiter als Inputparameter die Liste, welche von Formula_input() generiert wurde.

Coeff_filer selbst gibt 3 floats und 1 string zurück. Kann es in so einem Fall nicht sinnvoll sein diese 4 Rückgabeparameter ausnahmsweise als GLOBALS zu definieren?

So sieht der Code aus

Code: Alles auswählen

def Coeff_Filter(str_list):
    """ 
    Eingangsparamter:
    Liest Liste aus formula_input() ein und filtert Coeffizienten und Formeln
    raus

    Rückgabeparametet:
    Coeffizienten: a,b,c
    Formeln (string): formula
    """

    # Sicherstellen, dass Inputparameter Liste ist
    str_list1=list(str_list1)
    
    a=0 # Initialisierung von a
    b=0 # Initialisierung von b
    c=0 # Initialisierung von c
        
    for i in range(len(str_list1)):
        if str_list1[i].find('a = ') is not -1:
            a=str_list1[i][str_list1[i].find('a = ')+4:str_list1[i].find('a = ')+18]
            a=float(a)
        if str_list1[i].find('b = ') is not -1:
            b=str_list1[i][str_list1[i].find('b = ')+4:str_list1[i].find('b = ')+18]
            b=float(b)
        if str_list1[i].find('c = ') is not -1:
            c=str_list1[i][str_list1[i].find('c = ')+4:str_list1[i].find('c = ')+18]
            c=float(c)

        # Rausfiltern der angegebenen Formel
        if str_list1[i].find('mass1 = ') is not -1:
            formula=str_list1[i][str_list1[i].find('mass1 = '):str_list1[i].find(']')]
            formula=formula.strip()

    return a,b,c,formula
Mehrere weitere Funktionen benötigen diese Rückgabeparameter.

Danke für Eure Hilfe,

Johannes

Verfasst: Mittwoch 10. Mai 2006, 13:36
von Buell
Kenn jatzt zwar den kompletten "Ablauf" deines Programmes nicht, aber eventuell sollte es in dem Fall helfen die Funktionen rekursiv zu verwenden. Das heißt, dass eine Funktion/Methode die andere aufruft.

Wenn du wie in deinem Beispiel:

Code: Alles auswählen

for dateiname in glob.glob( SOURCEPATH ):
    Coeff_Filter( formula_input(dateiname) )
eh jedes mal formula_input aufrust, sollte es doch Sinn machen das Ergebnis dieser Funktion nicht als Übergabewert für Coeff_Filter zu nutzen, sondern diese einfach aus Coeff_Filter heraus am Anfang aufzurufen. Globale Variablen, auch wenn ich selbst sie der Einfachheit halber auch ab und an als Beispiel hernehme, sollten möglichst vermieden werden. Du musst an Sie immer denken beim weiteren Programmieren und bist du dir sicher immer alle Methoden im Kopf zu haben die eine global ändern?

Verfasst: Mittwoch 10. Mai 2006, 13:42
von gerold
keboo hat geschrieben:Kann es in so einem Fall nicht sinnvoll sein diese 4 Rückgabeparameter ausnahmsweise als GLOBALS zu definieren?
Hi Johannes!

Versuche es. Stell dich einfach mal darauf ein und verwende "globals" nur dann, wenn du keinen anderen Ausweg findest. Du wirst sehen, dass dein Code automatisch lesbarer wird.

In deinem Fall, würde ich sogar noch eine zusätzliche Zeile spendieren, damit man als Programmierer, ohne "formula_input" zu kennen, sofort erkennt, was "formula_input" zurück liefert.

Code: Alles auswählen

for dateiname in glob.glob(SOURCEPATH):
    filtered_list = formula_input(dateiname)
    print coeff_filter(filtered_list)
Dabei erübrigt sich, durch den aussagekräftigen Variablenamen, ein zusätzlicher Kommentar.

lg
Gerold
:-)

PS: import this

Verfasst: Mittwoch 10. Mai 2006, 13:46
von gerold
keboo hat geschrieben:

Code: Alles auswählen

return a,b,c,formula
Mehrere weitere Funktionen benötigen diese Rückgabeparameter.
Hi Johannes!

Wenn mehrere Funktionen das Ergebnis dieser Funktion benötigen, dann würde ich das Ergebnis einfach irgendwo zwischenspeichern.

Code: Alles auswählen

a, b, c, formula = irgendeine_funktion(ein_argument)

print funktion2(a, b, c)
print funktion3(c, formula)
...
lg
Gerold
:-)

Verfasst: Mittwoch 10. Mai 2006, 14:07
von keboo
Danke Buell für den Tipp!

Mein Skript besteht aus vielen kleinen Teilskript, die den Inhalt einer Datei rauslesen, Werte an verschiedenen Stellen verändern, die alten Werte löscht und dann das neue File an einer anderen Stelle speichert.
Das Skript ist schon zu groß um es an dieser Stelle sinnvoll zu posten.

Die Daten werden dabei immer weitergegeben. Es sind zwar relativ viele Bereiche für mich zu identifizieren gewesen, aber weil dieses eben immer nacheinander von einander abhängig sind, weiß ich nicht, ob es Sinn macht viele kl. Funktionen zu machen.

Denk mal, es ist am sinnvollsten die "Hauptbereiche": Einlese, Datenmanipulation und Auslese als Funktionen zu definieren.

Kann es Sinn machen Klassen zu verwenden für die Hauptbereiche?

Danke.

Johannes

Verfasst: Mittwoch 10. Mai 2006, 14:21
von Buell
Hallo,

wenn es zu groß wird macht es natürlich Sinn Klassen zu verwenden, allerdings sollten diese dann auch absolut eigentsändig funktionieren. Klassen machen vor allem Sinn, wenn du später leicht etwas verändern willst. Für grafische Anwendungen kommt zB die Oberfläche bei mir immer in eine separate Klasse und alle Berechnungen & Co in eine oder mehrere andere. Beschäftige dich mal ein bißchen mit OOP (objektorientierte Programmierung) und du erkennst den Sinn ziemlich schnell. Abgesehen davon, kannst du Klassen auch direkt von Anfang an mit bestimmetn Eigenschaften versehen (sie erben dann von anderen, hoffe das die Aussage so richtig ist). Das wird dir vor allem auffallen solltest du dich irgendwann mal mit wx beschäftigen. Den Dateizugriff in eine eigene Klasse zu packen halte ich für sinnvoll, zumindest wenn dort noch mehr passieren soll als das schnöde ein- und auslesen.

Verfasst: Mittwoch 10. Mai 2006, 14:39
von gerold
Hi!

Es gibt schon eine Möglichkeit, **indirekt** "globale" Variablen zu verwenden. Als Klasseninstanzattribute. Denn da sind die Variablen direkt an eine Klasseninstanz gebunden. Dadurch ist die Zugehörigkeit der Variable eindeutig geklärt. Eine Klasse kann man (unter anderem) ja auch als Datenkontainer, mit Methoden zum Zuweisen/Verändern/Auslesen dieser Daten, ansehen.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

class Zigarettenschachtel(object):
    
    def __init__(self, marke, inhalt = 40):
        self.marke = marke
        self.inhalt = inhalt

    def get_menge(self):
        return self.inhalt
    
    def get_zigarette(self):
        self.inhalt -= 1
    
schachteln = [
    Zigarettenschachtel("Marlboro", 40),
    Zigarettenschachtel("Dames", 37)
]

for schachtel in schachteln:
    for i in range(3):
        schachtel.get_zigarette()
    
for schachtel in schachteln:
    print "%s: %s" % (schachtel.marke, schachtel.get_menge())
lg
Gerold
:-)

Edit: Ich hätte wohl eine halbe Stunde früher auf [Absenden] klicken sollen. :D

Verfasst: Mittwoch 10. Mai 2006, 14:44
von keboo
Hey Leute!

Danke für eure Tipps.

Ich muss das jetzt mal setzen lassen. Ist ja nicht ganz leicht verdaulich das ganze :)

Danke nochmals für die detaillierten Antworten.

Lg
Johannes