Struktur när man skapar spel/program i Python 3.x

Permalänk
Medlem

Struktur när man skapar spel/program i Python 3.x

Jag kan i dag tillräckligt med programmering så att jag förstår mig på klasser, funktioner och individuella problem. Till exempel så har jag skapat några enkla spel i ett enda skript med hjälp av pygame. Jag har gjort ett 40-tal lösningar på projecteuler.net. Snart är jag också färdig med en master inom statistik, så jag kan även programmera i Rstudio.

Men när jag ska skriva något större program så har jag ingen aning om programstruktur, versionskontroll, felsökning, debugging (det verkar inte vara samma som felsökning), hur man kompilerar allting till en .exe så att andra bara kan köra programmet. Helt enkelt jag kan programmera utan att programmera.

Det blev väldigt uppenbart för mig när jag började spela Death road to Canada och blev sugen på att plagiera det genom att göra en väldigt mycket mindre version av spelet bara på kul. Jag får känslan av att jag kan de individuella delarna som behövs för att göra något sådant, men att jag kommer göra det 110 ggr svårare än vad det behöver vara.

Finns det något bra ställe som lär ut det här som jag lite luddigt har beskrivit?

Tack på förhand

Permalänk
Medlem
Skrivet av burkabu:

Jag kan i dag tillräckligt med programmering så att jag förstår mig på klasser, funktioner och individuella problem. Till exempel så har jag skapat några enkla spel i ett enda skript med hjälp av pygame. Jag har gjort ett 40-tal lösningar på projecteuler.net. Snart är jag också färdig med en master inom statistik, så jag kan även programmera i Rstudio.

Men när jag ska skriva något större program så har jag ingen aning om programstruktur, versionskontroll, felsökning, debugging (det verkar inte vara samma som felsökning), hur man kompilerar allting till en .exe så att andra bara kan köra programmet. Helt enkelt jag kan programmera utan att programmera.

Det blev väldigt uppenbart för mig när jag började spela Death road to Canada och blev sugen på att plagiera det genom att göra en väldigt mycket mindre version av spelet bara på kul. Jag får känslan av att jag kan de individuella delarna som behövs för att göra något sådant, men att jag kommer göra det 110 ggr svårare än vad det behöver vara.

Finns det något bra ställe som lär ut det här som jag lite luddigt har beskrivit?

Tack på förhand

Kan rekomendera dig att titta på någon längre tutorial på t.ex. YouTube där du kan få se hur personen hanterar upplägg och liknande. Versionshantering antar jag att din IDE har inbyggt stöd för, där du framförallt kanske ska kika på GIT eller SVN. Jag vill inte bli citerad på denna punkt men jag misstänker att det kan bli svårt att kompliera ditt/dina python skript till en exe (någon mer insatt i python får gärna bekräfta/rätta). Såg nu att det finns py2exe för pygame

Visa signatur

2600k @ STOCK <|> GTX 970 Omega!<|> Nån samsung 500gb ssd <|> 16 GB Kingston Hyper X <|> BenQ XL2420t
"Det finns inget skrot, bara gamla delar som kan användas på nya sätt" - Mulle Meck

Permalänk
Hedersmedlem
Skrivet av burkabu:

Finns det något bra ställe som lär ut det här som jag lite luddigt har beskrivit?

Livets hårda skola!

Viss sanning ligger tyvärr i det. Hur ett "större" projekt bör struktureras brukar sällan vara något som ens mjukvaruingenjörer har klart för sig efter långa utbildningar. Har man skrivit många projekt så märker man snart vad som fungerar eller ej, och inte minst så tittar man på hur andra projekt gör, tar till sig av de "vettiga" delarna och fortsätter utvecklas.

I detta ligger också att även om man skulle hitta en bloggpost att länka om hur exempelvis katalogstrukturen i ett större Python-projekt kan se ut, så skulle det sannolikt mest kännas bökigt och alltför strikt om man inte själv upplevt de problem som har drivit fram en sådan struktur. Trots det så finns det mycket att tjäna att titta på ett existerande större Python-projekt och fundera på varför de har strukturerat sitt projekt på ett visst sätt.

Vet inte exakt vilket typ av svar du är ute efter, men ska man trots detta försöka slänga ut några generella projekttips med tonvikt på Python, så:

  • Använd virtuella miljöer för all utveckling ("virtual environments"). Din globala Pythontolks enda uppgift ska egentligen vara att köra systemskript och skapa virtuella miljöer för dina egna projekt. Definiera dina paketberoenden i en requirements.txt-fil (output från pip freeze).

  • När du gjort detta så se till att det finns något sätt som gör det trivialt att sätta upp utvecklingsmiljön från scratch (för Python betyder detta typiskt en virtuell miljö med alla paket) — antingen genom att skriva ett eget mindre setup-skript (i exempelvis just Python), eller snickra en egen make-fil om du har någon vana vid detta. Tar det mer än ett kommando att sätta upp en utvecklarmiljö så är det som tumregel för mycket.

  • Du nämner många bra saker i din lista över saker att tänka på: något du bör ha med direkt från början är versionshantering. Detta tar tid att lära sig bra, men är utan snack ett av de viktigaste verktygen redan för små personliga projekt i mina ögon. Ska du gå vidare och jobba tillsammans med andra så är det absolut nödvändigt. Git är aldrig ett dåligt val för kodprojekt.

  • På samma sätt som det måste vara trivialt att sätta upp din utvecklingsmiljö så måste det vara trivialt att köra dina testsviter. Valde du make-fil-spåret tidigare så bör exempelvis make test köra din testsvit. Börja skriva tester med Pythons egna unittest-modul, vilket räcker långt. När du börjar få stora sviter, behöver testa mot flera olika Python-versioner eller liknande så gå vidare till Pytest (man skulle kunna börja där direkt och bara använda Pytest för att exekvera sviterna, men jag tror man tjänar på att ha bra koll på unittest innan man går över till tredjepartslösningar).

    Du nämnde inte testsviter ovan, men tillsammans med versionshantering så är det ett av de absolut viktigaste koncepten som jag sällan sett läras ut på något praktiskt användbart plan i skolmiljöer.

  • Loggning — logga allt, stort och smått, redan från början. Det underlättar felsökning något oerhört och fungerar som en sorts exekverbar koddokumentation. Den inbyggda logging-modulen är mycket kompetent (alternativt kan du börja med att strössla print-anrop i dina funktioner, nå stadiet då du skulle tycka det vore käckt att ha olika loggningsnivåer, irritera dig över att olika moduler loggar på olika sätt, få ordentligt med smäll på fingrarna när du börjar skriva multitrådade program för att till slut förstå varför logging är rätt väg att gå ). Konfigurera den gärna till att logga både till terminal och en fil (som roteras), och utnyttja de olika loggningsnivåerna för att inte drunkna i meddelanden, förutom när du startar med exempelvis en --debug-växel.

    Notera också att du mer eller mindre kan räkna med att tredjepartsbibliotek också använder den inbyggda logging-modulen, och att de automatiskt kommer ärva din konfiguration av loggningsvägar- och nivåer, vilket bara är bra.

  • Ovan nämnda --debug-växel lägger du med fördel till genom att använda den inbyggda argparse-modulen för att definiera ett kommandoradsgränssnitt.

    Generellt är det trevligt att kunna klara sig med moduler i standardbiblioteket så länge det går. Mindre huvudvärk vid uppgraderingar, lätt att hitta dokumentation och hjälp. Därmed inte sagt att man ska undvika tredjepartsmoduler när de kan göra livet betydligt enklare, men allt som oftast finns det bra grejer redan i standardbiblioteket.

Några mer konkreta tips vad gäller övergripande kodstruktur:

  • Skriv din kod så att separata delar fungerar som separata moduler. I praktiken innebär det att dessa ska kunna ligga i olika kataloger, där du får avgränsade namnrymder ("name spaces") automatiskt. Detta leder till bättre modularitet, vilket ger mindre, mer överblickbara delar, vilket leder till projekt som är lättare att underhålla när de växer.

  • Om du skriver ditt program så att det fungerar likt en modul som du kan köra med exempelvis python3 -m minmodul så bör du skapa ett launcherskript som kör denna modul med hjälp av runpy. Dels förenklar det att köra skriptet, och dels fungerar det som en naturlig ingångpunkt ifall du vill distribuera paketet som en körbar binär.

Kodmässigt:

  • Ha koll på PEP-8 (se pycodestyle för statisk analys) och använd Pylint regelbundet. "Regelbundet" betyder i praktiken ofta att man använder en vettig editor med stöd för löpande kontroll. Dessa riktlinjer ska inte slaviskt följas som ett blint mantra i sig, men om man väljer att frångå dessa konventioner så bör man kunna ha en bra motivering. Att vänja sig vid detta gör det mycket lättare att läsa både sin egen men även andras kod.

    När man känner att man har span på PEP-8 så rekommenderar jag att titta på Beyond PEP-8 av Raymond Hettinger för ytterligare insikter. För övrigt så är alla föredrag av Hettinger troligen värda att titta på — oerhört duktig presentatör.

  • Skriv små, isolerade funktioner. En funktion vars kropp är en enda rad är absolut inget ovanligt — hjälper det läsbarheten så är det bara bra. En funktion på 100 rader är nästan garanterat dålig, på så sätt att den är svår att testa ordentligt och svår att följa logiskt. Sannolikt går gränsen långt under 100 rader, men det är oerhört situationsberoende.

  • Återigen för att främja testbarhet: försök separera kodstycken som sköter input och output till egna funktioner (läsning från terminaler, filer, nätet, mm) så att du enkelt kan injicera testdata och rikta utdata till kontrollerade filer i dina enhetstester. När jag sett kodbaser som författare hävdar är "svåra att testa" så beror det ofta på att just input är anonymt strösslad långt inne i funktioner. Det är en konst att skriva bra test, men det är lika mycket en konst att skriva enkelt testbar kod.

  • Skriv docstrings till alla* funktioner, triviala eller ej. Du kommer tacka dig själv i framtiden. Det kommer också få dig att skriva bättre kod med hjälp av skam: när du känner att du skäms för att skriva en viss docstring så är det ofta ett tecken på en felaktig programstruktur. Skammen kommer få dig att skriva om programmet på ett bättre sätt hellre än att skriva en sådan docstring.

    En annan tumregel är att om du behöver många "and", meningar, krångliga bisatser, mm, för att skriva din docstring så kanske din funktion/metod/klass/fil borde delas upp i fler, mer isolerade sådana.

    *: Så kallade "dunder"-funktioner ("double underscore" likt __init__, __str__, etc.) behöver sällan en docstring om de inte gör något som kan vara oväntat för läsaren (och det ska de helst inte göra utan en väldigt bra anledning).

Ett annat tips angående att du nämner att göra en "exe" av ditt program: detta är ofta smärtsamt i Python-världen så fort man börjar blanda in resursfiler och tredjepartsmoduler som ofta inte alls tar höjd för att köra i "frysta" miljöer. Pyinstaller, py2exe, cx_Freeze, m fl, projekt finns, men det är i verkligheten sällan så enkelt som det beskrivs i tillrättalagda exempel i dokumentationen just pga tredjepartsmoduler. Personligen föredrar jag bland dessa cx_Freeze, då det inte känns som att det aktivt motarbetar dig så fort du hamnar i en situation där default-inställningarna inte duger.

Börja med att få ditt program att köra genom en Python-tolk, och lägg till stöd för att kompilera till plattformsberoende binär i slutet, om det fortfarande är aktuellt.


Det finns alltså många små tips och tricks, även om jag försökte hålla mig till svepande generaliseringar ovan, för alla språk och deras respektive ekosystem. Ett bra sätt att utvecklas är att skriva små egna projekt, och inte vara nöjd bara för att det "typ fungerar" utan ständigt förbättra koden för bättre läs- och underhållbarhet. Ska du bara minnas ett fåtal saker från textmassan ovan så minns versionshantering och enhetstester, då båda delar dels är viktiga på egen hand, men också hjälper/tvingar dig att skriva bättre kod generellt.

Visa signatur

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

Permalänk
Medlem

@elklazor: Tackar

@phz: Det var ett sådant här svar jag både ville ha och var rädd för att få. Det visar tydligt på hur mycket jag inte kan
Men det är fantastiskt att få en liten checklista som jag kan börja nysta i för att få lite struktur och styr på mina små (som förhoppningsvis ska bli större) projekt. Nu har jag inte gått ett datavetenskapligt program, men ungefär hälften av kurserna jag gått under 5 år har innehållit någon typ av programmering, men inte en gång har det snuddats vid någon av de punkter du gav här.

Så igen tack för att du påvisat hur okunnig jag är

Permalänk
Hedersmedlem
Skrivet av burkabu:

Nu har jag inte gått ett datavetenskapligt program, men ungefär hälften av kurserna jag gått under 5 år har innehållit någon typ av programmering, men inte en gång har det snuddats vid någon av de punkter du gav här.

Jag tror inte någon av de punkterna jag nämnde ingår ens i ett datavetenskaps- eller mjukvaruingenjörsprogram (det som ingår där går också att lista, men det är saker som också är lättare att hitta läromaterial om), utan de var mer saker som kommer från att jobba praktiskt med att skapa små och stora projekt som ska överleva över länge, kanske lämnas över till andra programmerare med tiden och inte minst skapas tillsammans med andra. Saker som att göra det trivialt att sätta upp utvecklingsmiljön och köra tester, vara frikostig med docstrings, att skriva små och tydliga funktioner, etc., är en förutsättning för att kunna ta in nya människor i en utvecklargrupp på ett tillnärmelsevis effektivt sätt med ett minimum av svordomar.

Jag brukar vilja framhäva nyttan av sådant genom insikten att man själv symboliskt sett är denna "nya utvecklare" även för den kod man själv utvecklat, om man av någon anledning är borta från projektet ett par månader. I stora projekt sker detta hela tiden med olika delar av koden: det kan utan konstigheter ha gått ett halvår sedan du senast tittade på en viss algoritm, och då får du hoppas att du lämnade den i gott skick, så att inte din spontana reaktion är att bara kasta allt gammalt och skriva om det på nytt hellre än att försöka förstå vad som händer i koden.

Som jag nämnde så kan det vara lärorikt att titta på ett existerande projekt för att se hur de strukturerat koden, vilket är lättare att göra idag än någonsin med alla öppna kodbaser som går att hitta. Klona hem ett projekt som verkar intressant från exempelvis Github och försök få det att snurra lokalt. Kolla hur de löst testning av mjukvaran. Har de något releaseskript? Automatgenererar de dokumentation? Hur ser deras katalogstruktur ut?

Men framför allt: skriv egna program, precis som du säger att du gör. Börja i liten skala så att du hela tiden kan se inkrementella framsteg, och nöj dig aldrig med koden, utan förbättra den ständigt. Gör saker ordentligt, och låt det ta tid. När det känns svårt så betyder det ofta att du lär dig något .

Visa signatur

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