önskar feedback på python3 kod för fortsatt utbildning

Permalänk
Medlem

önskar feedback på python3 kod för fortsatt utbildning

Hej!

Har börjat med mina första staplande steg i python programmering. Målet med resan är att kunna få ihop arduino och rasberryPI. Sysslat en del med arduino/C++ jag vet inte om det är en fördel eller nackdel men det jag känner med python är att det händer grejer som jag inte riktigt är med på. Variabler är en sådan sak som jag tycker är lite klurig de kan bli lite vad de vill beroende på vad jag lägger in i dem. iaf

Har suttit och knåpat ihop en miniräknare som kan göra de fyra räknesätten med två olika nummer som programmet får av användaren. Användaren väljer räknesätt med ett menyval genom att skriva in en siffra vilket räknesätt som skall användas. Väljer användaren lustiga menyval som ej är med i menyn för många gånger säger programmet ifrån. Programmet lägger även ihop alla resultat som räknats ut under hela användningen. Har försökt att få med så många grejer som möjligt och när jag kollar igenom koden så inser jag att det är ändå några stycken med.

Det jag skulle vilja ha hjälp med är om jag definerar saker och ting rätt. Naming conventions, stor liten bokstav på lokala variabler alt globala t.ex _lokalVariabel globalVariabel. Skall jag kommentera koden mer? Brukar det vara mer kommentarer i börjar av programkoden?
Detta är för att jag helst inte vill lära in något som blir sjukt jobbigt att lära sig av med och även försöka skriva så läsbar kod som möjligt. Kan jag använda funktioner på ett bättre sätt? t.ex. looparna

Jag vet att det finns problem i programmet tex så stoppar det om användaren skriver . istället för ,

Jag har precis börjat med felhantering och debbugging men insåg att jag måste nog skriva lite kod för att få ordning på det jag redan lyckats få in i huvudet. Just nu använder jag QPython på mobilen och en standard Python installation på datorn i Windows10. Att skriva kod på mobilen tar en hel evighet. Lyckades hitta mina SD-kort ikväll så jag kan få igång en RPi snart och skriva lite kod på den med. Provade på pyCharm lite men blev inte riktigt kompis med det programmet.

Just nu använder jag mig av mobilappen soloLearn för att lära mig samt lite youtube klipp. Funderar på CodeAcademy men just nu tycker jag det räcker med det jag har. Har även en bok som ligger och i väntläge "Learning python with RaspberryPI" tror jag den heter.

Nedan är koden och tackar på förhand.

#Functions for Arithmetic operations def num_add(x, y): result = x + y return result def num_sub(x, y): result = x - y return result def num_mul(x, y): result = x * y return result def num_div(x, y): result = x / y return result #Function for entering the numbers that the user want to preform arithmetic operation on def input_num(): num = float(input("Enter a number: ")) return (num) #Function for printing a Welcome message def welcome_message(): print("Super Calculator for two numbers\n") #Fuction for printing the meny def print_meny(): #Meny text for operation of calculator print("What do you want to do?\n") for each_meny in meny: print(each_meny) #Defining Arrays, variables needed for the program #Variable counting the number of times the user choosed an invalid choise in meny insult = 0 #Variable that holds the total of all your results added together total = 0 #array for meny text making it global so all functions and loops can see it meny =["1, Addition", "2, Subtraction", "3, Multiplication", "4, Division", "5, Quit"] #Program start welcome_message() print_meny() #Main program loop while True: #Variable to hold the user choise choice_meny = input("Choose a number! ") if choice_meny == "1": print(meny[0]) show_result = num_add(input_num(), input_num()) total += show_result print(show_result) elif choice_meny == "2": print(meny[1]) show_result = num_sub(input_num(), input_num()) total += show_result print(show_result) elif choice_meny == "3": print(meny[2]) show_result = num_mul(input_num(), input_num()) total += show_result print(show_result) elif choice_meny == "4": print(meny[3]) show_result = num_div(input_num(), input_num()) total += show_result print(show_result) elif choice_meny == "5": print(meny[4]) break else: print("I said choose a number from the meny") insult += 1 if insult == 5: insult = 0 print("Oh my God!") print("Why cant you be a normal user?!") print("and atleast use the keys with a number on!!!!!\n\n") print_meny() print("while loop ended") print("Thank you for using this calculator") print("The total of all of your results are: " + str(total)) #Program ended

Visa signatur

Laptop: HP Elitebook 640 G9
Server: HP Microserver N54L, 8 GB ram, 8 TB hd.

Permalänk
Medlem

Jag skulle föreslå att inte använda globala variabler. Sätt in main-loopen i en funktion. Sedan starta denna funktion inuti:

if __name__ == '__main__':

Kanske inte är bästa sättet att göra, men det är så jag gör och så jag lärt mig.

Visa signatur

AMD R5 1600 3.2 GHz | Gigabyte AX370 Gaming K3 | Corsair Vengeance LP 2x8GB 3200MHz CL16 | Sapphire RX580 Nitro+ 8GB | Phanteks Eclipse P400S | 1x Samsung 970 EVO 500GB 1x WD 500GB | Windows 11 Pro

Permalänk
Hedersmedlem
Skrivet av zeem:

Det jag skulle vilja ha hjälp med är om jag definerar saker och ting rätt. Naming conventions, stor liten bokstav på lokala variabler alt globala t.ex _lokalVariabel globalVariabel.

PEP 8 är den vanligaste referensen för namngivning och liknande saker. Typiskt så ska variabler ha snake_case och vara lokala: globala "variabler" ska undvikas, men däremot är det vanligt med globala "konstanter", som ju egentligen är precis samma sak, med enda skillnad att man per konvention inte ändrar dem efter att de väl satts. Sådana konstanter brukar namnges enligt SNAKE_CAPS.

Bill du grotta ner dig i stilval i Python så är PEP 8 som länkades ovan en bra inkörsport. Vidare kan du börja titta på verktyg som pycodestyle och Pylint och försöka förstå vad det är de klagar på i din kod. Kan också nämna Googles interna stilguide för Python som ofta fungerar bra att följa om man känner sig osäker. När man känner sig haj på dessa stilguider så brukar jag rekommendera föredraget Beyond PEP 8 av Raymond Hettinger som kan vara en ögonöppnare för vad som egentligen är mest viktigt när man programmerar.

Skrivet av zeem:

Skall jag kommentera koden mer? Brukar det vara mer kommentarer i börjar av programkoden?

Du bör använda så kallade "docstrings" för dina funktioner. Mer eller mindre alla dina nuvarande kommentarer skulle bättre fungera som docstrings, som då blir en mer officiell dokumentation över din kod. Det du skriver i en docstring exponeras sedan i som ett "magiskt" attribut på din själva funktion, och Python genererar automatiskt en vettig hjälptext åt din funktion om någon vill se en sådan.

Kommentarer som bara säger saker som "End of file", "Load modules", "Define classes", "Define constants", etc., är i mina ögon i stort sett alltid fullkomligt kontraproduktiva. Det är allt som oftast bländade uppenbart att det de beskriver händer enbart utifrån att titta på koden, och den enda effekten de kan ha på koden är att de säger emot vad som faktiskt står och därmed orsakar förvirring. Det är mycket bättre att helt enkelt följa vanliga konventioner för ett språk, vilket för en läsare gör det uppenbart att du i ett visst stycke exempelvis importerar moduler, osv.

Det kan låta överdrivet dramatiskt och negativt, men det går emot mycket av en absolut grundtanke i Python: läsbarhet genom att minimera visuellt brus och okonventionella överraskningar. När nu språket kämpat så för att bli av med exempelvis semikolon och måsvingar för att bli lättare att snabbt skanna av så ska man inte skjuta sig i foten genom att lägga in en massa icke-funktionella sektionsavgränsare i onödan. Det är inte Python.

Skrivet av zeem:

(Citatet ska symbolisera första raden i din kod )

Börja gärna med en så kallad shebang för ett skript som ska kunna exekveras. Ett typexempel för Python 3:

#!/usr/bin/env python3

Detta är bra på ett par olika sätt:

  • det ger tips till din editor att det är just Python 3 och inte Python 2 du skriver (även mänskliga läsare: jag vet egentligen fortfarande inte om koden är Python 2 eller Python 3 bara av att titta på ditt exempel, men jag gissar på 3 pga att du använder input och har parenteser kring print).

  • i de flesta operativsystem gör detta skriptet direkt exekverbart med rätt tolk. Detta gäller även Windows för de som sitter med exempelvis MSYS2 eller liknande *nix-lika miljöer.

Direkt efter din shebang bör man i Python-världen skriva en docstring för denna modul (ungefär "fil med Python-kod"). Det gör man enligt konvention genom att bara skriva en sträng avgränsad med trippla citationstecken:

#!/usr/bin/env python3 """Perform arithmetic operations on user request."""

Skrivet av zeem:

#Functions for Arithmetic operations def num_add(x, y): result = x + y return result def num_sub(x, y): result = x - y return result def num_mul(x, y): result = x * y return result def num_div(x, y): result = x / y return result

Docstrings till dina funktioner, som sagt. Typiskt ska en docstring berätta vad funktionen gör samt beskriva in- och utparametrar. Ifall det är trivialt så brukar Python-communityt förespråka att skippa utläggningar om parametrarna, men det ska banne mig alltid vara en docstring som säger vad funktionen gör (OK, det finns ett fåtal fall där det går att välja bort (triviala "dunder"-funktioner, vanligen), men det är aldrig fel att ha en docstring).

Den översta kommentaren du har är rent krasst helt fel sätt att använda kommentarer . Om du känner att du behöver gruppera dessa funktioner så kanske de passar i en klass, där det du skrev som kommentar ovan passar utmärkt som en docstring till klassen i fråga.

I dessa triviala fall skulle jag skippa att mellanlagra resultatet innan retur.

Det finns ingen direkt poäng i att förkorta ord i funktions- och variabelnamn. Det är allt som oftast mer läsbart att inte behöva fundera på vad cnf_cc_abs egentligen betyder.

Ett förslag som innefattar alla dessa idéer.

def add(x, y): """Return arithmetic sum of parameters.""" return x + y def subtract(x, y): """Return arithmetic difference of parameters.""" return x - y def multiply(x, y): """Return arithmetic product of parameters.""" return x * y def divide(x, y): """Return arithmetic quotient of parameters.""" return x / y

eller om man vill gruppera funktionerna i en klass:

class Arithmetic: """Arithmetic operations.""" @staticmethod def add(x, y): """Return arithmetic sum of parameters.""" return x + y @staticmethod def subtract(x, y): """Return arithmetic difference of parameters.""" return x - y @staticmethod def multiply(x, y): """Return arithmetic product of parameters.""" return x * y @staticmethod def divide(x, y): """Return arithmetic quotient of parameters.""" return x / y

Här använde jag dekoratorn @staticmethod för att tydliggöra att funktionerna inte måste användas genom en instans av klassen, men det är inget att haka upp sig på. I detta fall kan jag tycka att klassen är överdriven — i Python ska man inte göra allting "objektorienterat" (i betydelsen "strössla klasser överallt") bara för att det går.

Skrivet av zeem:

#Function for entering the numbers that the user want to preform arithmetic operation on def input_num(): num = float(input("Enter a number: ")) return (num)

Tidigare tips gäller. Ett tillägg vad gäller felhantering passar här: vad händer om användaren matar in fel sak? Typiskt bör du fånga detta och be om input igen. Funktionen bör inte returnera förrän den vet att den returnerar något vettigt.

Här kan det kanske också passa med en default-parameter för promptmeddelandet — dels för att göra funktionen mer generell (vilket inte måste vara en bra sak, om man nu inte behöver det), men också för att det kan göra koden mer läsbar där den anropas.

def float_input(prompt="Enter a number"): """Return floating point number from a user prompt.""" while True: user_input = input('{}: '.format(prompt)) try: return float(user_input) except ValueError: print("Invalid input.")

Skrivet av zeem:

#Fuction for printing the meny def print_meny(): #Meny text for operation of calculator print("What do you want to do?\n")

Håll dig konsekvent till engelska ("meny" → "menu").

Vi kan också notera att denna funktion jobbar med globala variabler. Detta är en styggelse — det gör kod mycket svårare att följa, och det är en ovana man bör stävja tidigt. När du börjar nå stadiet med att skriva tester för din kod så kommer du förbanna varje stund du tänkt "äsch, det är ju rätt smidigt med globala variabler ändå". Det är inte bra praxis. Försök se skillnaden mellan vad en global variabel och en så kallad global konstant är, och när en konstant faktiskt är passande.

Med funktionen ovan har du ett typexempel på en funktion som skulle kunna ta exempelvis en lista (egentligen vad som helt som går att "iterera" över — Python är inte så kinkigt gällande exakt vilken typ det är) med menyval som skrivs ut automatiskt.

Skrivet av zeem:

for each_meny in meny: print(each_meny)

Jag skulle föredra en syntaxkonvention likt

for menu_item in menu_items:

dvs plural för samlingen av element och singular för iterationsvariabeln.

Att generera menyval passar sig också utmärkt för en standardfunktion i Python (det finns inte så många, så det är fullkomligt görbart att lära sig denna uppsättning) som heter enumerate. Denna kommer ge dig ett ordningstal tillsammans med varje iteration, så att du slipper baka in menynumret i själva strängen som ska beteckna valet. Något i stil med:

for i, menu_item in enumerate(menu_items, start=1): print('{}. {}'.format(i, menu_item))

Sannolikt så bör du även baka in din parsning av användarens input av menyval i samma funktion som den där du skriver ut menyvalen, för att ha kontroll över siffrorna. Det är möjligt att det är vettigt att splitta utskriften av hela menyn och inmatningen, men en användare av din funktion (t ex du själv där du vill få användaren att interagera med din meny) bör bara behöva göra ett funktionsanrop för att skriva ut menyn och få ett svar, kan jag tycka.


Vad gäller resten av programmet så håller jag med ovanstående talare om att huvudflödet ska logga i en funktion som heter main() som du kallar på ifrån en if __name__ == "__main__"-konstruktion. Detta är bra praxis av ett par olika anledningar som inte är helt lätta att se till en början, men dessa anledningar existerar definitivt: annars hade det inte varit praxis!

Kort sagt handlar det om att göra moduler importerbara utan att riskera märkliga sidoeffekter samt att hålla den globala namnrymden ren. Vill man mäta mikrosekunder så är variabelåtkomst av lokala variabler faktiskt också snabbare inne i en funktion än i den globala namnrymden, men det är inget direkt argument (dessutom kan overheaden för själva funktionsanropet potentiellt äta upp dessa mikrosekunder ändå).

Så, några summerande ord:

  • Använd docstrings.

  • Spana på de inbyggda funktionerna .

  • Undvik att ta in magiska parametrar bakvägen in i funktioner via globala variabler. Använder du explicita parametrar får du kod som är enklare att förstå, och inte minst enklare att testa.

  • "Brädgårdskommentarer" ska behöva användas mycket sparsamt. Docstrings (och logging-utskrifter, vilket inte nämnts än) bör täcka de allra, allra flesta fall. Brädgårdskommentarer kan generellt hålla sig till tekniska detaljer över en viss implementation, som man tänker sig inte är uppenbar ens för en van Python-programmerare. Märk väl att detta inte betyder att man inte ska dokumentera sin kod — tvärtom! Men gör det med docstrings, bra variabelnamn, genomtänkt struktur och funktioner som är väl avgränsade i sitt ansvarsområde och någorlunda korta.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem
Skrivet av oTiuZ:
Skrivet av phz:

Hej och tack så massor båda två!
Nu har jag massor att grotta ner mig i.

Av tipsen och riktlinjerna jag har fått är det här den jag inte får grepp om!

if __name__ == '__main__':

När jag har läst runt bland kod folk har postat så har jag inte riktigt sett det någonstans, jag kanske letat på felställen eller så skriver folk slarvig kod. Så långt jag har förstått kodsnutten så verkar det vara att om jag vill anropa mitt program_0.py från ett annat program_1.py så hjälper den kodsnutten mig. Blir det som att jag använder mig en class då?

program_0.method()?

Docstrings samt #
Det jag förstått är att docstrings följer med i programmet och #gör det inte. Jag kan se vinsten när jag felsöker eller vill förstå vad som händer och när. MEN Hur läser jag det? Måste jag lägga in en kodsträng för att docstrings skall skrivas ut eller är det ett separat program jag använder mig av i min utvecklingsmiljö?

Python generellt
När jag nu börjat pilla med python så det som slagit mig mest är fokus på läsbarhet och enkelhet. De andra språken jag pillat på har det inte varit en sådan tydlig fokus på att skriva tydligt och enkelt. Jag gillar det och märker att det är en konst att få till det snyggt. Som jag har använt mig av kommentering så är det för min egen skull. Att skriva en #bara för att försöka komma ihåg vad katten += var för något när jag kommer tillbaka till koden efter en tids uppehåll. Här kan jag inse att docstrings hjälper mycket om jag lyckas läsa dem.

Min miniräknare
En vidare utveckling kommer vara att försöka lägga in alla tips som ni har gett mig och snygga till det som ett mönsterprogram att återanvända till. Att lägga till en class för räknesätten var något jag funderade på men inte riktigt listade ut hur jag skulle göra men ligger högt på listan att lägga till

Jag tackar igen för alla tips och skall kämpa vidare. Kommer posta en uppdaterad kod när jag fått lite tid att koda vidare.

Visa signatur

Laptop: HP Elitebook 640 G9
Server: HP Microserver N54L, 8 GB ram, 8 TB hd.

Permalänk
Hedersmedlem
Skrivet av zeem:

Av tipsen och riktlinjerna jag har fått är det här den jag inte får grepp om!

if __name__ == '__main__':

När jag har läst runt bland kod folk har postat så har jag inte riktigt sett det någonstans, jag kanske letat på felställen eller så skriver folk slarvig kod. Så långt jag har förstått kodsnutten så verkar det vara att om jag vill anropa mitt program_0.py från ett annat program_1.py så hjälper den kodsnutten mig. Blir det som att jag använder mig en class då?

program_0.method()?

Poängen med

if __name__ == '__main__': main()

är att main() här bara anropas om du kör detta skriptet direkt med en Python-tolk, vilket sätter variabeln __name__ till just '__main__'. Om modulen i stället har importerats från en annan fil så är __name__ i stället modulnamnet.

Detta gör alltså att du kan importera en fil utan att den för den delen exekveras. Detta underlättar på egen hand testning, då du vid testning av en modul av nöden behöver importera modulen i fråga. Det gör även att du har en funktion main() som du kan anropa via modulnamn.main() vilket också gör denna kod testbar på ett enkelt sätt. För att ytterligare främja testbarhet så brukar mitt idiom i praktiken snarare vara

#!/usr/bin/env python3 """Docstring for the module.""" import sys ... def main(argv): """Main entry point.""" ... if __name__ == '__main__': main(sys.argv)

dvs en main-funktion som tar kommandoradsparametrar som argument. Med denna konstruktion kan du sedan i tester mata din main-funktion på enklaste sätt med olika parametrar.

Skrivet av zeem:

Docstrings samt #
Det jag förstått är att docstrings följer med i programmet och #gör det inte. Jag kan se vinsten när jag felsöker eller vill förstå vad som händer och när. MEN Hur läser jag det? Måste jag lägga in en kodsträng för att docstrings skall skrivas ut eller är det ett separat program jag använder mig av i min utvecklingsmiljö?

Ett sätt är att kalla på help(mitt_objekt) där mitt_objekt kan vara en klass, funktion, modul, instans, egentligen vad som helst, så får du upp en API-dokumentation för detta objekt. Här syns din docstring.

Detta kan vara användbart när man vill snabbundersöka ett objekt i den interaktiva tolken, men en annan stor fördel med docstrings är att de syns i de allra flesta IDE:er bara du hovrar över ett namn på en funktion/klass/etc. Om du automatgenererar dokumentation med exempelvis Sphinx så är det också docstrings som är basen till API-dokumenten. Detta är möjligt just för att introspektion av en modul direkt kan ge denna docstring som ett attribut tillhörande objektet, och saker som arv och liknande hanteras på det mest rimliga sättet. Brädgårdskommentarer aspirerar inte på att göra något av detta slag alls.

Skrivet av zeem:

Python generellt
När jag nu börjat pilla med python så det som slagit mig mest är fokus på läsbarhet och enkelhet. De andra språken jag pillat på har det inte varit en sådan tydlig fokus på att skriva tydligt och enkelt. Jag gillar det och märker att det är en konst att få till det snyggt. Som jag har använt mig av kommentering så är det för min egen skull. Att skriva en #bara för att försöka komma ihåg vad katten += var för något när jag kommer tillbaka till koden efter en tids uppehåll. Här kan jag inse att docstrings hjälper mycket om jag lyckas läsa dem.

Just under initial upplärning så kan brädgårdskommentarer spela en lite annorlunda roll, som du nämner: som en sorts personliga påminnelser om grundläggande syntax. Ganska snabbt så bör du dock märka att kod som

# Loopa över varje element i listan menu_items, tillsammans med ett iterationsindex i for i, menu_item in enumerate(menu_items): # Skriv ut i följt av menyvalet print('%d. %s' % (i, menu_item))

helt enkelt är jobbigare att läsa än om det bara hade stått

for i, menu_item in enumerate(menu_items): print('%d. %s' % (i, menu_item))

Med syntax highlighting så blir kommentarer mest en grå massa, och människor är enormt mycket snabbare på att genom mönsterigenkänning direkt koppla till vad koden gör. När du ser for så kopplar din gärna direkt till att det är en loop, när du ser enumerate så förstår du direkt att du vill hålla reda på ett iterationsindex, när du ser print och lite procenttecken (i detta fall, det finns många sätt att interpolera strängar på i Python (tyvärr)) så ser du direkt att det ska skrivas ut något till (sannolikt) skärmen. Brädgårdskommentarer gör det bara tyngre att läsa.

Ta dock inte detta som att "någon på ett forum sa att man inte borde kommentera sin kod" — det är absolut inte vad jag menar. Däremot bör man i Python sträva efter att kunna klara sig med bra docstrings, tydliga namn, använda vanliga idiom och bra uppdelningar av funktioner/klasser/moduler. Motsvarande saker gäller givetvis alla språk, men som du nämner så läggs det i Python-världen ofta extra mycket fokus på att skriva tydlig kod som mer eller mindre direkt ska kunna kännas uppenbar för en läsare.

Skrivet av zeem:

Min miniräknare
En vidare utveckling kommer vara att försöka lägga in alla tips som ni har gett mig och snygga till det som ett mönsterprogram att återanvända till. Att lägga till en class för räknesätten var något jag funderade på men inte riktigt listade ut hur jag skulle göra men ligger högt på listan att lägga till

Miniräknare i Python ger mig en flashback till när jag svarade i en tidigare tråd här på forumet angående ett program för att öva huvudräkning, där jag postade en variant som lekte lite med klasser. Det var ett tag sedan, men det verkar inte som att jag sagt emot mig själv alltför mycket sedan dess . Det svaret innehåller även fler tips liknande de som skrivits i denna tråd.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem

Hej!
Hamnade i en tankeloop som jag inte kunde komma ur så jag började om från scratch. Nu försöker jag implementera så mycket det går av tips och råd. Har framförallt försökt få till input värdena så det inte strular. Dock tycker jag det blir mycket kod men kanske inte går att göra snyggare. Såg någonstans att man kan importera math men då tycker jag läsbarheten för en nybörjare försvinner.

Iaf så är jag sjukt trött men koden ser ut såhär just nu med felsökningsgrejer och allt:
Todo:
name main grejen
alla räknesätten
förolämpning,insult
kolla runt i IDLE för att läsa docstring

#!/usr/bin/env python3 """Functions for Arithmetic operations""" def add(x, y): result = x + y return result def subtract(x, y): result = x - y return result def multiply(x, y): result = x * y return result def divide(x, y): result = x / y return result def input_number(): """Function for user input in arithmetic operations""" while True: try: number = input('Enter a number: ') number = float(number) return number except ValueError: print('That is not a number') def menu(): """Function for handeling user input for main menu and printing it""" menu =["Addition", "Subtraction", "Multiplication", "Division", "Quit"] insult = 0 for i, each_menu in enumerate(menu, start=1): print('{}. {}'.format(i, each_menu)) while True: try: menu_choice = input('Enter a number: ') menu_choice = int(menu_choice) #break except ValueError: print('Please choose a number') insult += 1 print('insult ' + str(insult)) else: if 1 <= menu_choice <= 5: break else: print('Please choose a value in menu range') print('User input ok') if menu_choice == 1: print(menu[0]) return(menu[0]) elif menu_choice == 2: print(menu[1]) return(menu[1]) elif menu_choice == 3: print(menu[2]) return(menu[2]) elif menu_choice == 4: print(menu[3]) return(menu[3]) elif menu_choice == 5: print(menu[4]) return(menu[4]) else: insult += 1 print(insult) return('ok') while True: """Main program loop""" choice = menu() if choice == 'Quit': break elif choice == 'Addition': print(add(input_number(), input_number())) print('bye')

Visa signatur

Laptop: HP Elitebook 640 G9
Server: HP Microserver N54L, 8 GB ram, 8 TB hd.

Permalänk
Medlem

@zeem Jag tycker inte funktionerna add, multiply osv. tillför värde eller läsbarhet till koden. Små funktioner är förvisso vackra men i detta fall finns redan "funktionen" inbyggd i ett enkelt uttryck som x + y

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem
Skrivet av Teknocide:

@zeem Jag tycker inte funktionerna add, multiply osv. tillför värde eller läsbarhet till koden. Små funktioner är förvisso vackra men i detta fall finns redan "funktionen" inbyggd i ett enkelt uttryck som x + y

Hej! Tackar för din input!

Jag håller med dig, och det som är lurigt att få in i huvudet är fokusen på läsbarhet. Vad är enkel kod att läsa? Det blir så mycket bättre när andra läser koden och skriver vad de tycker. När jag sitter och skriver koden blir jag lätt hemmablind och fattar vad jag menar vilket inte är det enklaste alla gånger.

Jag kan skriva lite hur min tankegång var och är om dessa funktioner ifall det är någon som är nyfiken eller sitter i samma båt och vill lära sig python.

Syftet med hela miniräknaren är att lära sig python när jag satte mig ner och äntligen fick tid över till att jobba med den hade jag läst om function. Har inte riktigt greppat hur function och class har hängt ihop i C++ och tänkte att om jag leker här i python så kanske polletten trillar ner och jag kan överföra det till mina Arduino projekt. Så helt sonika gjorde jag så mycket def function som jag kunde komma på. Det som hela tiden ligger och gnager i bakhuvudet är "mer än 25 rader kod då kanske det skall delas upp i funktioner"(vet inte vad jag fått det ifrån). Så nu när jag nästan fått grepp om funktioner så inser jag att de ovanstående funktionerna inte direkt fyller något syfte och kanske för villar men var nyttigt att skriva för lärandet skull. Det jag tänker just nu med funktionerna är om jag vill låta användaren mata in fler siffror än 2st, hur skall jag lösa det? Jag vet inte men jag kanske behöver dessa funktioner då eller inte. Vidare så har jag tänkt att dessa funktioner är ypperliga att slå ihop till en class som jag kan anropa för att lära mig class-skapande. Det känns som en enkel och tydlig klass med metoderna add, multiply ...

Åter igen tack för ditt inlägg

Visa signatur

Laptop: HP Elitebook 640 G9
Server: HP Microserver N54L, 8 GB ram, 8 TB hd.

Permalänk
Medlem
Skrivet av zeem:

Hej! Tackar för din input!

Jag håller med dig, och det som är lurigt att få in i huvudet är fokusen på läsbarhet. Vad är enkel kod att läsa? Det blir så mycket bättre när andra läser koden och skriver vad de tycker. När jag sitter och skriver koden blir jag lätt hemmablind och fattar vad jag menar vilket inte är det enklaste alla gånger.

Jag kan skriva lite hur min tankegång var och är om dessa funktioner ifall det är någon som är nyfiken eller sitter i samma båt och vill lära sig python.

Syftet med hela miniräknaren är att lära sig python när jag satte mig ner och äntligen fick tid över till att jobba med den hade jag läst om function. Har inte riktigt greppat hur function och class har hängt ihop i C++ och tänkte att om jag leker här i python så kanske polletten trillar ner och jag kan överföra det till mina Arduino projekt. Så helt sonika gjorde jag så mycket def function som jag kunde komma på. Det som hela tiden ligger och gnager i bakhuvudet är "mer än 25 rader kod då kanske det skall delas upp i funktioner"(vet inte vad jag fått det ifrån). Så nu när jag nästan fått grepp om funktioner så inser jag att de ovanstående funktionerna inte direkt fyller något syfte och kanske för villar men var nyttigt att skriva för lärandet skull. Det jag tänker just nu med funktionerna är om jag vill låta användaren mata in fler siffror än 2st, hur skall jag lösa det? Jag vet inte men jag kanske behöver dessa funktioner då eller inte. Vidare så har jag tänkt att dessa funktioner är ypperliga att slå ihop till en class som jag kan anropa för att lära mig class-skapande. Det känns som en enkel och tydlig klass med metoderna add, multiply ...

Åter igen tack för ditt inlägg

Hej! Jag har lite feedback på din kod. Jag är osäker på om du vill ha "du kan skriva såhär istället" eller bara riktlinjer, så jag kommer inte skriva ut någon kod som direkt relaterar till din miniräknarmodul. Har heller inte läst igenom de andra kommentarerna jättenoga, så jag ber om ursäkt om jag upprepar någonting. Notera även att det här är toppat med mina personliga åsikter av fin kod, så ta det (och alla andra tips du någonsin får om kod) med en nypa salt, det finns sällan en absolut sanning.

  • Som någon nämnde är dina funktioner för aritmetiska operationer onödiga, då de redan existerar som operatorer. Jag förstod inte riktigt av ovanstående citat om du skrev dem endast för att träna, eller om du skrev dem för att ha användning av dem. Om det är för det sistnämnda, så finns alla aritmetiska operationer som funktioner i operator-modulen (e.g. operator.add och operator.sub). Python har ett ENORMT standardbibliotek som är otroligt värt att bekanta sig nära med. Men om funktionerna bara är skrivna för träningens skull, för all del, ha kvar dem. Var bara medveten om att man inte skulle ha sådana funktioner "på riktigt".

  • Undvik att definiera flera symboler i samma namespace med samma namn. Du har i funktionen menu en lista som också heter menu, vilket skriver över menu-funktionens namn i funktionens kontext. Nu behöver du visserligen inte funktionens namn i dess egen kontext i det här fallet, men det är bad practice att göra på det här sättet och minskar läsbarheten (jag reagerade på att koden försökte plocka ut ett element ur något jag trodde var en funktion).

  • Din långa if/elif i funktionen menu kan förenklas. Det finns egentligen bara två fall du är intresserad av: antingen så är inputen i range [1, 5], och då tar du printar och returnerar element nr menu_choice - 1. Annars så är inputen någonting annat, och då printar du en förolämpning. Note: På grund av din input-check i while-loopen precis ovanför så får input endast vara inom range [1, 5].

  • menu-funktionen är för stor. Försök följa riktlinjen att en funktion ska göra en sak!, och alla delmoment av den saken bör abstraheras till funktioner. Just nu ser jag att menu gör följande:

    1. Skapar meny-listan

    2. Printar meny-listan

    3. Hämtar user input

    4. Gör input checking

    5. Utför någon handling beroende på user input

    Kan nämna att jag personligen skulle ta och göra meny-listan till en global konstant, och de fyra andra sakerna till varsin egen funktion, som binds ihop av menu-funktionen.

  • Undvik while true break-konstruktionerna. Det är tydligare att ha någonting i stil med:

    loop_condition = true while loop_condition: if some_condition: # do something loop_condition = false # loop will exit! else: # do something else # loop will keep going!

    loop_condition bör döpas till någonting relevant i kontexten, och kan negeras med ett not. E.g. i din funktion input_number skulle du kunna lira på en boolean-variabel input_is_ok och loopa på

    while not input_is_ok:

    .

  • Om det är möjligt, returnera i slutet av funktionen. Exempelvis i input_number, om du gör som jag föreslog med loopar ovan, så kan du returnera efter loopen istället. Det blir då i min åsikt lättare att direkt se vad man vill göra med funktionen.

  • Lägg aldrig kod löst i modulen (såsom din main-loop). Det stora problemet med att göra detta är att om du importerar modulen så kommer den koden att exekveras. Gör du som tipsats tidigare och lägger det i en main-funktion, och lägger till

    if __name__=="__main__": main()

    så slipper man detta, då modulen endast får namnet main om det är den modulen som man kör med interpreteraren (dvs, python-programmet).

Om du vill att jag ska utveckla på något av detta med mer kod-exempel som relaterar till din kod är det bara att hojta till

Permalänk
Hedersmedlem
Skrivet av zeem:

def input_number(): """Function for user input in arithmetic operations""" while True: try: number = input('Enter a number: ') number = float(number) return number except ValueError: print('That is not a number')

Vad gäller din docstring så är det redan tydligt att det är en funktion, så skriv inte "Function for…" eller liknande. Din nuvarande docstring säger ju faktiskt inte vad funktionen gör eller returnerar, utan bara vad den relaterar till. Se PEP 257 -- Docstring conventions; specifikt

Citat:

The docstring is a phrase ending in a period. It prescribes the function or method's effect as a command ("Do this", "Return that"), not as a description; e.g. don't write "Returns the pathname ...".

Vad gäller koden så ska du alltid sträva efter att låta try: ... except: ... fängsla en så isolerad mängd kod som möjligt. Du förväntar dig inte att input- eller return-raden ska slänga ValueError, så de bör inte ligga innanför din try.

Omskrivet:

def input_number(): """Return float from user input.""" while True: number = input('Enter a number: ') try: return float(number) except ValueError: print('That is not a number')

  • input har flyttats ut

  • De resterande två raderna i try-blocket kan lika gärna slås ihop i ett trivialt fall. Om float-operationen misslyckas så kommer det inte bli någon return, utan loopen kommer fortsätta.

Skrivet av zeem:

def menu(): """Function for handeling user input for main menu and printing it""" menu =["Addition", "Subtraction", "Multiplication", "Division", "Quit"] insult = 0 for i, each_menu in enumerate(menu, start=1): print('{}. {}'.format(i, each_menu)) while True: try: menu_choice = input('Enter a number: ') menu_choice = int(menu_choice) #break except ValueError: print('Please choose a number') insult += 1 print('insult ' + str(insult)) else: if 1 <= menu_choice <= 5: break else: print('Please choose a value in menu range') print('User input ok') if menu_choice == 1: print(menu[0]) return(menu[0]) elif menu_choice == 2: print(menu[1]) return(menu[1]) elif menu_choice == 3: print(menu[2]) return(menu[2]) elif menu_choice == 4: print(menu[3]) return(menu[3]) elif menu_choice == 5: print(menu[4]) return(menu[4]) else: insult += 1 print(insult) return('ok')

Menyn är inte bra i nuläget, tycket jag. Du ser att du upprepar dig själv en mängd gånger i elif-stycket i slutet — det är en ledtråd om att något inte står rätt till. Du har redan information om vilket menyval som hör till vilket index utifrån ditt enumerate-anrop: du ska inte likväl behöva hårdkoda dessa index som du gör på slutet, för då är hela poängen med att du dynamiskt genererar menyn och dess index borta.

Kort sagt så tycker jag att du borde sträva efter att kunna lägga till ett menyval och vad det ska exekvera enbart genom att lägga till ett element i din ursprungliga menu-lista. Jag tycker också att denna lista skulle kunna tas som ett argument till funktionen, så att du skriver en fullkomligt generell funktion för att skapa en lista.

En annan notis är att return inte ska ha parenteser runt sitt argument (det är ett "statement" och inte en funktion). Det känns heller inte elegant att returnera magiska strängar på detta sätt för att göra något.


I övrigt bra tips från alla i tråden. Det var två saker jag inte kunde undvika att kommentera:

Skrivet av SimpLar:
  • Undvik while true break-konstruktionerna. Det är tydligare att ha någonting i stil med:

    loop_condition = true while loop_condition: if some_condition: # do something loop_condition = false # loop will exit! else: # do something else # loop will keep going!

Detta beror på vilka idiom som är vanliga i olika språk, men jag skulle gå så långt som att säga att while True: … break med råge är det vanligaste alternativet i Python ([citation needed] — utifrån någon sorts sammantagen bild av standardbiblioteket, vanliga tredjepartsbibliotek, litteratur, etc.) i liknande fall, snarare än att skapa extravariabler som sätts till True/False mitt i loopen för att sedan utvärderas i början och sedan hoppa ur loopen. Dubbelt mycket så när det handlar om funktioner där return används för att bryta flödet (vilket också är vanligt för att kunna breaka ur flera loopblock på en gång).

Är man inte bekväm med while True: … break-konstruktionen så är man inte tvingad att använda den, men jag skulle likväl hävda att det är den typ av konstruktion jag mest förväntar mig att hitta i Python-kod när det handlar om dessa loopar. Varianten med loop_condition skulle jag behöva lägga mer tid på att läsa än varianten med break.

Skrivet av SimpLar:
  • Om det är möjligt, returnera i slutet av funktionen. Exempelvis i input_number, om du gör som jag föreslog med loopar ovan, så kan du returnera efter loopen istället. Det blir då i min åsikt lättare att direkt se vad man vill göra med funktionen.

Detta känns relaterat till ovanstående notis om while True: … break. Att enbart ha en enstaka return i en funktion eller ej är något som kan trigga religiösa krig, och även om jag kan se poängen i språk som C (typiskt språk utan exceptions samt mer manuell resurshantering) så ser jag även där allt som oftast multipla utvägar ur funktioner, med tidiga guard clauses för att snabbt returnera felkoder vid dålig input som typexempel (alternativet att behöva ha ett if-block med en extra indentering som löper med hela funktionen är rätt anskrämligt :-) — praktiskt exempel, bloggpost på temat).

Ska jag säga något luddigt så känner att då man i språk med exceptions ändå implicit har potentiella exit points mer eller mindre överallt så är det lite av en falsk säkerhet med en unik utväg sist i funktionen. Faktiskt så används "guard clauses" i sådana språk (exempelvis just Python) än mer till programmerarens fördel: genom att koncentrera kastandet av undantag till absoluta början av koden så kan läsaren fokusera bättre på logiken i huvuddelen av koden, eller i andra ord: genom att lägga till avslut (typiskt tidiga) så minskar man problemet med att ha flera vägar ur koden.

Något mer konkret är notisen om manuell resurshantering: i språk utan detta kan multipla utvägar vara svåra att skriva på ett sätt som både är läsbart och korrekt (undviker att lämna skräp) samtidigt. Det är inte förvånande att det, utöver tidigare nämnda fall, gärna undviks i just C.

Jag säger inte att man ska förespråka multipla utvägar mitt i funktioner i allmänhet, men jag säger definitivt att det finns tillfällen då det underlättar logiken i koden betydligt, och därmed också läsbarheten. En av grundtankarna i Python är att man i sådana lägen ska låta läsbarhet gå före någon sorts tänkt renhet och inte vara rädd för att använda det. Som jag nämnde ovan så exempelvis fallet med att bryta sig hela vägen ur nästlade loopar ett typexempel i Python, men även just input-snurror som brutits ut till separata funktioner. Detta gör att sådana konstruktioner nått hela vägen för att rättmätigt kunna kallas idiom (eller "Pythonic", om man så vill), vilket i sig gör konstruktionerna läsbara: en läsare som ser konstruktionen ofta kan också snabbare se intentionen.

Samlar ihop lite länkar på temat som jag stött på både tidigare och nu när jag försökte hitta både exempel och motexempel till "early return" och oändliga loopar med break:

  • Should a function have only one return statement? [SO] — Bra diskussioner. Inte minst citerar ett svar ur Code Complete:

    Citat:

    17.1 return

    Minimize the number of returns in each routine. It's harder to understand a routine if, reading it at the bottom, you're unaware of the possibility that it returned somewhere above.

    Use a return when it enhances readability. In certain routines, once you know the answer, you want to return it to the calling routine immediately. If the routine is defined in such a way that it doesn't require any cleanup, not returning immediately means that you have to write more code.

    Ibland känns det som att "minimize" tolkas som "aldrig mer än en!", och att andra stycket skippas helt — inte minst brasklappen om huruvida resurser behöver städas eller ej.

  • Where did the notion of “one return only” come from? [Programming.SX] — Väldigt intressant tråd i ämnet i mina ögon. Båda av de två högst rankade svaren är träffsäkra, exempelvis:

    Citat:

    Of course, a 200 LoC function with half a dozen return statements sprinkled randomly over it is not good programming style and does not make for readable code. But such a function wouldn't be easy to understand without those premature returns either.

    som påvisar att det ofta inte är multipla utvägar som är problemet för läsbarhet, utan funktionens själva avgränsning och struktur till att börja med. Även:

    Citat:

    So in languages where resources are not or should not be managed manually, there is little or no value in adhering to the old SESE ["single entry, single exit"] convention. OTOH, as I have argued above, SESE often makes code more complex. So it is a dinosaur that (except for C) does not fit well into most of today's languages. Instead of helping the understandability of code, it hinders it.

  • Loop Exits and Structured Programming: Reopening the Debate [Eric S Raymond] — Lägger ut lite om historiken kring varför break fick "dåligt rykte" i samma veva som Dijkstras GOTO considered harmful [PDF], även om denna koppling inte verkar bygga på några empiriska grunder.

  • Are “while(true)” loops so bad? [SO] — Handlar om Java, vilket visserligen är lite annorlunda, men det accepterade svaret berör också likheterna mellan break och "early return", och nämner hur break kan förenkla läsbarheten av kod. Jag gillar även Jon Skeets kommentar om hur han gillar att ha kod så oindenterad som möjligt: det tycker jag personligen är en viktigare riktlinje för att hålla kod läsbar, och om break eller "early return" kan hjälpa till med detta så skulle jag inte tveka att använda det.

  • while True-förekomster i CPython.

  • Announcing PEP 3136 — Ett förslag inför Python 3 om att införa ny syntax för att kunna hoppa ur nästade loopar, vilket nekas av Guido himself som föreslår användandet av return som en "existing work-around that produces clean code".

  • How to break out of multiple loops in Python? [SO] — Accepterade svaret "My first instinct would be to refactor the nested loop into a function and use return to break out." känns väldigt "Python", och alla andra varianter som föreslås med exceptions och annat bör ju inte kunna sägas öka någon läsbarhet, precis.

Dagens exkursion avslutas härmed. Alltid lärorikt att ge sig ut på liknande historiska utflykter .

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem

@phz: Jag ger med mig på att sådan användning av break och early returns är mer pythoniskt och att det kan ge god läsbarhet om det används på rätt sätt. Jag skulle gärna försvara mina ståndpunkter för en intressant diskussion, men jag känner att det inte tillför TS särskilt mycket, så jag avstår. Bra inlägg!

Permalänk
Medlem

Hej tråden!

Vill tacka för alla inputs det är hjärtvärmande att läsa. Vet inte hur många gånger jag läst inläggen (finns säkert någon statistik som går att läsa) Det är synd att det finns så lite tid på ett dygn och så mycket annat som måste ordnas med men tänkter får jag bara till en hyfsad grund så ger jag mig i kast med lite riktiga projekt

Det som hänt med min miniräknare just nu är att jag försöker få in alla tips och råd. Det stora med just den här varianten var att skapa en class och använda den. Det som jag också ville prova var att lägga till classens metoder i varsin variabel.

addition = Arithmetic.add

Jag ville även få name main att fungera. Det fungerar men jag vet inte om den ligger rätt i koden d.v.s. skall den ligga sist eller först i koden? eller för den delen så vet jag inte om den fungerar rätt.

if __name__=="__main__": """A simple calculator that uses Arthritmic on two numbers from user inputs""" main()

Jag försökte även att rätta upp kommentarerna till endast docstrings.

Jag hastade igenom enumerate funktionen märker jag nu. Får läsa på mer om just den funktionen och hur jag kan använda den på ett bättre sätt än att bara skriva ut siffrorna i min list. (trodde det kallades array).

for i, each_menu in enumerate(menu, start=1): print('{}. {}'.format(i, each_menu))

Globala konstanter: Här har jag en fråga. Bör jag skapa den som en Tuple? I mitt fall

menu = [+, -, *,/,]

och ifall jag bestämmer mig för att skapa en global konstant skall den vara i def main() eller utanför?

Mina tankar inför fortsättningen:
* menu() funktionen behöver en omarbetning som ni skrivit. Det som jag blir låst av här är att jag nog inte fullt förstått vad jag kan göra i python t.ex. enumerate. En mentallåsning för mig blir att den fungerar faktiskt, hade varit enklare om den inte fungerade
*funktionerna för de fyra räknesätten har nog nu spelat ut sin roll iallafall för tillfället och ger mig en chans att antigen ge mig in i math bilioteket eller helt enkelt använda mig av en ny funktion men tror att jag kommer hålla mig i standard biblioteket. Här skall ju en tanke komma om räknesätten skall vara i olika funktioner eller om de skall vara i samma. Funktionen kan ju vara utför något av de fyra räknesätten på siffrorna.

Nu får det nog bli lite sova tror jag och tack igen för alla svar

#!/usr/bin/env python3 class Arithmetic: """Arithmetic operations""" def add(x, y): """Arithmetic operation addition""" result = x + y return result def subtract(x, y): """Arithmetic operation subtraction""" result = x - y return result def multiply(x, y): """Arithmetic operation multiplication""" result = x * y return result def divide(x, y): """Arithmetic operation division""" result = x / y return result def input_number(): """User input for arithmetic operations""" while True: number = input('Enter a number: ') try: return float(number) except ValueError: print('That is not a number') def menu(): """Handeling user input for main menu and printing it""" menu =["Addition", "Subtraction", "Multiplication", "Division", "Quit"] insult = 0 for i, each_menu in enumerate(menu, start=1): print('{}. {}'.format(i, each_menu)) while True: menu_choice = input('Enter a number: ') try: return int(menu_choice) except ValueError: print('Please choose a number') insult += 1 print('insult ' + str(insult)) else: if 1 <= menu_choice <= 5: break else: print('Please choose a value in menu range') print('User input ok') if menu_choice == 1: print(menu[0]) return menu[0] elif menu_choice == 2: print(menu[1]) return menu[1] elif menu_choice == 3: print(menu[2]) return menu[2] elif menu_choice == 4: print(menu[3]) return menu[3] elif menu_choice == 5: print(menu[4]) return menu[4] else: insult += 1 print(insult) return('ok') def main(): """Main program loop""" exit_program = False addition = Arithmetic.add subtraction = Arithmetic.subtract multiplication = Arithmetic.multiply division = Arithmetic.divide while exit_program != True: choice = menu() if choice == 'Quit': exit_program = True elif choice == 'Addition': print(addition(input_number(), input_number())) elif choice == 'Subtraction': print(subtraction(input_number(), input_number())) elif choice == 'Multiplication': print(multiplication(input_number(), input_number())) elif choice == 'Division': print(division(input_number(), input_number())) print('bye') if __name__=="__main__": """A simple calculator that uses Arthritmic on two numbers from user inputs""" main()

Visa signatur

Laptop: HP Elitebook 640 G9
Server: HP Microserver N54L, 8 GB ram, 8 TB hd.

Permalänk
Hedersmedlem
Skrivet av zeem:

Jag ville även få name main att fungera. Det fungerar men jag vet inte om den ligger rätt i koden d.v.s. skall den ligga sist eller först i koden? eller för den delen så vet jag inte om den fungerar rätt.

if __name__=="__main__": """A simple calculator that uses Arthritmic on two numbers from user inputs""" main()

Det stycket ska ligga sist, både av konvention och ren praktikalitet (två saker som allt som oftast hänger ihop, även om man kanske inte ser varför en viss konvention finns innan man sett vad alternativen innebär). Funktionen main måste ha definierats innan koden ovan stöts på, så om man ser till att ovanstående kod ligger sist (och återigen av konvention så brukar main-funktionen då ligga precis innan, dvs "näst sist").

Jag nämnde även Pylint och pycodestyle i ett tidigare inlägg i tråden. Dessa verktyg hade då klagat på avsaknad av mellanslag ovan runt ==-operatorn. Jag skulle även lägga docstringen på main-funktionen och inte inne i if-satsen ovan: där får den ingen speciell betydelse (dvs den exponeras inte som något __doc__-attribut), och idiomet är så pass välkänt att det inte kräver vidare förklaring.

Sammantaget:

def main(): """Din docstring här.""" ... if __name__ == "__main__": main()

Skrivet av zeem:

Jag hastade igenom enumerate funktionen märker jag nu. Får läsa på mer om just den funktionen och hur jag kan använda den på ett bättre sätt än att bara skriva ut siffrorna i min list. (trodde det kallades array).

Olika språk väljer sin terminologi på lite olika sätt, men i Python så finns även datatypen array, fast den är "gömd" i en modul och i praktiken väldigt sällan använd. Den största funktionella skillnaden mellan en array och en lista i Python är att en array bara kan innehålla en datatyp åt gången från en samling av vissa fundamentala datatyper (i praktiken är det en rätt tunn wrapper över C-lika arrayer), där en lista kan innehålla vilka objekt som helst, och även kan blanda objekttyper (även om det kanske inte alltid är rekommenderat av andra skäl).

I praktiken så hajar jag till ordentligt om jag ser någon använda array-modulen i Python — då antar jag att det är något märkligt på gång i stil med hårda minneskrav eller gränssnitt mot rena C-utökningar. Ifall någon använder array för prestandakrävande beräkningar så borde man nog titta på NumPy och dess egna arrayer direkt i stället.

Generellt sett så bör listor användas för att samla objekt av samma typ (homogena samlingar) där man rent teoretiskt skulle kunna tänka sig att man kan lägga till eller ta bort objekt, samtidigt som samma objekt potentiellt kan förekomma multipla gånger och/eller ordningen av objekten skulle kunna vara av intresse. Ett exempel är en lista av dagstemperaturer, som kan växa med tiden.

Som du säkert märkt känns en tuple rätt lik en lista, men den används annorlunda. Dels är en tuple "omuterbar", dvs när den väl är skapad så är den "klar" — det går inte att lägga till eller ta bort element. Detta har effekten att man kan använda en tuple som en nyckel i en hashtabell (se set och dict), men också att datatypen är naturlig för samlingar av data där ordningen bär en semantisk betydelse.

Tupeln har vissa konceptuella analoger med en struct i C, på så sätt att du per konvention kan skapa en inhomogen datastruktur där du vet att första elementet bär en viss typ av information, andra något annat, etc. Det går att använda en tuple som exempelvis en mätpunkt av temperatur, luftfuktighet och vindhastighet. Notera att datatyperna inte är homogena på så sätt att de har olika enhet (kanske °C, procent respektive en vektor med storlek och riktning, och kanske en tidsstämpel därtill) och det är inte vettigt att plötsligt lägga till ett värde i en skapad tuple, då en mottagare förväntar sig hitta just ett visst mätvärde på en viss position. Dessa tuplar kanske sedan i sin tur lagras i en lista med mätvärden över tid.

En tuple i Python kan efter överenskommelse om ett protokoll ses som en typ av record. Se även collections.namedtuple som gör ett sådant protokoll än mer tydligt (en datatyp som används alldeles för sällan ).

Dessutom har en tuple i Python egentligen ytterligare ett användningsområde, som snarast skulle kunna kallas frozenlist: dvs en lista som "frusits", dvs gjorts omuterbar. Jag hade personligen kunnat önska att detta var ett standardlib-alias för tuple i Python för att i rent pedagogiskt syfte kunna skilja på dessa två uppgifter, men men.

När man nämner listor kan man ofta även nämna datatypen "mängd" (set). Om du har en lista där ordningen inte spelar roll, det inte finns någon mening med multipla instanser av samma objekt i samlingen, medlemmarna är hashbara och det kanske dessutom är av intresse med upprepade medlemskapstester ("finns detta objekt i mängden?") så är ett set mest naturligt. Extremt nära besläktat en Pythons naturliga hashtabell under namnet dict, som konceptuellt är som en mängd, men där varje mängdmedlem även pekar på ett objekt. Historiskt i Python så kom dict först, och set implementerades helt enkelt som en dict där man ignorerade tillhörande värden, men det är i vilket fall nyttigt att veta släktskapet.

Man bör tidigt se till att skaffa sig lite koll på list, tuple, set och dict, då insikt om dessa datatyper ständigt betalar sig — inte minst gällande dess begränsningar så att man vet när man bör gå över till en deque, heap, etc.

Skrivet av zeem:

Globala konstanter: Här har jag en fråga. Bör jag skapa den som en Tuple? I mitt fall

menu = [+, -, *,/,]

och ifall jag bestämmer mig för att skapa en global konstant skall den vara i def main() eller utanför?

Enligt ovanstående resonemang så skulle jag välja datatypen lista för en sådan samling av operatorer på grunden att det är en homogen lista av objekt. Om vi inte kodar världens mest resurssnåla system på ett inbyggt system med extrema minneskrav (men då lär man inte dra igång en Python-tolk till att börja med… ) så tycker jag det är mycket viktigare att välja den konceptuellt mest passande datatypen. Det kanske till och med vore vettigt med ett set här — fundera gärna på skillnaden.

Om du skapar en konstant inne i en funktion så blir den per definition lokal och inte global. Per konvention brukar konstanter deklareras direkt under modulimporter, innan klasser och funktioner — ytterligare en av alla dessa konventioner som kommer från praktiska funderingar.

Skrivet av zeem:

*funktionerna för de fyra räknesätten har nog nu spelat ut sin roll iallafall för tillfället och ger mig en chans att antigen ge mig in i math bilioteket eller helt enkelt använda mig av en ny funktion men tror att jag kommer hålla mig i standard biblioteket. Här skall ju en tanke komma om räknesätten skall vara i olika funktioner eller om de skall vara i samma. Funktionen kan ju vara utför något av de fyra räknesätten på siffrorna.

Se tidigare tipset om operator-modulen. Det finns en implementation som använder denna modul i den tidigare tråd jag länkade (se sista spoilern).

Stavning ("funktionalla" → "funktionella")
Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem

@phz: Tackar för inlägget! Jag märker att det är väldigt mycket för mig att ta in just nu men tror jag gör framsteg. Inatt har jag fastnat på att försöka lösa meny funktionen. Jag har även upptäckt att jag en förutfattad mening om python att det skall gå att göras på så himla få rader. Det ställer till det för mig, för jag blir lite "orolig i kroppen" när jag skrev ner inputs kontrollen med valueError och sedan att det blir så grymt mycket funktioner som jag kallar på hela tiden. Hade jag suttit med mina Arduino så hade jag inte fått den känslan dock blir jag begränsad av sådant som jag inte blir begränsad av på datorn.

Läste ditt inlägg nyss och hittade spoiler längst ner. Får försöka kolla på det imorgon och se hur en lösning på menu kan se ut, för som jag gör det just nu vet jag inte om enumerate fyller någon funktion annat än för inlärning.

Något som jag inser är att jag inte är bra på att döpa variabler och funktioner vilket nämnts tidigare i tråden. Får sova lite på den saken.

Iaf så här långt kom jag denna gång med min kod. Tror jag har börjat om 3ggr nu. Skönt att det bara finns ett sätt att lösa problemet miniräknare för två tal på.

#!/usr/bin/env python3 def Arithmetic(x, y): """Arithmetic operations.""" def number_input(): """User input for arithmetic operations.""" while True: number = input('Enter a number: ') try: return float(number) except ValueError: print('That is not a number') def menu(menu_choise): """Handeling main menu.""" menus =["Addition", "Subtraction", "Multiplication", "Division", "Quit"] if menu_choise == 6: for i, each_menu in enumerate(menus, start=1): print('{}. {}'.format(i, each_menu)) else: print(menus[(menu_choise - 1)]) def menu_input(): """User input for users menu choice""" while True: menu_choice = input('Enter a number: ') try: int(menu_choice) except ValueError: print('Please choose a number') else: menu_choice = int(menu_choice) if 1 <= menu_choice <= 5: return menu_choice else: print('Please choose a value in menu range') def main(): """A simple calculator that uses Arthritmic on two numbers from user inputs""" menu(6) menu(menu_input()) print('bye') if __name__=="__main__": main()

Min kod

Yes! äntligen fattade jag hur jag gör en spoiler. Tänk om jag kanske skulle lära mig sådant där dataspråk kanske.

Visa signatur

Laptop: HP Elitebook 640 G9
Server: HP Microserver N54L, 8 GB ram, 8 TB hd.

Permalänk
Medlem

Hej tråden!

Nu har jag knåpat lite och tycker det ser mycket bättre ut än hur det började.

Det jag är orolig för är när jag använder varibel namn som används i flera funktioner med samma namn(menu_choise). I mitt fall hoppas jag det blir enklare att följa vart värdet i variablen tar vägen. Dock är det ju ingen global variabel så det kanske förvillar mer än det underlättar. Är osäker här hur jag skall tänka.

Jag fick rådet att titta på operator modulen, men jag förstår inte vad jag tjänar på att importera den istället för att skriva ett enkelt +.

import operator operator.add(x, y) vs x + y

Det jag kan lägga till är vad jag vill göra om någon vill dividera med 0, men börjar känna mig rätt trött på miniräkaren.

Tackar åter igen för alla tips jag har fått i denna tråd.

#!/usr/bin/env python3 def arithmetic(op, x, y): """Arithmetic operations""" print('test' + op) if op == 'Addition': result = x + y elif op == 'Subtraction': result = x - y elif op == 'Multiplication': result = x * y elif op == 'Division': result = x / y print(result) def number_input(): """User input for arithmetic operations.""" while True: number = input('Enter a number: ') try: return float(number) except ValueError: print('That is not a number') def menu(menu_choise): """Handeling main menu.""" menus =["Addition", "Subtraction", "Multiplication", "Division", "Quit"] if menu_choise == 6: for i, each_menu in enumerate(menus, start=1): print('{}. {}'.format(i, each_menu)) else: print(menus[(menu_choise - 1)]) return menus[(menu_choise -1)] def menu_input(): """User input for users menu choice""" while True: menu_choice = input('Enter a number: ') try: int(menu_choice) except ValueError: print('Please choose a number') else: menu_choice = int(menu_choice) if 1 <= menu_choice <= 5: return menu_choice else: print('Please choose a value in menu range') def main(): """A simple calculator that uses Arithmetic on two numbers from user inputs""" while True: menu(6) arithmetic_operation = menu(menu_input()) if arithmetic_operation == 'Quit': break arithmetic(arithmetic_operation, number_input(), number_input()) print('bye') if __name__ == "__main__": main()

Nya koden
Visa signatur

Laptop: HP Elitebook 640 G9
Server: HP Microserver N54L, 8 GB ram, 8 TB hd.

Permalänk
Medlem

Hej tråden!

Har nu börjat närma mig hur klasser skall fungera i python och med ett stort leende på läpparna inser jag att när jag skapade min klass tidigare för räknesätten så måste jag tänkt på något annat. Det går ju att göra massor med klasser! Vilket jag visste men måste ha glömt det helt och hållet.

Så för att ge mig själv en utvärdering. Den klassen jag skapade tidigare var bara en en samling av mina funktioner för räknesätten. Vilket gjorde att jag bara fick skriva fler bokstäver utan någon riktigt vinst. Skall man säga något om den så blev det iallafall förtydligat att det var räknesättet addition som användes

Hur eller hur så vidare i studierna som blir klart upphuggna med livet som hjälper en att fokusera på annat, dock viktigare saker skulle många påstå.

Visa signatur

Laptop: HP Elitebook 640 G9
Server: HP Microserver N54L, 8 GB ram, 8 TB hd.

Permalänk
Medlem

Halloj tråden!

Nu har jag gett mig in i spelbranschen tillsammans med pygame.

Efter att ha gjort färdigt sololearn's kurs om python gav jag mig in i min bok om RPi och python. En underlig resa så här långt vill jag påstå. Har min RPi kopplad till min tv och ett trådlöst tangentbord till det. Det är ingen höjdare att programmera så vill jag lova. Måste hitta något vettigt att koppla in den till istället.

Efter att skapat lite småprog tillsammans med boken kom jag fram till att skapa ett spel. Fick lite panik när inte något av de exempel filer som följde med fungerade! Visste inte riktigt hur jag skulle felsöka det hela så det slutade i att jag började göra en pong klon istället på min dator.

Iaf så här långt har jag kommit.

#!/usr/bin/env python3 """ Pong Clone made by Me with pygame""" import pygame import random from random import randint class Field: """Holds the size and position of the field""" #Maybe it should be a function instead pass class Paddle(object): """Holds size and position of the paddle also moves and draws it to the display""" def __init__(self, gameDisplay, paddleXPos, paddleYPos, paddleWidth, paddleHeight): self.paddleXPos = paddleXPos self.paddleYPos = paddleYPos self.gameDisplay = gameDisplay self.paddleWidth = paddleWidth self.paddleHeight = paddleHeight self.direction = 0 def draw(self): """Draws the paddle to the display""" #self.paddleYPos += self.direction self.move(self.direction) pygame.draw.rect(self.gameDisplay, (255, 0, 0), [self.paddleXPos, self.paddleYPos, self.paddleWidth, self.paddleHeight]) def draw2(self): """Draws the paddle to the display""" self.paddleYPos += 5 pygame.draw.rect(self.gameDisplay, (255, 0, 0), [self.paddleXPos, self.paddleYPos, self.paddleWidth, self.paddleHeight]) def moveUp(self, pixels): """Moves the paddle up to the top of the field and not above it""" self.paddleYPos -= pixels if self.paddleYPos <= 100: self.paddleYPos = self.paddleYPos + pixels def moveDown(self, pixels): """Moves the paddle down to the bottom of the field and not below it""" self.paddleYPos += pixels if self.paddleYPos >= 500: self.paddleYPos = self.paddleYPos - pixels def move(self, direction): self.direction = direction self.paddleYPos += self.direction if self.paddleYPos <= 100: self.paddleYPos = 100 self.direction = 0 if self.paddleYPos >= 400: self.paddleYPos = 400 self.direction = 0 @property def rect(self): return pygame.Rect(self.paddleXPos, self.paddleYPos, self.paddleWidth, self.paddleHeight) class Ball(object): """Holds size and position""" def __init__(self, gameDisplay, ballXPos, ballYPos, xDirection, yDirection): self.ballXPos = ballXPos self.ballYPos = ballYPos self.gameDisplay = gameDisplay self.xDirection = xDirection self.yDirection = yDirection def draw(self): """Draws the ball to the screen""" pygame.draw.rect(self.gameDisplay, (255, 0, 0), [self.ballXPos, self.ballYPos, 10, 10]) def move(self, obstacles): """Direction and speed of the ball""" self.ballXPos = self.ballXPos + (5 * self.xDirection) self.ballYPos = self.ballYPos + (5 * self.yDirection) if self.ballXPos >= 800: self.xDirection = -self.xDirection elif self.ballXPos <= 0: self.xDirection = -self.xDirection if self.ballYPos >= 590: self.yDirection = -self.yDirection elif self.ballYPos <= 10: self.yDirection = -self.yDirection if self.rect.collidelist(obstacles) != -1: print('test') self.xDirection = -self.xDirection def moveUp(self, pixels): self.ballYPos -= pixels def moveDown(self, pixels): self.ballYPos += pixels @property def rect(self): return pygame.Rect(self.ballXPos, self.ballYPos, 10, 10) def windowSetup(): """Setting all initial variables and functions for start up""" pygame.display.set_caption('Pong Clone') windowWidth = 800 windowHeight = 600 def score(): pass def collision_handler(ball, player1, player2, players): if ball.rect.colliderect(player1.rect) or ball.rect.colliderect(player2.rect): #self.xDirection = -self.xDirection print('collision_handler') ## if ball.rect.collidelist(players): ## print('collision_list') if ball.rect.collidelist(players) != -1: print(ball.rect.collidelist(players)) def main(): """Pong Clone that uses pygame""" pygame.init() windowSetup() clock = pygame.time.Clock() gameExit = False gameDisplay = pygame.display.set_mode((800, 600)) xPos = 400 yPos = 300 player1 = Paddle(gameDisplay, 50, 250, 10, 100) player2 = Paddle(gameDisplay, 750, 250, 10, 100) ball = Ball(gameDisplay, 300, 300, 1, 1) players = (player1, player2) player1.draw() player2.draw() ball.draw() #print(ball.ballXPos) print(randint(0, 10)) while not gameExit: #Main game loop for event in pygame.event.get(): #Event handler if event.type == pygame.QUIT: gameExit = True if event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: xPos -= 5 elif event.key == pygame.K_RIGHT: xPos += 5 elif event.key == pygame.K_UP: yPos -= 5 player1.moveUp(5) player2.move(-5) elif event.key == pygame.K_DOWN: yPos += 5 player1.moveDown(5) player2.move(5) if event.type == pygame.KEYUP: if event.key == pygame.K_UP: yPos -= 5 player1.moveUp(5) player2.move(0) elif event.key == pygame.K_DOWN: yPos += 5 player1.moveDown(5) player2.move(0) gameDisplay.fill((0, 0, 0)) pygame.draw.rect(gameDisplay, (255, 0, 0), [xPos, yPos, 10, 10]) ## player1.draw() ## player2.draw() for player in players: player.draw() ball.move(players) collision_handler(ball, player1, player2, players) if ball.rect.colliderect(player1.rect) or ball.rect.colliderect(player2.rect): print('collision') ball.draw() pygame.display.update() clock.tick(30) pygame.quit() quit() if __name__ == "__main__": main()

Dold text

Provar mig fram friskt för att få det att fungera. Så här i efterhand så är det synd att jag inte sparat alla lustigheter jag har haft med i koden. Ett sådant exempel hade hjälpt mig mycket i början iaf. Måste ta mig i kragen och installera git på den här datorn med. så behöver jag inte ha sweclockers för versionshantering

Upptäckte idag en lustighet med kollisions hanteringen som jag måste titta närmare på. Vidare så känns inte koden för hur jag flyttar mina paddlar bra. Det senaste var att jag lyckades flytta dem när jag höll inne piltangenten.

Iaf ni som har precis börjat programmering. Kämpa på!

Visa signatur

Laptop: HP Elitebook 640 G9
Server: HP Microserver N54L, 8 GB ram, 8 TB hd.