Neuling hat Problem bei Testprogramm

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.
pyriander
User
Beiträge: 3
Registriert: Dienstag 26. Oktober 2010, 11:36

Moin,
ich mach zuzeit in der Uni einen Kurs Einführung in Python, bin also kompletter Newbie. Meine Frage ist simpel und wahrscheinlich einfach zu beantworten aber ich bin grad zu blöd und hab keine Lust bis Freitag zu warten. Ich will das Programm "jetzt" zum laufen bekommen.

Also Problem ist folgendes: Wir sollen ein Programm schreiben, das kurz gesagt mit 3 Funktionen eine Liste mit Personen durchgeht und feststellt, ob diese Personen a) erwachsen sind (also über 18) , b) noch Kinder sind (also unter 18) und c) Frauen sind (über 18 und weiblich).

Mein Programm erstellt aus der csv-Datei eine verschachtelte Liste, in der jede Person eine eigene Liste mit je 4 Einträgen ist (Vorname, Nachname, Geburtsjahr, Geschlecht). Anhand dieser Liste soll die 2. Funktion das Alter überprüfen. Tut sie auch, allerdings nur beim ersten Datensatz, danach bricht sie ab. Und ich hab keine Ahnung wie ich sie dazu bewegen soll, den Rest auch noch zu testen...

Hier mal der Code den ich geschrieben hab:

Code: Alles auswählen

#!python

def get_info(datei):
    passagierliste=[]
    passagiere = open(datei, "r")
    for line in passagiere:
        line=line[:-1]
        line=line.split(":")
        passagierliste.append(line)
    passagiere.close()
    return(passagierliste)

def ist_erwachsen(datei):
    passagierliste=get_info(datei)
    for passagier in passagierliste:
        if 2010 - int(passagier[2]) >=18:
            return True
        else:
            return False
Momentan kommt es mir erstmal darauf an, dass die ist_erwachsen Funktion alles checkt. Der Rest baut darauf aus und müsste also eigentlich dann auch ganz funktionieren wenn die erste alles richtig macht.
Sorry, das ist wahrscheinlich ein total simples Problem das ich hab, aber in den Tutorials und Hilfen zu Python hab ich nix gefunden, das mir geholfen hätte...

Hoffe mir kann da wer helfen oder wenigstens einen Tipp geben?

mfg,
pyri
Zuletzt geändert von pyriander am Dienstag 26. Oktober 2010, 12:46, insgesamt 1-mal geändert.
Benutzeravatar
lutz.horn
User
Beiträge: 205
Registriert: Dienstag 8. November 2005, 12:57
Wohnort: Pforzheim

pyriander hat geschrieben:

Code: Alles auswählen

def ist_erwachsen(datei):
    passagierliste=get_info(datei)
    for passagier in passagierliste:
        if 2010 - int(passagier[2]) >=18:
            return True
        else:
            return False
Deine Testfunktionen sollten für jeweils eine Person prüfen, ob diese Person den Kriterien der Funktion entspricht. Deine Funktionen prüfen aber, ob die erste Person der Liste den Kriterien entspricht. Mache es z. B. so:

Code: Alles auswählen

def ist_erwachsen(person):
    return (2010 - int(passagier[2])) >= 18
Über die Struktur Deiner Daten sollten wir übrigens noch einmal gesondert reden :)
https://www.xing.com/go/invite/18513630.6a91d4
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Zudem öffne die Datei bitte mit "with open(...) as filehandle:"
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich würde den SheBang anders formulieren:

Code: Alles auswählen

#!/usr/bin/env python
# oder bei Python 3 (und nicht ArchLinux ;-))
#!/usr/bin/env python3
Ich würde in einer main-Funktion die Datei einlesen und dann alles andere auf dieser Liste erledigen. Mit Deinem Konzept musst Du ja für jede Test-Funktion die Datei neu parsen... nicht sehr schön.

@Frage: Wenn alle Personen auf die Eigenschaft hin überprüft werden sollen, dann würde ich in der Testfunktion eine neue Liste erstellen und alle Personen, die das kriterium erfüllen dort einfügen, etwa so:

Code: Alles auswählen

def check_adult(persons):
    adults = []
    for person in persons:
        # wieso wird die Konvertierung nicht beim Parsen erledigt?
        if 2010 - int(person) >=18:
            adults.append(person)
    return adults
Wenn man List-Comprehensions verwendet, geht es auch kompakter:

Code: Alles auswählen

adults = [person for person in persons if 2010 - int(person[2]) >= 18]
Da die anderen check-Funktionen allesamt sehr ähnlich aufgebaut sind, wäre der Vorschlag von lutz.horn sogar besser, da er das Kriterium für eine Person prüft. Von außen könnte man das dann auf die gesamte Liste iterativ anwenden, oder ggf. kumuliert in einer "and"-Kaskade.

"return" ist übrigens keine Funktion, sondern ein Statement. Daher solltest Du die "()" danach weglassen.

Je nach Dateiformat könntest Du auch direkt das CSV-Modul verwenden.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@pyriander: Neben dem schon gesagten:

``line[:-1]`` ist keine gute Idee weil nicht in jeder Textdatei das Zeilenende nur mit einem Zeichen kodiert ist und es ausserdem vorkommen kann, dass die letzte Zeile einer Datei nicht mit Zeilenende-Zeichen abgeschlossen ist.

Zur Namensgebung: Die Mischung aus deutsch und englisch ist unschön. Warum dann nicht auch `zeile` statt `line` und `hole_info()` statt `get_info()`!?

Du setzt Leerzeichen inkonsequent. Der Python-Style-Guide empfielt vor und nach Operatoren und Zuweisungs-``=`` jeweils ein Leerzeichen zu setzen.

Das die Testfunktion besser auf eine einzelne Person beschränkt sein sollte, wurde ja schon gesagt. Dann kann man sie in einer Schleife oder mit der `map()`-Funktion auf ein "iterable" von Personendaten anwenden. Oder `filter()` verwenden um Personenlisten zu erstellen.

Wenn Du in einem ``if`` und dem folgenden ``else``-Zweig jeweils nur einen literalen Wahrheitswert zurückgibst, dann ist das ziemlich umständlich ausgedrückt, denn die Bedingung beim ``if`` ist ja in der Regel auch schon eben jener Wahrheitswert, oder kann zumindest einfach in einen solchen umgewandelt werden.

Code: Alles auswählen

if condition:
    return True
else:
    return False

# ist in den meisten Fällen äquivalent zu:

return condition

# wo es das nicht ist, geht:

return bool(condition)

# Mit entsprechenden Definitionen für `Date`, ``type(person)``, und `LEGAL_AGE`:

def is_adult(person):
    return Date.today().year - person.birthyear >= LEGAL_AGE
pyriander
User
Beiträge: 3
Registriert: Dienstag 26. Oktober 2010, 11:36

Hey!
Wow! Danke für die schnelle und vor allem umfassende Hilfe zu meinem winzigen Problem!!

Also ich habs jetzt erstmal wie folgt gemacht und meinen schon geschriebenen Code einfach angepasst, da ich jetzt grad nicht mehr Zeit hab kann ich mit den restlichen Tipps noch nicht auseinandersetzen, aber vielen Dank dafür!

Mein Programm sieht jetzt wie folgt aus:

Code: Alles auswählen

#!python

def hole_infos(datei):
    passagierliste = []
    passagiere = open(datei, "r")
    for passagier in passagiere:
        passagier = passagier[:-1]
        passagier = passagier.split(":")
        passagierliste.append(passagier)
    passagiere.close()
    return(passagierliste)

def ist_erwachsen(passagier):
    if 2010 - int(passagier[2]) >= 18:
        return True
    else:
        return False

def ist_kind(passagier):
    if ist_erwachsen(passagier) == False:
        return True
    else:
        return False

def ist_frau(passagier):
    if ist_erwachsen(passagier) == True:
        if passagier[3] == "w":
            return True
        else:
            return False

def rettung(datei):
    passagierliste = hole_infos(datei)
    frauen_u_kinder = []
    maenner = []
    for passagier in passagierliste:
        if ist_frau(passagier) == False:
            maenner.append(passagier)
        if ist_kind(passagier) == True:
            frauen_u_kinder.append(passagier)
        if ist_frau(passagier) == True:
            frauen_u_kinder.append(passagier)
    print (frauen_u_kinder)
    print (maenner)
Im Grunde wird also die unterste Funktion aufgerufen und greift dann auf alle anderen zu.

Für 4 Stunden Python find ich das was ich geschafft hab schon okay :) Die restlichen Tipps mit den Vereinfachungen und Verbesserungen werd ich mir in Ruhe anschauen... so auf die schnelle sagen mir die ganzen Befehle nix. Aber das kommt sicher noch!

Danke nochmal!
lg,
pyri
Benutzeravatar
lutz.horn
User
Beiträge: 205
Registriert: Dienstag 8. November 2005, 12:57
Wohnort: Pforzheim

pyriander hat geschrieben:Im Grunde wird also die unterste Funktion aufgerufen und greift dann auf alle anderen zu.
Noch ist Dein Code nicht lauffähig. Du solltest noch etwas in dieser Art machen:

Code: Alles auswählen

if __name__ == "__main__":
    rettung("meine_datei.csv")
https://www.xing.com/go/invite/18513630.6a91d4
BlackJack

@pyriander: Genau wie das zurückgeben von literalen `True` und `False` wenn das eigentlich schon das Ergebnis der Auswertung der Bedingung ist, so ist das explizite Vergleichen mit ``==`` oder ``!=`` auf `True` und `False` unnötig.

Beispielsweise bei `ist_frau()` der erste Test: `ist_erwachsen()` gibt doch schon `True` oder `False` zurück, also genau das was bei einer ``if``-Bedingung erwartet wird. Wenn Du diesen Wert noch einmal auf ``== True`` testest, kommt dabei auch nur wieder *genau* der selbe Wert heraus wie der Aufruf von `ist_erwachsen()` schon ergab.

Wenn man das Gegenteil eines Wahrheitswertes haben möchte, sollte man auch nicht ``!= True`` oder ``== False`` verwenden, sondern den Wert einfach mit ``not`` negieren. `ist_kind()` wäre dann zum Beispiel ganz einfach nur ``return not ist_erwachsen(passagier).

Verschachtelte ``if``-Abfragen sollte man nur verwenden, wenn es der Lesbarkeit dient weil die Bedingungen sonst zu komplex werden, und wenn man die jeweiligen Bedingungen nicht logisch mit ``and`` oder ``or`` verknüpfen kann. ``ist_frau()`` ist nämlich auch *viel* zu kompliziert ausgedrückt. Das ist einfach nur eine Zeile: ``return ist_erwachsen(passagier) and passagier[3] == 'w'``. In der `rettung()`-Funktion kann man mit ``or`` zwei ``if``\s zusammenfassen.

Das und einiges andere umgesetzt (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import with_statement
from datetime import date as Date


NAME_INDEX, SURNAME_INDEX, BIRTHYEAR_INDEX, SEX_INDEX = xrange(4)
MALE, FEMALE = 'mw'
CURRENT_YEAR = Date.today().year
LEGAL_AGE = 18


def iter_persons_from_lines(lines):
    for line in lines:
        person = line.rstrip().split(':')
        person[BIRTHYEAR_INDEX] = int(person[BIRTHYEAR_INDEX])
        yield person


def is_adult(person):
    return CURRENT_YEAR - person[BIRTHYEAR_INDEX] >= LEGAL_AGE


def is_child(person):
    return not is_adult(person)


def is_woman(person):
    return is_adult(person) and person[SEX_INDEX] == FEMALE


def is_man(person):
    return is_adult(person) and person[SEX_INDEX] == MALE


def partition(predicate, iterable):
    result_a = list()
    result_b = list()
    for item in iterable:
        (result_a if predicate(item) else result_b).append(item)
    return (result_a, result_b)


def main():
    def should_go_first(person):
        return is_child(person) or is_woman(person)
    
    with open('passagiere.txt') as lines:
        persons = iter_persons_from_lines(lines)
        women_and_children, the_rest = partition(should_go_first, persons)
        # 
        # Just to be sure there are no unknown sexes, hermaphrodites,
        # or robots among the passengers.  :-)
        # 
        men = filter(is_man, the_rest)
        print women_and_children
        print men


if __name__ == '__main__':
    main()
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ich würde es eher so machen:

Code: Alles auswählen

#!/usr/bin/env python
from datetime import date

LEGAL_AGE = 18

class Person(object):
    
    def __init__(self, name="", year=0, sex="m"):
        self.name = name
        #...
        self.birthyear = int(year)
        self.sex = sex

    def __repr__(self):
        return "Name: %s\n\tGeburtsjahr: %i\n\tGeschlecht: %s\n" \
               % (self.name, self.birthyear, self.sex)
        
    def is_adult(self):
        return date.today().year - self.birthyear >= LEGAL_AGE

    def is_child(self):
        return not self.is_adult()

    def is_female(self):
        if self.is_adult():
            return self.sex == "w"
        

def get_infos(file_):
    passenger_list = []
    
    with open(file_) as passengers:
        for passenger in passengers:
            passenger_data = passenger.strip().split(":")
            passenger_list.append( Person(*passenger_data) )
            
    return(passenger_list)

def rescue(passenger_list):
    females_and_children = []
    males = []
    
    for passenger in passenger_list:
        if not passenger.is_female():
            males.append(passenger)
            
        if passenger.is_child():
            females_and_children.append(passenger)
            
        elif passenger.is_female():
            females_and_children.append(passenger)

    return females_and_children, males


if __name__ == "__main__":
    passenger_list = get_infos("./list.txt")
    females_and_children, males = rescue(passenger_list)

    print("Frauen und Kinder:")
    for person in females_and_children:
        print(person)

    print("")
    print("M\xe4nner:")
    for person in males:
        print(person)
Ok CURRENTYEAR und einige andere "Konstanten" könnte man noch aus Black beispiel mit auslagern.

Edit: und wieder aus einer Mücke einen Elefanten gemacht :D
Zuletzt geändert von Xynon1 am Dienstag 26. Oktober 2010, 14:31, insgesamt 2-mal geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
pyriander
User
Beiträge: 3
Registriert: Dienstag 26. Oktober 2010, 11:36

hmm... also für mich funktioniert der code :) zumindest wenn ich ihn im IDLE aufrufe. Klar ist das keine ausführbare Datei.

Alle eure Ansätze sind zwar garantiert wesentlich eleganter als mein gewurschtel, aber ich kann sie (noch) nicht nachvollziehen, da mir das Wissen um Python noch fehlt. Tja, viele Wege führen nach Rom, manche halt mit umwegen :).

Nichts desto trotz: Nochmal danke für eindringlichen Hinweise auf Vereinfachung des Codes, ich werde sie im Kopf behalten und sie mir am Wochenende mal genauer ansehen.

thx!
pyri
BlackJack

@Xynon1: Es lässt ein bisschen Sorgfalt bei der Namensgebung vermissen. :-P

Mädchen, im Sinne von nichtvolljährigen "Weibchen" sind bei Dir nicht weiblich!? Und Defaultwert für das Geschlecht bei Personen ist männlich!? Ein Sexist mit komischen Ansichten. ;-)

Das `is_female()` auch `None` zurückgeben kann, ist IMHO unschön, auch wenn es so funktionieren könnte.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@pyriander
Sieh dir vorallem blackjacks hinweise an, da die sehr gut sind.

@BlackJack
Ja, war schnell hingekritzelt :roll: und auf sein Beispiel aufgesetzt, werde es gleich mal ausbessern.
btw ich hätte beinah "w" geschrieben gehabt, doch zum Glück habe ich rechtzeitig noch eines besseren besonnen. :P
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

So oder habe ich noch was veressen ?
Damit sollte auch dein undefinierter Fall glöst sein :D

Code: Alles auswählen

#!/usr/bin/env python
from datetime import date

MALE, FEMALE, NEUTER = "mwn"
CURRENT_YEAR = date.today().year
LEGAL_AGE = 18

class Person(object):
    
    def __init__(self, name="", year=0, sex=NEUTER):
        self.name = name
        #...
        self.birthyear = int(year)
        self.sex = sex.lower()

    def __str__(self):
        return "Name: %s\n\tGeburtsjahr: %i\n\tGeschlecht: %s\n" \
               % (self.name, self.birthyear, self.sex)
        
    def is_adult(self):
        return CURRENT_YEAR - self.birthyear >= LEGAL_AGE

    def is_child(self):
        return not self.is_adult()

    def is_woman(self):
        return self.is_adult() and self.sex == FEMALE
        
    def is_man(self):
         return self.is_adult() and self.sex == MALE
        

def get_infos(file_):
    passengers = []
    
    with open(file_) as persons:
        for person in persons:
            data = person.strip().split(":")
            passengers.append( Person(*data) )
            
    return passengers

def rescue(passengers):
    women_and_children = []
    men = []
    neuter = []
    
    for person in passengers:

        if person.is_child() or person.is_woman():
            women_and_children.append(person)
            
        elif person.is_man():
            men.append(person)

        else:
            neuter.append(person)

    return women_and_children, men, neuter


if __name__ == "__main__":
    passengers = get_infos("./list.txt")
    women_and_children, men, neuter = rescue(passengers)

    print("Frauen und Kinder:")
    for person in women_and_children:
        print(person)

    print("")
    print(u"M\xe4nner:")
    for person in men:
        print(person)

    if len(neuter) > 0:
        print("")
        print(u"Folgende Personen k\xf6nnen nicht zugeordnet werden:")
        for person in neuter:
            print(person)
Zuletzt geändert von Xynon1 am Dienstag 26. Oktober 2010, 15:19, insgesamt 6-mal geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
lutz.horn
User
Beiträge: 205
Registriert: Dienstag 8. November 2005, 12:57
Wohnort: Pforzheim

Xynon1 hat geschrieben:

Code: Alles auswählen

    def is_woman(self):
        return self.is_adult() and self.sex == MALE
        
    def is_man(self):
         return self.is_adult() and self.sex == FEMALE
Sicher?
https://www.xing.com/go/invite/18513630.6a91d4
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

richtig :oops: - danke, geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Code: Alles auswählen

print("M\xe4nner:")
Hm... wozu das?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Weil ich selten Zeichenkodierungen definiere, ist ein Tick von mir.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Xynon1 hat geschrieben:Weil ich selten Zeichenkodierungen definiere, ist ein Tick von mir.
An dem Tick solltest Du arbeiten :mrgreen:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Code: Alles auswählen

# unschön kopiert ;-)
return(passengers)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

verdammt :shock: , ich hätte dochmal ein editor nehmen soll mit highlighting und vorallem das ganze mal mit daten füttern. :oops:

Noch was ? :D
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Antworten