Anfänger - Problem mit Funktion und Rückgabe

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
xoechz
User
Beiträge: 8
Registriert: Dienstag 6. Oktober 2020, 21:58

Hallo!

Ich bin neu hier, totaler Anfänger in Python, aber sehr motiviert. Im Moment taste ich mich gerade an die Verwendung von Funktionen heran und habe anscheinend ein Verständnisproblem. Ich bin mir sicher ihr könnt mir helfen.
Ich habe hier einen kleinen Code generiert.
Anfangs definiere ich eine Funktion für die eingabe des Namens, und Rückgabe der Eingabe in der Variable n.
Im Hauptteil starte ich die Funktion name und möchte anschließend den rückgegbenen Wert ausgeben.
Das funktioniert aber nicht. Ich bekomme immer die Fehlermeldung, daß die Variable n nicht definiert ist. Aber warum? Ich dachte genau das ist der Sinn der return Befehls, nämlich daß eine Variable zur weiteren Verwendung an den Hauptcode zurückgegebn wird. Wo liegt hier mein Denkfehler? Wie gesagt, bin Anfänger, also bitte nicht auslachen :-)

Code: Alles auswählen

#!/usr/bin/python3

def name():
	n=input("Name: ")
	return n

name()
print(n)
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst die Rückgabe auch an einen Namen binden. Und das kann auch ein ganz anderer sein als der Name in der Funktion.

Code: Alles auswählen

flitzpiepe = funktion()
print(flitzpiepe)
narpfel
User
Beiträge: 691
Registriert: Freitag 20. Oktober 2017, 16:10

In anderen Worten: `return` gibt nicht eine Variable an den Aufrufer zurück, sondern einen Wert. Und mit dem musst du was machen, sonst ist er weg.
xoechz
User
Beiträge: 8
Registriert: Dienstag 6. Oktober 2020, 21:58

Code: Alles auswählen

#!/usr/bin/python3

def name():
	n=input("Name: ")
	return n
	
deinname=name()
print(deinname)
Und so funktionierts. Danke !!!!!

Es wird also die in der Funktion bestimmte Variable n zurückgegeben, aber in der Variable gespeichert mit der ich die Funktion aufgerufen habe. In dem Fall (deinname). Cool!
Nochmals vielen Dank für die schnelle Hilfe!
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@xoechz: Nochmal: Da wird keine Variable zurückgegeben sondern ein Wert. Den muss man in der Funktion ja nicht einmal an irgendeinen Namen binden. Und auch ausserhalb nicht:

Code: Alles auswählen

def ask_for_name():
    return input("Name: ")


print(ask_for_name())
Sonstige Anmerkungen: Eingerückt wird mit vier Leerzeichen, nicht mit Tabs.

`name` ist ein schlechter Name für eine Funktion. Funktionen benennt man üblicherweise nach der Tätigkeit die sie durchführen um sie von eher passiven Werten unterscheiden zu können. `name` wäre beispielsweise ein passender Name für das Ergebnis der Funktion, wo Du dann `deinname` gewählt hast, weil `name` ja bereits durch die Funktion belegt war.

Abschauen/herleiten können hättest Du Dir das übrigens von der `input()`-Funktion. Da hattest Du es ja richtig gemacht. Das ist ja auch nur eine Funktion die irgendwo ein ``return`` drinnen stehen hat, wo dann der Rückgabwert an den Aufrufer zurück gegeben wird. Wenn es die Funktion nicht geben würde, könnte man etwas sehr ähnliches selbst schreiben:

Code: Alles auswählen

import sys


def input_(prompt=""):
    print(prompt, end="", flush=True)
    return sys.stdin.readline().rstrip("\n")
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
xoechz
User
Beiträge: 8
Registriert: Dienstag 6. Oktober 2020, 21:58

@__blackjack__:

Erstmal Danke für die Antwort!

Code: Alles auswählen

def ask_for_name():
    return input("Name: ")


print(ask_for_name())
Was du hier machst ist wohl die Kurzversion meines Codes, also EIngabe, Ausgabe, fertig. Aber wenn ich es richtig verstehe wird hier der Name nirgends gespeichert. Worum es mir ging, war eine Funktion zur Abfrage des Namens, und Rückgabe des Namens aus der Funktion zur weiteren Verwendung. Name war hier nur ein Beispiel, weil es halt einfach ist.
Mein Ziel ist es, einegebene Werte in Funktionen zu verarbeiten und die Ausgaben der Funktionen weiter zu verwenden.
Aber jeder fängt halt klein an :-)
Benutzeravatar
ThomasL
User
Beiträge: 1379
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Wenn du den in der Funktion eingegebenen Namen weiterverwenden willst, dann speichere ihn doch in eine Datenstruktur deiner Wahl.
Am einfachsten wäre eine Variable.

Code: Alles auswählen

name = ask_for_name()
print(name)
Du kannst den Namen aber auch an eine Liste anhängen, in ein Dictionary oder ein Set einfügen, etc.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
xoechz
User
Beiträge: 8
Registriert: Dienstag 6. Oktober 2020, 21:58

Hallo nochmal!
Ich hoffe ich gehe euch nicht auf die Nerven, aber ich habe noch ein kleines Beispiel geschrieben, nur um mit der Materie besser vertraut zu werden. Funktionieren tut alles wie gewünscht, aber vielleicht könnte einer von euch Profis einen schnellen Blick drauf werfen, ob der Code halbwegs vernünftig strukturiert und geschrieben ist. Ich will halt von Grund auf alles richtig lernen, damit sich nicht schon am Anfang Fehler in mein Denksystem einschleichen. Danke! :-)

Code: Alles auswählen

#!/usr/bin/python3

def eingabe():
    r=float(input("Radius: "))
    return(r)

def berechnung():
    u=2*r*3.14
    a=r*r*3.14
    print("Umfang: ",str(u))
    print("Fläche: ",str(a))

r=eingabe()
berechnung()
print("Ende.")
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@xoechz: Funktionen werden üblicherweise nach der Tätigkeit benannt die sie durchführen um sie von eher passiven Werten unterscheiden zu können. `eingabe` wäre ja beispielsweise ein passender Name für das Ergebnis einer Eingabe. Dafür kann man den Namen aber nicht mehr benutzen, weil die Funktion schon so heisst.

`berechnung()` wäre auch als `berechne()` inhaltlich nicht ganz zutreffend, denn die Funktion macht mehr als nur etwas zu berechnen. Die gibt auch etwas aus. Entweder man bennent die passender, oder man trennt die Berechnung von der Ausgabe. Eingabe, Verarbeitung, und Ausgabe sind gängige ”Trennlinien” zur Aufteilung von Code. Weil man dann alle drei Teilbereiche unabhängig voneinander testen kann und die auch leichter austauschbar werden. Wenn man beispielsweise aus einer Konsolen-Anwendung eine GUI-Anwendung machen will, dann ändert sich der Eingabe- und der Ausgabeteil, aber die Verarbeitung der Daten bleibt gleich.

``return`` ist keine Funktion, das sollte man also auch nicht so schreiben als wäre es eine. Da gehören keine Klammern um das ”Argument” was ja gar keines ist.

Man muss auch nicht alles an einen Namen binden.

Funktionen (und Methode) bekommen alles was sie ausser Konstanten benötigen als Argument(e) übergeben. Deine `berechnung()` verwendet einfach so aus heiterem Himmel `r`. Das sollte aber auch gar nicht gehen, weil Variablen auf Modulebene nichts zu suchen haben. Dann können nämlich unter anderem genau solche undurchsichtigen und fehleranfälligen Sachen passieren.

Namen sollten nicht kryptisch abgekürzt werden. Schon gar nicht mit nur einem Buchstaben. Wenn man `radius` meint, sollte man nicht nur `r` schreiben.

`print()` wandelt die Argumente bereits selbst in Zeichenketten um, das muss man nicht vorher selbst machen.

Anstelle der 3.14 würde man besser `math.pi` verwenden.

Code: Alles auswählen

#!/usr/bin/env python3
from math import pi as PI


def erfrage_radius():
    return float(input("Radius: "))


def berechnen_und_ausgeben(radius):
    print("Umfang:", 2 * radius * PI)
    print("Fläche:", radius * radius * PI)


def main():
    berechnen_und_ausgeben(erfrage_radius())
    print("Ende.")


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
xoechz
User
Beiträge: 8
Registriert: Dienstag 6. Oktober 2020, 21:58

Super! Vielen Dank für die ausführliche Antwort.

Nur eines ist mir noch unklar:
Der Funktion berechnen_und_ausgeben übergibst du den Wert radius mit dem dann Umfang und Fläche berechnet werden. Nur wurde dieser vorher nirgends definiert. Woher weiss also Python, dass der Wert radius aus der Funktion erfrage_radius stammt?
Ich dachte genau dafür braucht man den Return Befehl, damit aus einer Funktion ein Wert zurück gegeben wird.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

die Funktion berechnen_und_ausgeben() bekommt ja die Funktion erfrage_radius() als Paramter übergeben und wird durch die () ausgeführt.
Ist also das gleiche wie:

Code: Alles auswählen

from math import pi as PI

def erfrage_radius():
    return float(input("Radius: "))


def berechnen_und_ausgeben(radius):
    print("Umfang:", 2 * radius * PI)
    print("Fläche:", radius * radius * PI)


def main():
    radius = erfrage_radius()
    berechnen_und_ausgeben(radius)
    print("Ende.")
    
if __name__ == "__main__":
    main()
Nur dass sich die eine Variable (radius in der main()) gespart wurde, da der Radius ja eigentlich nicht weiter im Programm benötigt wird.


Du kannst alles an eine Varaible binden, musst du aber manchmal gar nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@xoechz: Den Radius gibt die Funktion `erfrage_radius()` doch als Rückgabewert zurück. Und der Aufruf dieser Funktion steht an der Stelle wo beim Aufruf von `berechnen_und_ausgeben()` der Radius erwartet wird. Und damit ist in `berechnen_und_ausgeben()` dann der Name `radius` als dieser Wert definiert. Und `berechnen_und_ausgeben()` beziehungsweise Funktionen allgemein ist das auch vollkommen egal wie die Werte die übergeben mal zustande gekommen sind, welche Namen die beim Aufrufer hatten, oder noch früher in anderen Funktion, oder ob die überhaupt mal an einen Namen gebunden waren.

Das ist das Ersetzungsprinzip wie man das aus der Mathematik kennt. Die Anwendung einer Funktion steht für das Ergebnis. Statt `x = f(42)` und `y = g(x)` kann man das `x` auch einsetzen und `y = g(f(42))` schreiben und sich damit den Namen `x` sparen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
xoechz
User
Beiträge: 8
Registriert: Dienstag 6. Oktober 2020, 21:58

Super, jetzt hab ich es verstanden!

Danke nochmal an alle Beteiligten für die kompetente Hilfe!!!
xoechz
User
Beiträge: 8
Registriert: Dienstag 6. Oktober 2020, 21:58

So, einmal melde ich mich noch zu diesem Thema.

Habe mich etwas in das Thema OOP, Klassen, Objekte und Methoden eingelesen und versucht meinen kleinen Kreisberechnungscode in eine objektorientierte Schreibweise zu bringen. Funktionieren tut es, aber -falls es jemanden freut - bitte nochmal drüber schauen, ob man das so stehen lassen kann, oder ob ich etwas ungünstig oder schlecht formuliert habe. Das wäre sehr nett.
Danke!

Code: Alles auswählen

#!/usr/bin/python3

from math import pi as PI

class Kreis():

    def __init__(self,radius):
        self.radius=radius

    def umfang(self):
        print("Umfang: ",self.radius*2*PI)

    def flaeche(self):
        print("Fläche: ",self.radius**2*PI)

def eingabe_radius():
    return float(input("Radius: "))

def main():
    meinkreis=Kreis(eingabe_radius())
    meinkreis.umfang()
    meinkreis.flaeche()
    print("Ende.")

if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@xoechz: Wieder die Namen: `umfang` und `flaeche` klingen nach Werten und nicht nach Methoden die etwas tun. Und die Ausgabe gehört auch eher nicht in so eine Klasse. Wieder die Frage was denn passieren soll wenn das nicht auf der Konsole ausgegeben werden soll, oder wenn man mal mehrere Kreise erstellen und die Gesamtfläche von allen Kreisen addieren möchte. Das geht nicht ohne den Code für die Flächenberechnung noch mal zu schreiben, weil der Vorhandene fest mit der Ausgabe via `print()` verdrahtet ist.

Die Namen könnte man beibehalten wenn man Properties daraus macht, also berechnete Attribute.

Die Klammern nach dem Klassennamen kann man weglassen. Und das `mein` bei `meinkreis`. Der Zusatz enthält hier keine sinnvolle Information.

Am Namen `eingabe_radius()` hätte ich auch etwas auszusetzen, denn diese Funktion gibt nichts ein. Die fragt den Benutzer. Der gibt dann was ein.

Die Funktion könnte auch robuster sein, also beispielsweise die Frage solange wiederholen bis der Benutzer dann auch tatsächlich eine Zahl eingibt.

Code: Alles auswählen

#!/usr/bin/python3
from math import pi as PI


class Kreis:
    def __init__(self, radius):
        self.radius = radius

    @property
    def umfang(self):
        return self.radius * 2 * PI

    @property
    def flaeche(self):
        return self.radius ** 2 * PI


def erfrage_radius():
    while True:
        try:
            return float(input("Radius: "))
        except ValueError:
            print("Bitte eine Zahl eingeben!")


def main():
    kreis = Kreis(erfrage_radius())
    print("Umfang:", kreis.umfang)
    print("Fläche:", kreis.flaeche)
    print("Ende.")


if __name__ == "__main__":
    main()
Nun bricht das Programm schon mal nicht mehr ab wenn der Benutzer absichtlich oder unabsichtlich etwas eingibt, was nicht als Zahl angesehen werden kann, aber er kann immer noch negative Werte für den Radius eingeben.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Auch Methoden sollten nach Tätigkeiten benannt werden, vor allem weil bei umfang nicht klar ist, was damit passieren soll.

Code: Alles auswählen

class Kreis():
    def __init__(self, radius):
        self.radius = radius

    def gebe_umfang_aus(self):
        print("Umfang:", self.radius * 2 * PI)

    def gebe_flaeche_aus(self):
        print("Fläche:", self.radius ** 2 * PI)
Wenn es keinen `unserkreis` gibt, ist das `mein´ bei `meinkreis´ unsinnig und kann weg. Es fehlen viele Leerzeichen.
Antworten