Pythonuppgift, skapa en filofax... behöver hjälp med att sortera efter datum!

Permalänk
Medlem

Pythonuppgift, skapa en filofax... behöver hjälp med att sortera efter datum!

Hej!

Jag har en skoluppgift där jag ska skapa en fungerande filofax (anteckningsbok) i Python.
Man ska kunna få fram en meny, skapa nya sidor med anteckningar, bläddra osv.

Jag har fått till allt förutom att när nya sidor skapas så läggs de till i den ordning man lägger till dem. Jag vill att de ska läggas till efter datumordning. Hur gör man det!? Jag har testat olika metoder i två dagar nu, men inte kommit fram till någon lösning. Jag använder den inbyggda funktionen datetime.

Snälla hjälp mig, uppskattar all hjälp jag kan få!

Kod:

import datetime # -*- coding: utf-8 -*- class anteckningssidor(object): def __init__(self, datum, anteckning): self.datum = datum self.anteckning = anteckning def __str__(self): return "Sida: " + self.datum + " med anteckningen: "+ self.anteckning class filofax(object): def __init__(self, sidor=[], sidan=""): self.sidor = sidor self.sidan = sidan def __str__(self): return str(len(self.sidor)) + " sidor inlagda" def bladdra(self, riktning): if len(self.sidor): sidnummer = self.sidor.index(self.sidan) sidnummer += riktning if sidnummer >= len(self.sidor): sidnummer = 0 elif sidnummer < 0: sidnummer = len(self.sidor) - 1 self.sidan = self.sidor[sidnummer] #else: #self.sidan = self.sidor[sidnummer] #return True def nysida(self, datum, anteckning): ny_sida = anteckningssidor(datum, anteckning) self.sidor.append(ny_sida) self.sidan = ny_sida def radera(self): if len(self.sidor): raderad = self.sidan self.bladdra(1) self.sidor.remove(raderad) if len(self.sidor) == 0: self.sidan = () def lista(self): for lista in self.sidor: return self.sidor #felhantering för datum def felhantering_datum(): while True: try: korrekt_datum = input ("Ange datum på formen ÅÅÅÅ-MM-DD: ") datetime.datetime.strptime(korrekt_datum, "%Y-%m-%d") return korrekt_datum break except ValueError: print ("Ange ett riktigt datum på den korrekta formen, tack!") def main(): sidorna = filofax() lista = [] try: file = open("sidor1.txt", "r") tmp = file.readlines() while tmp: datum = tmp [:-1] anteckning = file.readlines()[:-1] sidorna.nysida(datum, anteckning) tmp = file.readlines() file.close() except(IOError): print ("\n") val = " " while val: if sidorna.sidan: print ("\n Aktuella sidan: ", sidorna.sidan) print (""" Meny 0 - Avsluta 1 - Bläddra framåt 2 - Bläddra bakåt 3 - Lägg till anteckning 4 - Ta bort denna anteckning 5 - Visa alla anteckningar \n""") val = input("Ange val: ") if val == "0": print ("\nVi ses!\n") val = "" elif val == "1": if sidorna.lista() == None: print ("\nDu har inga anteckningar ännu...") else: sidorna.bladdra(1) elif val == "2": if sidorna.lista() == None: print ("\nDu har inga anteckningar ännu...") else: sidorna.bladdra(-1) elif val == "3": datum = felhantering_datum() anteckning = input("Ange anteckning for datumet: ") sidorna.nysida(datum, anteckning) elif val == "4": if sidorna.lista() == None: print ("\nDet finns inga anteckningar att radera...") else: sidorna.radera() print ("\nAnteckning borttagen.") elif val == "5": if sidorna.lista() == None: print ("\nDu har inga anteckningar ännu...") continue else: for alla_sidor in sidorna.lista(): print (alla_sidor) else: print("\nVänligen välj en siffra mellan 0-5") main()

Permalänk
Medlem

Nu är det sent (för mig) och jag är inte särskilt vass på python, eller kod i allmänhet. Men borde du inte kunna sortera listan baserat på datum? Eller ha två olika listor, en osorterad och en sorterad.

Som sagt, inge vidare på detta, bara en idé.

Skickades från m.sweclockers.com

Permalänk
Medlem

@Neihada: Jo det stämmer, det borde jag kunna göra. Problemet är bara att jag inte vet hur jag ska gå tillväga...

Permalänk

Du vill nog använda den inbyggda funktionen sorted. Exakt hur får du komma på själv.

Permalänk
Medlem

@Hieronymus Bosch: Okej, tack. Jag har försökt använda den, men vet du var man lägger in den? Är det i klassen eller när man anropar själva klassen, alltså i main() ?

Permalänk
Skrivet av sweshady:

@Hieronymus Bosch: Okej, tack. Jag har försökt använda den, men vet du var man lägger in den? Är det i klassen eller när man anropar själva klassen, alltså i main() ?

Det beror på. Kommer det vanligaste fallet vara att du vill få anteckningarna presenterade i datumordning, eller kommer olika delar av programmet att vilja ha dem sorterade på olika sätt?

Permalänk
Medlem

@Hieronymus Bosch: Det vanligaste fallet kommer vara att de ska vara sorterade i datumordning.

Permalänk
Skrivet av sweshady:

@Hieronymus Bosch: Det vanligaste fallet kommer vara att de ska vara sorterade i datumordning.

Ah, då blir det lätt. Placera koden som gör sorteringen så att du bara behöver skriva den en gång -- i filofaxklassens lista-metod.

Permalänk
Medlem

@nikwad: Bra, tack! Nu borde det funka enligt mig, men jag får tillbaka

def lista(self): sorterade = sorted(self.lista, key=lambda x: datetime.datetime.strptime(x[0], '%Y-%m-%d')) for listan in sorterade: return (listan)

men jag får bara tillbaka: "TypeError: 'method' object is not iterable"

Permalänk
Medlem
Skrivet av sweshady:

Massa text

Hej! Vad roligt att du skriver program. Fortsätt göra detta och experimentera så kommer du långt. Det är inte heller fel att fråga om hjälp om man fastnar. Ibland kan det vara enkla problem; och det kan vara så att man bara tänker från fel perspektiv.

Jag tar för givet att du använder Python 3.x. Python har en inbyggd funktion vid namn sorted. Denna hittar du dokumentation om på deras sida här. Ibland kanske man inte förstår, men mitt tips är att läsa igenom flera gånger samt prova dig fram tills du har stenkoll.

Genom att titta i dokumentationen får du veta några saker.

sorted(iterable[, key][, reverse]) Return a new sorted list from the items in iterable. Has two optional arguments which must be specified as keyword arguments. key specifies a function of one argument that is used to extract a comparison key from each list element: key=str.lower. The default value is None (compare the elements directly). reverse is a boolean value. If set to True, then the list elements are sorted as if each comparison were reversed.

Vad härligt! Man kan alltså t.ex skicka in en hjälpfunktion som nyckel till listan (som exemplet med str.lower visar). Har du gjort lambda-funktioner tidigare? Om inte, häng med och försök förstå det bästa du kan.

# Försöker efterlikna ditt problem med en class för varje anteckning class Note: def __init__(self, date, note): self.date = date self.note = note # En osorterad lista med anteckningsobjekt list_of_notes = [Note("2016-01-01", "Hej jag är en anteckning"), Note("2015-01-01", "Jag borde vara före den andra!"), Note("2015-12-01", "Jag ska vara på andra plats")] for note in list_of_notes: print(note.date) """ Konsollen skriver ut: 2016-01-01 2015-01-01 2015-12-01 Det är osorterat! """ """ Vi sorterar den efter datum. OBS! sorted() och list.sort() är lika. Enligt dokumentationen kan vi använda en nyckel! Vi skickar in en lambdafunktion som key som helt enkelt anger att vi vill bara iterera över varje objekts instansvariabel date. """ list_of_notes.sort(key=lambda note: note.date) for note in list_of_notes: print(note.date) """ Vi får ut detta i konsollen: 2015-01-01 2015-12-01 2016-01-01 Wohoo! Objekten i listan är nu sorterade efter deras instansvariabel date. """

Du lär också se till att använda Stora bokstäver för klasser. Det är bra syntax för att skilja på funktioner, variabler och klasser. Glöm inte heller att kommentera! Du kommer tacka dig själv när du ska gå igenom kod du har skrivit för en tid sedan.

Vill du ha ytterligare förklaringar får du fråga mer eller googla!

Visa signatur

Chassi: En stor bakpotatis
CPU: En körsbärstomat (OC 4.5 tomater) som kyls av ketchup
Modertkort: En extra-allt pizza
Nätagg: En modulär kålrot
Grafikkort: En fet biff (standardklock 987mHeinz)

Permalänk
Medlem
Skrivet av sweshady:

@nikwad: Bra, tack! Nu borde det funka enligt mig, men jag får tillbaka

def lista(self): sorterade = sorted(self.lista, key=lambda x: datetime.datetime.strptime(x[0], '%Y-%m-%d')) for listan in sorterade: return (listan)

men jag får bara tillbaka: "TypeError: 'method' object is not iterable"

Om du hittar mitt svar ovan så har jag ytterligare anpassat hjälpkod för dig.

import datetime # Försöker efterlikna ditt problem med en class för varje anteckning class Note: def __init__(self, date, note): self.date = date self.note = note # En osorterad lista med anteckningsobjekt list_of_notes = [Note(datetime.datetime.strptime("2016-01-01", "%Y-%m-%d"), "Hej jag är en anteckning"), Note(datetime.datetime.strptime("2015-01-01", "%Y-%m-%d"), "Jag borde vara före den andra!"), Note(datetime.datetime.strptime("2015-12-01", "%Y-%m-%d"), "Jag ska vara på andra plats")] print("Nu är det osorterat!") for note in list_of_notes: print(note.date) """ Konsollen skriver ut: 2016-01-01 00:00:00 2015-01-01 00:00:00 2015-12-01 00:00:00 Det är osorterat! """ """ Vi sorterar den efter datum. OBS! sorted() och list.sort() är lika. Enligt dokumentationen kan vi använda en nyckel! Vi skickar in en lambdafunktion som key som helt enkelt anger att vi vill bara iterera över varje objekts instansvariabel date. """ list_of_notes.sort(key=lambda note: note.date) print("Nu ska det vara sorterat!") for note in list_of_notes: print(note.date) """ Vi får ut detta i konsollen: 2015-01-01 00:00:00 2015-12-01 00:00:00 2016-01-01 00:00:00 Wohoo! Objekten i listan är nu sorterade efter deras instansvariabel date. """

Visa signatur

Chassi: En stor bakpotatis
CPU: En körsbärstomat (OC 4.5 tomater) som kyls av ketchup
Modertkort: En extra-allt pizza
Nätagg: En modulär kålrot
Grafikkort: En fet biff (standardklock 987mHeinz)

Permalänk
Medlem

@Deatlev: TUSEN tusen tack för hjälpen! Uppskattar det enormt. Är som sagt nybörjare på Python, så ditt pedagogiska svar uppskattas mycket.

Jag förstår lite mer av vad jag behöver göra nu, men av någon anledning så fungerar inte .sort() för mig, utan jag måste använda funktionen sorted() istället. Ja, jag använder Python3 Det jag kom fram till att jag måste skriva är denna kod:

def Lista(self): sorterade = sorted(self.Lista, key=lambda x: datetime.datetime.strptime(x[0], '%Y-%m-%d')) for x in sorterade: return sorterade

men då får jag som svar att: 'method' object is not iterable.

Är det inte en korrekt användning av sorted() funktionen?

Permalänk
Medlem

Du försöker att sortera metoden Lista, inte själva listan. Lista är förövrigt en metod/funktion/whatever och ska inte ha stor bokstav. Ge dina variabler lite enklare namn också.

self.Lista = metoden Lista i klassen.

Permalänk
Medlem
Skrivet av sweshady:

@Deatlev: TUSEN tusen tack för hjälpen! Uppskattar det enormt. Är som sagt nybörjare på Python, så ditt pedagogiska svar uppskattas mycket.

Jag förstår lite mer av vad jag behöver göra nu, men av någon anledning så fungerar inte .sort() för mig, utan jag måste använda funktionen sorted() istället. Ja, jag använder Python3 Det jag kom fram till att jag måste skriva är denna kod:

def Lista(self): sorterade = sorted(self.Lista, key=lambda x: datetime.datetime.strptime(x[0], '%Y-%m-%d')) for x in sorterade: return sorterade

men då får jag som svar att: 'method' object is not iterable.

Är det inte en korrekt användning av sorted() funktionen?

Som @Pether säger.

Ändra din kod så att syntaxen lyder:

  • Klasser börjar med stor bokstav

  • metoder, funktioner och variabler börjar med liten bokstav

Detta är oerhört viktigt för att öka läsligheten på din kod och så att du inte blandar ihop saker som ger upphov till fel!

Sedan kan du också döpa saker med huvudordet först. Det bättre att döpa din funktion till t.ex datum_felhantering istället för felhantering_datum för att om du har flera funktioner som har med datum att göra kan det ju se ut så här:

def felhantering_datum(): .... def something_datum(): .... def increment_datum(): ...

När det istället bör vara på detta sätt för att det är lättare att hitta och veta vad saker gör med översiktsläsning:

def datum_felhantering(): ... def datum_something(): ... def datum_increment(): ...

Mycket finare!

Visa signatur

Chassi: En stor bakpotatis
CPU: En körsbärstomat (OC 4.5 tomater) som kyls av ketchup
Modertkort: En extra-allt pizza
Nätagg: En modulär kålrot
Grafikkort: En fet biff (standardklock 987mHeinz)

Permalänk
Medlem

@Deatlev: Okej, kanon - tack!

Jag har ändrat så klasser har första bokstaven stor, och allt det andra småbokstäver nu.
Jag har skrivit min sorten() kod enligt nedan, men får felmeddelande "TypeError: must be str, not Anteckningssidor", där Anteckningssidor är min första klass... jag hittar inget på google om att det måste vara en sträng och inte klass. Vad behöver jag ändra på!?

def lista(self): self.sidor = sorted(self.sidor, key=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d')) for x in self.sidor: return self.sidor

Permalänk
Medlem
Skrivet av sweshady:

@Deatlev: Okej, kanon - tack!

Jag har ändrat så klasser har första bokstaven stor, och allt det andra småbokstäver nu.
Jag har skrivit min sorten() kod enligt nedan, men får felmeddelande "TypeError: must be str, not Anteckningssidor", där Anteckningssidor är min första klass... jag hittar inget på google om att det måste vara en sträng och inte klass. Vad behöver jag ändra på!?

def lista(self): self.sidor = sorted(self.sidor, key=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d')) for x in self.sidor: return self.sidor

Du har krånglat till lambdan lite bara.

I denna kodsnutt ska du titta.

sorted(self.sidor, key=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d'))

Fråga dig själv vad x innehåller. Du fick felet TypeError. Funktionen sorted förväntade sig en nyckel i form av en funktion som ger en sträng, inte ett objekt. Du har skickat in x i sorted-funktionen, och den säger ifrån och säger att det som för tillfället är x vill den inte ha. Titta på min förra kodsnutt om hur jag löste ditt problem.

För övrigt klarar funktionen sorted av att ta in datetime som det är, vilket betyder att din formatering m.h.a datetime är överflödig. Ett problem som denna kod gör nu är att om du senare vill anteckna tid på dygnet kommer denna kod du har inte sortera efter klockslag. Det skulle se helknasigt ut!

Nu fick du inte ett direkt svar, men jag tror du kan lösa detta mycket enkelt! Annars får du fråga igen så går jag igenom hur du löser det.
Tips: Döp om x till anteckning så ser du nog!

Visa signatur

Chassi: En stor bakpotatis
CPU: En körsbärstomat (OC 4.5 tomater) som kyls av ketchup
Modertkort: En extra-allt pizza
Nätagg: En modulär kålrot
Grafikkort: En fet biff (standardklock 987mHeinz)

Permalänk
Medlem

@Deatlev: YES!!! Nu funkar det! Tack så hemskt mycket för hjälpen! Löste det tack vare dig! Uppskattar det verkligen!! Åh vad skönt.

Angående koden överlag, jag ska fixa bättre variabelnamn och skriva kommentarer så jag förstår mig själv bättre.
Är det något annat jag ska tänka på i framtiden?

Permalänk
Medlem
Skrivet av sweshady:

@Deatlev: YES!!! Nu funkar det! Tack så hemskt mycket för hjälpen! Löste det tack vare dig! Uppskattar det verkligen!! Åh vad skönt.

Angående koden överlag, jag ska fixa bättre variabelnamn och skriva kommentarer så jag förstår mig själv bättre.
Är det något annat jag ska tänka på i framtiden?

Lite småsaker jag ser du kan titta lite på är docstring m.m. Du har ju redan kommit underfund med __str__; funktionen som körs om man kör funktionen print på ett objekt. Titta på __doc__, __name__ och __class__ också.

class Note: """ Psst. Detta är en docstring. """ def __init__(self): pass

En stor grej i all programmering är unittest. Detta är en jätteviktigt sak som kommer att göra dig en bättre programmerare än mängden för att du lär dig att tänka på problem och hur de kan uppstå samt hur du konstruerar test och upptäcker dessa. Detta är anledningen till att jag t.ex nämnde problemet med klockslag. Med test programmerar man då enligt principen testa först, skriv sedan koden. Ungefär metaforiskt med "tänk först, handla sedan". I python används biblioteket unittest samt nyckelordet assert.

Nu ger jag dig ett exempel för processen.

Steg 1 är att skriva ett test.

#testcar.py import unittest from car import * # Klassen TestCar ärver från klassen TestCase i biblioteket unittest. class TestCar(unittest.TestCase): def test_create_instance(self): car = Car() self.assertTrue(isinstance(car, Car))

Nu har vi skrivit ett test, och nu konstruerar vi koden för att testet ska fungera.

#car.py class Car: def __init__(self): pass

Vi kör detta kommando i kommandotolken och får resultat:

python -m unittest testcar.py . --------------------------------------------------------------- Ran 1 test in 0.001s OK

Gå tillbaka till steg 1.

På detta vis är du jädra smart genom att skapa tester för ditt program och på så sätt få ökad förståelse för din egen kod samt ha lättare för att lösa problem. Programmet kommer att växa smidigt och nästan felfritt när du konstruerar en ram av tester för att framhäva din mening. Skriv så små test du kan. Det är dålig vana att skriva alltför mycket i ett test för då kommer du ha svårt att hitta fel om ett test inte går rätt till. I första exemplet ser du ett litet test. Det mest grundläggande är ju faktiskt att bara skapa en instans av ett objekt och se om det har fungerat.

Så ser du att koden växer:

#testcar.py import unittest from car import * # Klassen TestCar ärver från klassen TestCase i biblioteket unittest. class TestCar(unittest.TestCase): def test_create_instance(self): car = Car() self.assertTrue(isinstance(car, Car) def test_create_fueled_car(self): car = Car(10) self.assertEqual(10, car.fuel) def test_add_fuel(self): car = Car() # Vi är helt säkra på att bilen inte ska ha bränsle när vi först skapat instansen. self.assertEqual(0, car.fuel) # Vi tankar bilen och testar car.add_fuel(5) self.assertEqual(5, car.fuel)

#car.py class Car: """ This is the class docstring, wink. This class represents a car. A car can have fuel. """ def __init__(self, fuel=0): self.fuel = fuel def add_fuel(fuel): """ Adds fuel to the car. @param fuel: The amount of fuel to add to the car. """ self.fuel += fuel

Vi testar i kommandotolken igen:

python -m unittest testcar.py E.. =================================== ERROR: test_add_fuel (testcar.TestCar) --------------------------------------------------------------------- Traceback (most recent call last): File "C:\users\testcar.py", line 21, in test_add_fuel car.add_fuel(5) TypeError: add_fuel() takes 1 positional argument but 2 were given. --------------------------------------------------------------------- Ran 3 tests in 0.001s FAILED (errors=1)

Woops! Testet misslyckades! Nu tittar jag på varför och ser att jag har glömt att skicka in parametern self när jag gjorde klassen. Lätt fixat!

Nu får jag:

... -------------------------------------------------------------------- Ran 3 tests in 0.000s OK

och fortsätter koda tills jag uppnått mitt ändamål med klassen car varav jag kanske skriver lite fler tester för att testa s.k. "edge cases".

Se så programmet växer! Detta är bra vana. Skriv test för alla program du gör. Jag stoppade in lite avancerade kommentarer i det senare programmet som är standard i python. Python kan läsa av dessa docstrings och hjälpa andra personer som använder ditt program genom att t.ex skriva help(Car) i konsollen och på så sätt få reda på vad klassen gör, vilka metoder den har och vad de är och används för.

Visa signatur

Chassi: En stor bakpotatis
CPU: En körsbärstomat (OC 4.5 tomater) som kyls av ketchup
Modertkort: En extra-allt pizza
Nätagg: En modulär kålrot
Grafikkort: En fet biff (standardklock 987mHeinz)

Permalänk
Medlem

@Deatlev: Okej, perfekt! Tusen tack!! Jag ska titta närmare och läsa på om allt du tipsade om

Permalänk
Legendarisk

*** Raderat sidospår om Google. Kan man hjälpa frågeställaren med antingen lösningar / ledtrådar till det direkta problemet, eller om det känns allt för enkelt för mer erfarna användare — med faktainsamling — så gör man naturligtvis det. Observera att det inte är tillåtet att posta länkar till LMGTFY. ***

Visa signatur

Abstractions all the way down.