Lär dig Python på en helg: Från noll till ett komplett projekt
Hur kan vi snabbt lära oss ett nytt programmeringsspråk? I den här artikeln delar vi med oss av vår helgupplevelse av att lära oss Python genom att bygga ett komplett projekt.
Hur kan vi snabbt lära oss ett nytt programmeringsspråk? I den här artikeln delar vi med oss av vår helgupplevelse av att lära oss Python genom att bygga ett komplett projekt.
Logto, som en identitetstjänst, är det viktigt att tillhandahålla en smidig upplevelse över olika programmeringsspråk och ramverk. Detta innebär ofta att skapa Software Development Kits (SDKs). Men när programmeringsspråket ligger utanför vår teknikstack och vårt team saknar erfarenhet, blir det en utmaning att skapa ett robust SDK.
Python utgjorde en sådan utmaning för oss. Trots att vi hade många Python-användare saknade vi ett Python SDK, vilket var en kvarstående oro. Besluten att adressera detta, började jag brygga denna klyfta.
Även om jag har flera års programmeringserfarenhet, är Python fortfarande relativt outforskad mark för mig. Även om jag hade experimenterat med Python 2 kort för enkla skript för många år sedan, var min kunskap föråldrad. Ändå var det dags att dyka in!
Av erfarenhet är det mest effektiva sättet att lära sig ett nytt programmeringsspråk att bygga ett komplett projekt. Som tur var var vårt mål klart: konstruera ett Logto Python SDK för webbapplikationer.
Istället för att hoppa direkt in i koden, låt oss bryta ner det. Här är listan:
Det ser ut som uppgift 1 har mest arbete, så vi behöver bekräfta omfattningen och fortsätta bryta ner den. Detta steg är avgörande för att säkra projektets gräns och undvika omfattningsglidning och överingenjörer.
Tidigare arbete av vårt team sparade mig massor av tid:
Genom att referera till dessa resurser kan jag se en tydlig bild av vad som behöver göras. Även om specifika detaljer går utanför denna artikels omfattning, låt oss gå vidare.
Jag använder en Mac, så Python är redan installerad. Jag undrade dock om det finns ett bättre sätt att hantera Python-versionerna (jag har hört talas om smärtan med versionens kompatibilitet), precis som nvm för Node.js. Snabbt hittade jag pyenv och började direkt installera det.
Nästa på agendan: en paketerings- och beroendemanager. Vanligtvis är de kopplade. Så varför inte pip (Python-standard)? Om du tittar på requirements.txt, kommer du märka att det bara är en lista över paket med versioner. Detta är inte tillräckligt för ett SDK som kan användas av andra projekt. Till exempel kanske vi behöver lägga till några paket för utveckling, men vi vill inte inkludera dem i det slutliga SDK:et. requirements.txt är för enkelt för att hantera detta.
En av fördelarna när du har andra programmeringsspråk i din teknikstack är att du kan söka efter "Python-ekvivalent". Så jag sökte efter "package.json Python-ekvivalent" och hittade Poetry, en strålande kandidat:
pyproject.toml-fil, liknande package.json i Node.js.Moderna CLI:er omfattar ofta ett init-kommando som är anpassat för nya projekt. Poetry gör det också. Jag körde kommandot och det skapade en pyproject.toml-fil åt mig.
Äntligen hade ögonblicket att skriva kod anlänt. Att börja med det klassiska "Hello, World!"-programmet är alltid ett bra val. När man lär sig ett programmeringsspråk är fullfjädrade IDE:er inte alltid nödvändiga; en redaktör backad av en stark gemenskap, såsom VS Code, är helt tillräcklig.
Med tanke på vårt SDK:s fokus på webbapplikationer började jag med en enkel webbserver som använde det populära ramverket Flask.
Genom att utnyttja Poetries kapabiliteter kan Flask enkelt installeras genom att köra poetry add flask. Sedan, enligt Flasks officiella snabbstartsguide, komponerade jag en 'hello.py'-fil med följande kodbit:
Att starta servern via flask --app hello run och navigera till http://localhost:5000 på min webbläsare resulterade i det önskade resultatet. Det fungerade!
Som nybörjare var jag inte snabb att skriva mer kod. Istället finns det mycket information att ta emot från kodbiten:
from x import y för att importera en modul eller en klass.new-nyckelordet.@app.route fungerar som en dekorerare som registrerar en funktion som en routhanterare.
def-nyckelord.Som du kan se, om vi försöker förstå varje rad kod istället för att "bara få det att fungera", kan vi lära oss mycket av det. Samtidigt förklarade Flasks officiella dokument ytterligare kodbiten i detalj.
Nu är det dags att starta projektet. Snart definierade jag en LogtoClient-klass och försökte lägga till några egenskaper och metoder för att känna språket:
Därefter integrera klassen med Flask:
Det började kännas som ett riktigt projekt. Men jag kände att något saknades: ett typeringssystem.
Eftersom det är ett SDK, kommer det att hjälpa användarna att förstå API:et och minska risken för fel vid utveckling genom att inkorporera ett typeringssystem.
Python introducerade typhintar i version 3.5. Det är inte lika kraftfullt som TypeScript, men det är bättre än ingenting. Jag la till några typhintar till LogtoClient-klassen:
Ser mycket bättre ut nu. Men utmaningen dök upp när det kommer till en komplex typ som ett objekt med fördefinierade nycklar. Till exempel, vi måste definiera en LogtoConfig-klass för att representera konfigurationsobjektet:
Det ser okej ut, men snart måste vi möta problemen med att koda, dekoda och validera objektet från JSON.
Efter lite forskning valde jag pydantic som lösning. Det är ett datavalideringsbibliotek som fungerar med typhintar. Det stödjer olika JSON-funktioner utan att följa tråkig boilerplate-kod.
Därför kan LogtoConfig-klassen skrivas om som:
Det lärde mig också om klassarv i Python genom att lägga till parenteser efter klassnamnet.
Inom Logto SDK, behöver vi göra HTTP-förfrågningar till Logto-servern. Om du har erfarenhet av JavaScript, klingar termen "callback hell" troligen bekant. Det är ett vanligt problem när man hanterar asynkrona operationer. Moderna programmeringsspråk presenterar liknande lösningar som Promise eller coroutine.
Lyckligtvis har Python en inbyggd lösning av async och await. Innan du använder dem, säkerställ kompatibiliteten med populära ramverk. I Flask kan detta göras genom att installera async-extra och använda async def istället för def:
Då kan vi använda await för att vänta på resultatet av en asynkron operation.
HTTP-förfrågningar är ett intressant ämne. Nästan varje programmeringsspråk har en inhemsk lösning, men utvecklare använder vanligtvis ett tredjeparts-bibliotek eftersom det är lättare att använda. Några exempel:
XMLHttpRequest vs. fetch vs. axiosURLSession vs. AlamofireHttpURLConnection vs. OkHttpDetta är också sant för Python. Mitt beslut var att använda aiohttp eftersom det stöder async och await samt dess popularitet.
Tidigare än Copilot, skulle vi nu komma till den tråkiga delen av att skriva affärslogik. Med hjälp av SDK-konventionen och andra SDK:er kunde jag skriva de beskrivande kommentarerna för varje metod innan jag skrev koden.
Det ökar kodens läsbarhet och hjälper utvecklare att förstå API:et direkt i IDE eller redaktör via kodintelligens.
Till exempel, överväg generateCodeChallenge-metoden, kommentarerna kan skrivas som:
Detta utgjorde ett bra prompt för stora språksmodeller (LLMs): sammansättning av metoddefinitioner genom tydliga kommentarer. Och Copilot gjorde inte besviken:
Några justeringar kan behövas, men det spelar ingen roll. Det har redan förändrat spelet.
Det är ungefär framstegen som uppnåddes under den första dagen. Det var en lång dag, men med moderna verktyg och teknologier var det mycket bättre än jag förväntat mig.
Baserat på arbetet den första dagen blev affärslogiken snabbt färdig. Men för ett SDK är det fortfarande otillräckligt. Här är uppgifterna för den andra dagen:
Enhetstester har räddat oss många gånger, så jag kommer inte att hoppa över det. Här är vanliga överväganden när man skriver enhetstester:
Med dessa frågor i åtanke fann jag att det inbyggda unittest-modulen inte räckte för vissa fall. Så jag valde pytest som testramverk. Det stöder asynkrona tester och ser tillräckligt moget ut.
Resan avslöjade några intressanta nya koncept som fixture för mig. Detta kan också gynna tankesättet när man skriver kod i andra språk.
Varje språk har sina egna kodformateringsstilar. Personligen kan konsekvent formatering göra mig glad och bekväm; det är också hjälpsamt för kodgranskning och samarbete.
Istället för att bläddra i argumentet för den "bästa" stilen, bestämde jag mig för att välja en bestämd formatterare och hålla mig till den.
Black ser ut som ett bra val. Den enda frustrationen är den omodifierbara flikstorleken, men det är inget stort problem, jag valde att anpassa mig till det.
Som ett SDK bör det vara kompatibelt med rådande Python-versioner. Genom att söka efter "python version usage statistics", bestämde jag att använda Python 3.8 som minsta version.
Fördelarna med miljöhanterare visar sig nu. Jag kan enkelt växla Python-versionen genom att köra pyenv local 3.8 och poetry env use 3.8. Då kunde jag köra testerna för att avslöja kompatibilitetsproblem.
Kontinuerlig integration garanterar kvaliteten på varje kodändring. Eftersom vårt arkiv fanns på GitHub presenterade GitHub Actions det naturliga valet.
Kärnworkflowen följer enkla principer:
GitHub Actions har ett bra community, så det tar bara några minuter att konstruera workflowen.
Genom att använda matrisstrategier kan vi köra workflowen på olika Python versioner, även på olika operativsystem.
Det sista steget är att publicera SDK. För offentliga paket kan detta vanligtvis göras genom att skicka till det officiella språksspecifika pakethanteringsregistret. Till exempel, npm för Node.js, PyPI för Python, och CocoaPods för Swift.
Poetry är min ledstjärna. Kör bara poetry publish för att publicera paketet till PyPI. Det är så enkelt.
Det var en engagerande resa. Utan hjälp från öppen källkodscommunity hade det varit mycket svårare. Beröm till alla bidragsgivare!
Här är några allmänna lärdomar:
Du kan hitta det slutgiltiga resultatet i detta arkiv. Med samma strategi byggde jag också snabbt Logto PHP SDK. Tveka inte att låta oss veta om du har några förslag.
Hoppas den här artikeln är hjälpsam för att lära sig ett nytt programmeringsspråk. Lycka till med hackandet!