Funktionen

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.
nfehren
User
Beiträge: 98
Registriert: Donnerstag 31. Oktober 2013, 15:11

So, bin jetzt dabei Funktionen zu lernen. Ich habe ein einfaches Programm geschrieben, dass einen String abfragt und diesen dann mit mehreren Sternchen umrahmt. Ist die Schreibweise gut? Wenn nicht bitte ich um Verbesserungsvorschläge :)

Code: Alles auswählen

text = input("""Willkommen zum "Rahmenprogramm"! Geben sie einen Text ein und wir umrahmen ihn.\n""")
space = ("")
stern = "*"
def rahmen():
    laenge = len(text)
    stern_laenglich = laenge + 6
    for i in range(stern_laenglich):
        print("*", end="")
rahmen()
print("")
print(stern,space,text,space,stern)
rahmen()

print("\nDanke für die Benutzung unseres Programms!")
BlackJack

@nfehren: Funktionen sollte keine Werte einfach so benutze was nicht als Argument übergeben wurden, ausser wenn es Konstanten sind. In diesem Fall betrifft das `text` und `stern`. Es muss, damit die Funktion funktioniert auf Modulebene etwas mit den Namen `text` und `stern` geben. Das sollte es auf Modulebene aber gar nicht geben, es sei denn es ist eine Konstante, Funktion, oder Klasse. Falls es als Konstante gemeint war, dann würde der Name eigentlich komplett in Grossbuchstaben geschrieben. Siehe: Style Guide for Python Code

Funktionsnamen sollten eine Tätigkeit beschreiben, denn sie tun etwas. Auch um sie von anderen ”passiven” Werten besser unterscheiden zu können. `rahmen` beschreibt eher ein ”Ding”, also ein Objekt oder eine Datenstruktur die ein ”Ding” beschreiben/repräsentieren.

Auch `zeichne_rahmen()` oder `drucke_rahmen()` wäre inhaltlich nicht richtig, denn die Funktion gibt nur eine Trennlinie aus, also den oberen oder unteren Teil eines Rahmens.

Da der Text mit der Funktion aber eigentlich nicht wirklich etwas zu tun hat, denn man möchte vielleicht auch Rahmen ohne Text zeichnen, oder einen der unabhängig von der Länge des Textes ist, zum Beispiel einen der über die gesamte Breite eines Terminals geht, sollte man nicht den Text sondern die gewünschte Länge übergeben. Dann bleibt allerdings eine Funktion übrig die vielleicht die Funktion nicht wert ist, weil man das auch direkt in den Quelltext schreiben könnte:

Code: Alles auswählen

def drucke_linie(laenge, zeichen='*'):
    print(zeichen * laenge)
Eine Funktion die *tatsächlich* einen gegebenen Text mit einem Rahmen versehen ausgibt, wäre vielleicht sinnvoller.

Aber als erstes solltest Du aufhören den Rest des Programms direkt auf Modulebene zu schreiben. Wenn man konsequent das Hauptprogramm in eine `main()`-Funktion schreibt und mit dem ``if __name__ == '__main__':``-Idiom aufruft, fällt es leichter sauber zu programmieren, weil man nicht mehr aus versehen, oder gar absichtlich, Werte in Funktionen benutzen kann, die nicht als Argument übergeben wurden. Also als Grundgerüst am besten immer hiermit anfangen:

Code: Alles auswählen

def main():
    pass # Hier kommt das Hauptprogramm hinein.


if __name__ == '__main__':
    main()
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

BlackJack hat schon alles Wesentliche zur Grundstruktur gesagt. Ich möchte daher nur noch eine Alternative für ein kleines Teilstück deines Codes aufzeigen.
nfehren hat geschrieben:

Code: Alles auswählen

print(stern,space,text,space,stern)
An dieser Stelle würde ich eine sinnvolle Stringformatierung verwenden.

Code: Alles auswählen

>>> print('* {0} *'.format('Python'))
* Python *
Das kann man bei Bedarf noch weiter flexibilisieren.

Code: Alles auswählen

>>> print('{1} {0} {1}'.format('Python', '='))
= Python =
nfehren
User
Beiträge: 98
Registriert: Donnerstag 31. Oktober 2013, 15:11

Ok vielen Dank für eure Mühe ich fang jetzt an zu verbessern :)
nfehren
User
Beiträge: 98
Registriert: Donnerstag 31. Oktober 2013, 15:11

@BlackJack willst du mir das mit dem

Code: Alles auswählen

if __name__ == '__main__':
    main()
mal erklären? Ich kann das nicht nachvollziehen was das bewirken soll.
BlackJack

Das sorgt dafür das `main()` aufgerufen wird wenn man das Modul direkt ausführt, aber *nicht* ausgeführt wird wenn man das Modul mit ``import`` importiert. Dann hat `__name__` nämlich den Namen des Moduls.
nfehren
User
Beiträge: 98
Registriert: Donnerstag 31. Oktober 2013, 15:11

Achso OK. Wenn wir schon mal bei Funktionen sind: Kann man eine try anweisung in eine Funktion packen, sodass man nur noch als parameter eine variable angeben muss und diese dann per input in der funktion geholt und geprüft wird?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

nfehren hat geschrieben:Kann man eine try anweisung in eine Funktion packen, sodass man nur noch als parameter eine variable angeben muss und diese dann per input in der funktion geholt und geprüft wird?
Das verstehe ich nicht - bitte erkläre das mal ausführlicher!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

nfehren hat geschrieben:Achso OK. Wenn wir schon mal bei Funktionen sind: Kann man eine try anweisung in eine Funktion packen, sodass man nur noch als parameter eine variable angeben muss und diese dann per input in der funktion geholt und geprüft wird?
Meinst du so etwas?

Code: Alles auswählen

def get_integer(message):
    while True:
        try:
            return int(input(message))
        except ValueError:
            pass

value = get_integer('Bitte eine Zahl eingeben: ')
nfehren
User
Beiträge: 98
Registriert: Donnerstag 31. Oktober 2013, 15:11

/me hat geschrieben:
nfehren hat geschrieben:Achso OK. Wenn wir schon mal bei Funktionen sind: Kann man eine try anweisung in eine Funktion packen, sodass man nur noch als parameter eine variable angeben muss und diese dann per input in der funktion geholt und geprüft wird?
Meinst du so etwas?

Code: Alles auswählen

def get_integer(message):
    while True:
        try:
            return int(input(message))
        except ValueError:
            pass

value = get_integer('Bitte eine Zahl eingeben: ')
Genau sowas!
template
User
Beiträge: 29
Registriert: Mittwoch 21. November 2007, 09:44

Hier wäre ne Lösung für Python 2

Code: Alles auswählen

text = raw_input("""Willkommen zum "Rahmenprogramm"! Geben sie einen Text ein und wir umrahmen ihn:\n""")
border = raw_input("""Nun noch den zu verwendenden Rahmen:\n""")
textWithBorder = '%s %s %s' % (border, text, border)
splitLine = (len(textWithBorder) * border)[:len(textWithBorder)]
print splitLine
print textWithBorder
print splitLine
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

template hat geschrieben:Hier wäre ne Lösung für Python 2
Streng genommen ist das *keine* Lösung für das Problem, weil das Thema sich um *Funktion* und deren Einsatz drehte. Du hast hier keine Funktion präsentiert ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
jqz4n
User
Beiträge: 21
Registriert: Sonntag 2. Februar 2014, 19:26

Jetzt wäre es aber eine Lösung (Wobei es bedutend besere gäbe ;) ):

Code: Alles auswählen

def main(text = raw_input("""Willkommen zum "Rahmenprogramm"! Geben sie einen Text ein und wir umrahmen ihn:\n"""), border = raw_input("""Nun noch den zu verwendenden Rahmen:\n""")):
    textWithBorder = '%s %s %s' % (border, text, border)
    splitLine = (len(textWithBorder) * border)[:len(textWithBorder)]
    print splitLine
    print textWithBorder
    print splitLine

if __name__ == "__main__":
    main()
BlackJack

@jqz4n: Würde ich auch nicht als Lösung ansehen weil das mit den Default-Werten kompletter Unsinn ist. Der OP möchte ja lernen wie man Funktionen richtig verwenden.
jqz4n
User
Beiträge: 21
Registriert: Sonntag 2. Februar 2014, 19:26

Aber es verwendet Funktionen und setzt sie ein. Zwar nicht korrekt, aber immerhin. Somit ist es nach Aussage von Hyperion eine Lösung :P

Nein, natürlich hast du recht. Der von mir erstellte Code ist kein Beispiel für den richtigen Einsatz von Funktionen. Bitte also nicht produktiv verwenden! :!: :!: :!:
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Darüber hinaus (geht noch etwas über "kompletter Unsinn"?) werden die default-Argumente nur ein einziges mal ausgewertet und die Funktion tut mit Sicherheit nicht das, wa Du (@jqz4n) erwartest ;)
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
jqz4n
User
Beiträge: 21
Registriert: Sonntag 2. Februar 2014, 19:26

Doch. Ich hab's ausprobiert. Und wenn sie nicht tut, was ich erwarte, ist Python, der Computer und die Welt schuld, aber nicht ich! :P :mrgreen:

Bevor man noch weiter auf mir rumhackt hier eine vernünftige Funktionsgruppe, die einen (auch mehrzeiligen) Text einrahmt (Auch hier wären noch eingie Dinge zu verbessern (Überprüfungen der Eingabe u.v.m.), aber diese Version würde ich sogar getrauen, produktiv zu verwenden). Funktioniert nur unter python2:

Code: Alles auswählen

#!/usr/bin/python
#-*-coding: utf-8 -*-
def bestimme_laenge(text):
    """
    bestimmt die Breite der längsten Textzeile
    """
    return len(max(text.split('\n')))

def zeichne_horizontale_linie(rahmen, laenge):
    """
    gibt eine horizontale Linie zurück, die aus dem Zeichen "rahmen" besteht und eine Länge von "laenge" hat
    """
    return rahmen*laenge
    
def zeichne_seitlichen_rahmen(rahmen, inhalt, laenge):
    """
    gibt den Text "inhalt", versehen zu beiden Seiten mit je einer vertikalen Linie aus dem Zeichen "rahmen" zurück
    "laenge" sollte mindestens der Länge der längsten Textzeile entsprechen.
    """
    for zeile in inhalt:
        return '{0}{1}{0}'.format(rahmen,zeile.center(laenge))

def zeichne_rahmen(inhalt, rahmen):
    """
    umgibt den Text "inhalt" mit einem Rahmen, der aus dem Zeichen "rahmen" besteht und gibt das Ergebnis zurück
    """
    laenge = bestimme_laenge(inhalt)
    linie = zeichne_horizontale_linie(rahmen, laenge+2)
    rahmentext = [
        linie,
        zeichne_seitlichen_rahmen(rahmen,inhalt,laenge),
        linie
    ]
    return '\n'.join(rahmentext)

# eine Möglichkeit des Aufrufs, erhält die Inhalte aus der Benutzereingabe
def main(begruessung, menu_inhalttext, menu_rahmentext):
    print begruessung
    inhalt = raw_input(menu_inhalttext)
    rahmen = raw_input(menu_rahmentext)
    return zeichne_rahmen(inhalt, rahmen)

if __name__ == '__main__':
    print main(
        'Willkommen zum "Rahmenprogramm"!',
        'Geben sie einen Text ein und wir umrahmen ihn: ',
        'Nun noch den zu verwendenden Rahmen:'
        )
BlackJack

@jqz4n: Nun ja:

Code: Alles auswählen

In [6]: bestimme_laenge('aaa\nz')
Out[6]: 1
Da müsste doch eigentlich 3 heraus kommen, oder?
jqz4n
User
Beiträge: 21
Registriert: Sonntag 2. Februar 2014, 19:26

Hab ich auch grade bemerkt. (Und weiß, worin der Fehler liegt¹. ;) ) Man ersetze also die entsprechende Funktion durch z.B.

Code: Alles auswählen

def bestimme_laenge(text):
    return max(map(len,text.split('\n')))

______
¹Sortierungs- und Vergleichsfunktionen wählen i.d.R. die alphabetisch letzte Zeichenfolge aus, nie die längste.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@jqz4n: ich finde auch, dass mehr als ein Buchstabe in einem Rahmen kein Mensch braucht.
Antworten