Seite 1 von 1

Die Ausführung ist etwas unschön (finde ich) geht das noch schöner bzw. komfortabler?

Verfasst: Mittwoch 9. September 2020, 21:33
von Trev
Habe vor ein paar Tagen angefangen Python zu Programmieren (Erstsprache). Die Eingabe ist etwas unkomfortabel doch seht selbst.
Das Programm soll int-Werte in einer Liste speichern und diese aufsummieren, aber irgendwie geht es nur mit der Funktion die einen Bool zurückgibt,
zumindest ist mir nur diese Lösung eingefallen

Code: Alles auswählen

""" The program integrated int-Values in a list and calculate the sum of them"""


def test():

    make = ""
    # Asks for (more) inputs
    while make != "n" and make != "y":
        make = (input("Do you want to make some input(y = yes | n = no)?: "))
        str(make)

    # Return a boolean for the main-function 
    if make == "n":
        return False

    else:
        return True


def make_input():

    # Requests inputs
    new_input = int(input("Please enter an integer: "))

    if new_input == 0:
        return 0

    else:
        return new_input


def average_result(usable_list):

    addition = 0

    # Calculate the sum of all values in the list with a for-loop
    for i in range(0, len(usable_list)):

        addition += usable_list[i]

    print("The average is", addition / len(usable_list))


if __name__ == "__main__":

    created_list = []

    """ While test is True the int_values should integrated in a list"""
    while test():

        created_list.append(make_input())

    average_result(created_list)
Geht das noch einfacher bzw. schöner?
Die Lösung mit dem Bool wirkt echt unschön.
Danke im Vorraus für die Antworten.

Re: Die Ausführung ist etwas unschön (finde ich) geht das noch schöner bzw. komfortabler?

Verfasst: Mittwoch 9. September 2020, 22:27
von Sirius3
Die vielen Leerzeilen machen den Code schwer lesbar. Nach dem Funktions-def kommt keine Leerzeile, weil dadurch der Funktionsblock vom Header optisch getrennt wird.
Ähnliches gilt für die while- und for-Blöcke.
In `test` wird make an einen Dummywert gebunden, nur damit die while-Schleife startet. Statt dessen benutzt man eine while-True-Schleife und verläßt die in einem if-Block per break.
Der str-Aufruf ist unsinnig. Input hat zu viele Klammern.
Statt dem `if make == 'n'` kann man bei return auch gleich den Wahrheitswert als Parameter übergeben.
Die if-Abfrage in make_input ist ziemlich unsinnig, denn bei new_input == 0 liefert `return new_input` auch 0.
In `average_result hast Du eine for-Schleife über einen Index; das macht man nicht, da man auch direkt über die Elemente iterieren könnte.

Code: Alles auswählen

def average_result(values):
    addition = 0
    for value in values:
        addition += value
    print("The average is", addition / len(values))
Dafür gibt es aber schon die sum-Funktion, bzw. mean aus dem statistics-Modul.
Das unter `if __name__...` gehört in eine Funktion `main`.
Strings sind kein Ersatz für Kommentare.

Code: Alles auswählen

""" The program integrated int-Values in a list and calculate the sum of them"""
from statistics import mean

def test():
    # Asks for (more) inputs
    while True:
        answer = input("Do you want to make some input(y = yes | n = no)?: ")
        if answer in ('n', 'y'):
            break
    return answer == 'y'


def make_input():
    # Requests inputs
    return int(input("Please enter an integer: "))


def average_result(values):
    print("The average is", mean(values))


def main():
    values = []

    # While test is True the int_values should integrated in a list
    while test():
        values.append(make_input())
    average_result(values)

if __name__ == "__main__":
    main()
Welche ›Lösung mit dem bool‹ meinst Du denn?

Normalerweise möchte man so lange Werte eingeben, bis man die Eingabe z.B. durch einen leeren String beendet:

Code: Alles auswählen

""" The program integrated int-Values in a list and calculate the average of them"""
from statistics import mean

def main():
    values = []
    while True:
        value = input("Please enter an integer (return to end): ")
        if not value:
            break
        values.append(int(value))
    print("The average is", mean(values))

if __name__ == "__main__":
    main()

Re: Die Ausführung ist etwas unschön (finde ich) geht das noch schöner bzw. komfortabler?

Verfasst: Mittwoch 9. September 2020, 22:49
von Trev
Ja die if-Abfrage in make_input war ein Überbleibsel von einem anderen Versuch.
Ich habe noch nicht so viele Bibliotheken im Kopf.
Sah bestimmt lustig aus hahaha
Edit: Alter was nicht mal 20 Zeilen mit der Bibliothek?

Re: Die Ausführung ist etwas unschön (finde ich) geht das noch schöner bzw. komfortabler?

Verfasst: Mittwoch 9. September 2020, 22:55
von __blackjack__
@Trev: Zeichenketten sind keine Kommentare. Kommentare werden nur durch # gekennzeichnet. Zeichenketten mitten im Code sind im besten Fall einfach nur unnötige Ausdrücke deren Ergebnis (die Zeichenkette) für nichts verwendet werden. Aber sie haben für die Sprache selber an bestimmten Stellen eine besondere Bedeutung zur Dokumentation. Und für Werkzeuge zur Dokumentation nicht an den von der Sprache spezifizierten Stellen, sondern teilweise auch noch anderswo.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in der Regel auch was in der Dokumentation von Python oder verwendeten Bibliotheken steht.

Das was unter dem ``if __name__ …``-Guard steht gehört in eine Funktion damit keine globalen Variablen auf Modulebene existieren auf die man versehentlich in einer Funktion zugreifen könnte, was Programme schwerer verständlich und fehleranfälliger macht.

Grunddatentypen haben in Namen nichts zu suchen. Das ändert man während der Programmentwicklung gerne mal und dann hat man falsche, irreführende Namen im Quelltext oder muss die überall im Programm anpassen.

Wenn bei `created_list` der Typ aus dem Namen fällt, bleibt da nur noch `created` was keinen Sinn macht. Auch mit dem `_list` ist der Name nicht sinnvoll. Namen sollen dem Leser verraten was der Wert dahinter für eine Bedeutung im Programm hat. Das es sich um eine Liste handelt, und das die erstellt worden ist (das wird jede Liste), sagt dem Leser nicht was da eigentlich enthalten ist. `numbers` wäre beispielsweise passender.

Funkions- und Methodennamen beschreiben üblicherweise die Tätigkeit, welche die Funktion oder Methode durchführt. `test()` ist da *sehr* allgemein. Der Leser will wissen *was* da getestet wird. `ask_confirmation()` beispielsweise. Und dann könnte man die auch etwas allgemeiner halten und den eigentlichen Fragetext als Argument übergeben. Und man würde nach der Eingabe von weiteren Werten auch *nach* der Eingabe eines Wertes fragen und nicht davor. Wenn der Benutzer nämlich schon *vor* der ersten Eingabe die Frage verneint, dann hat Dein Programm ein Problem und läuft in eine Ausnahme.

Was mir der Name `make` sagen soll ist mir schleierhaft.

Man belegt nicht vor einer ``while``-Schleife eine Variable mit einem Dummywert der die Schleife nicht abbricht sondern macht daraus eine ”Endlosschleife” mit einem ``if`` an der passenden Stelle, welches die Schleife dann abbricht.

Bei einem ``if``/``else`` das über eine Bedingung `True` oder `False` zurück gibt, braucht man das ``if``/``else`` nicht, denn man hat ja bereits das Ergebnis der Bedigung das einen Wahrheitswert darstellt.

`make_input()` ist als Namen schlecht weil die Funktion nicht erstellt sondern den Benutzer fragt. *Der* macht dann die Eingabe, nicht die Funktion.

Was soll das `new_` bei `new_input`? Das hat keinen Informationsgehalt.

Auch hier ist wieder ein unnötiges ``if``/``else`` denn wenn der Benutzer 0 eingibt wird 0 zurückgegegen sonst das was der Benutzer eingegeben hat. Also letztendlich *immer* das was der Benutzer eingegeben hat. Die Abfrage macht keinen Sinn.

`average_result()` ist wieder keine Tätigkeit. Das wäre ein guter Name für das Durchschnittsergebnis. Da fehlt die Tätigkeit: `print_average_result()`.

Das mit den Namen geht weiter: `usable_list`. Was ist denn eine „verwendbare Liste“? Wie verhält die sich gegenüber einer nicht-verwendbaren Liste? Und der Datentyp ist da wieder drin. `numbers` wäre hier wieder ein passender Name.

`addition` macht auch keinen Sinn. Das ist die (Gesamt)Summe und keine „Addition“.

Die Schleife dort ist in Python ein „anti pattern“. Statt ``for i in range(len(sequence)):`` nur um dann über den unnötigen Laufindex auf das jeweilige Element zuzugreifen, iteriert man in Python direkt über die Elemente: ``for element in sequence:``.

Und auch das kann man sich sparen, denn es gibt die `sum()`-Funktion.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3


def ask_confirmation(prompt):
    prompt = f"{prompt} (y = yes | n = no): "
    while True:
        response = input(prompt)
        if response in ["y", "n"]:
            return response == "y"


def ask_number():
    #
    # FIXME Handle user input that's not an integer.
    #
    return int(input("Please enter an integer: "))


def print_average(numbers):
    print("The average is", sum(numbers) / len(numbers))


def main():
    numbers = []
    while True:
        numbers.append(ask_number())
        if not ask_confirmation("Do you want to make more input?"):
            break
    print_average(numbers)


if __name__ == "__main__":
    main()
Das mit der Frage bei jeder Zahl würde ich mir überlegen. Das ist nervig. Eine Leereingabe als Ende der Eingabe ist für den Benutzer einfacher als immer "y" plus Eingabetaste zwischen den Eingaben tippen zu müssen.

Re: Die Ausführung ist etwas unschön (finde ich) geht das noch schöner bzw. komfortabler?

Verfasst: Donnerstag 10. September 2020, 09:55
von Trev
@__blackjack__ @Sirius3 Ach herrje ist das simpel. Das ist meine Lösung.
Die hat jetzt auch diese komische abfrage weg, die einen Bool zurückgibt, ist also komfortabler, aber aus irgendeinem Grund will es bei einer bestimmten Größe nicht mehr

Code: Alles auswählen

# The program integrated int-Values in a list and calculates  the average of them


def request_integer():
    # In this function you can enter your values
    val = input("Please enter an integer: ")
    return val


def calculate_average_result(numbers):
    # This function calculates the average and prints out the result
    total = 0
    # Calculates the total sum in a for-loop
    for i in numbers:
        total += i
    print("The average is: ", total / len(numbers))


def main():
    values = []
    while True:
        # Integrates inputs as long as you make an empty input
        values.append(int(request_integer()))
        # If you press just enter the while-loop should break because you've made an empty input
        if not request_integer():
            break
    # Transfers the list 'values' to the function 'calculate_average_result'
    calculate_average_result(values)


if __name__ == '__main__':
    main()
Ich habe so eine komische Warnung:
PEP 8: W292 no newline end of file.
Was heißt das
Vielen Dank für eure Hilfe

Re: Die Ausführung ist etwas unschön (finde ich) geht das noch schöner bzw. komfortabler?

Verfasst: Donnerstag 10. September 2020, 10:07
von Sirius3
Die Warnung bedeutet, dass die letzte Zeile nicht mit einem Zeile-Ende-Zeichen abgeschlossen wurde.
Benutze keine Abkürzungen, wenn Du value meinst, schreibe nicht val.
`request_integer` tut nicht, was der Funktionsname verspricht, sie liefert nämlich einen String.
Du fragst jetzt immer zweimal eine Zahl ab, die erste Zahl wird in die Liste gepackt, die Zweite nur dazu benutzt, um zu testen, ob man nichts eingegeben hat, und ansonsten ignoriert. Das ist denke ich nicht das Verhalten, das ein Benutzer erwartet.

Re: Die Ausführung ist etwas unschön (finde ich) geht das noch schöner bzw. komfortabler?

Verfasst: Montag 14. September 2020, 07:38
von Trev
Sirius3 hat geschrieben: Donnerstag 10. September 2020, 10:07 Die Warnung bedeutet, dass die letzte Zeile nicht mit einem Zeile-Ende-Zeichen abgeschlossen wurde.
Benutze keine Abkürzungen, wenn Du value meinst, schreibe nicht val.
`request_integer` tut nicht, was der Funktionsname verspricht, sie liefert nämlich einen String.
Du fragst jetzt immer zweimal eine Zahl ab, die erste Zahl wird in die Liste gepackt, die Zweite nur dazu benutzt, um zu testen, ob man nichts eingegeben hat, und ansonsten ignoriert. Das ist denke ich nicht das Verhalten, das ein Benutzer erwartet.
Habs fixen können.
Danke nochmal