torsdag 29 september 2011

RESTful och web.py

Såhär prototypar du en RESTful webbservice i toolkitet web.py:
import web
import json

urls = (
	'/(.*)', 'index'    # Regexp matching for URI -> class
)

app = web.application(urls, globals())

class index:
	def GET(self, name):
		extra = web.input(extra=None)
		d = {'one':1,'two':2, 'name':name, 'extra':extra}
        	web.header('Content-Type', 'application/json')
        	return json.dumps(d)

if __name__ == "__main__":
	app.run()
Man kör skriptet och går till http://localhost:8080/John+Doe?extra=My+extra+data, och vips så dyker både "John Doe" och "My extra data" upp som JSON hos klienten. ISS/.NET skulle behöva fler rader enbart för konfigurationen än det är rader prototypkod ovan.

måndag 14 maj 2007

Lektion 5: tanka

Till din Python-installation har du fått en massa färdiga .py-filer. Dessa används ungefär som #include-filer i C.

Till skillnad från C måste man inte heller vara mästare på att fatta något för att få något gjort. Här laddar jag hem och skriver ut googles html-sida på tre rader:

>>> import urllib
>>> f = urllib.urlopen("http://www.google.se")
>>> print f.read()

Blir du kåt nu?

FTP är nästan lika enkelt, och det krävs knappt en lamer för att hacka ihop ett nedladdningsprogram.

Slut på lektion 5.

Lektion 4: use the force, Luke

Allt Sker Runtime.
Allt Är Objekt.

Nåväl. Nästan. Och det gör att sakernas tillstånd blir ljuva för oss programmerare. Det inbyggda nyckelordet "import" funkar ungefär som en runtime-variant av #include i C. Skriv:

>>> import os
>>> os.listdir("C:\\")
['boot.ini', 'Bootfont.bin', 'C_DILLA', 'Documents and Settings', 'NTDETECT.COM', 'ntldr', 'pagefile.sys', 'Program', 'RECYCLER', 'RnD', 'System Volume Information', 'WINDOWS']

Mja. Den koden var enklare i Python än C... Det följande fattar du dock inget av än, men
det visar på en viktig detalj:

>>> m = __import__("os"); getattr(m, dir(m)[74])("C:\\")
['boot.ini', 'Bootfont.bin', 'C_DILLA', 'Documents and Settings', 'NTDETECT.COM', 'ntldr', 'pagefile.sys', 'Program', 'RECYCLER', 'RnD', 'System Volume Information', 'WINDOWS']

Nämligen att det går att göra saker nästan hur som helst i Python. Vid utvecklingen av Python har man ett måtto: "vi ska försöka att få folk att göra rätt, men om de vill så ska de tamejfan få skriva vilken fungerande griskod som helst".

Och för att åstadkomma diverse runtimetricks så finns gott om inbyggda funktioner till vår hjälp. Du har redan testat på help(). Här ett axplock:

  • import - ungefär som #include.
  • print - ungefär som printf().
  • help() - sammanställer dokumentation rekursivt för ett objekt.
  • dir() - listar medlemmar i ett objekt.
  • hasattr() - kollar om ett objekt har en medlem med ett visst namn.
  • callable() - returnerar True/False beroende på om ett objekt går att anropa eller ej.
  • file() - filklass.
  • open() - filöppningsfunktion.
  • filter() - funktionell filtrering.
  • map() - funktionell mappning.
  • reduce() - funktionell reducering.
Så för att testa lite introspection så får du skriva lite kod som jag har snott från Mark Pilgrim:

>>> def info(object, spacing=10):
         methodList = [method for method in dir(object) if callable(getattr(object, method))]
         processFunc = lambda s: " ".join(s.split())
         print "\n".join(["%s %s" % (method.ljust(spacing), processFunc(str(getattr(object, method).__doc__))) for method in methodList])

>>> class sunkKlassen:
         def a(self):
                 """Min dokumenterade funktion...
                 Jajjemen.
                 Du.

                 Missa ej min dokumentation,
                 annars blir det att smaka min menstruation..."""
                 pass
         def b(self, x):
                 "Men finare dokumenterat i b()!"
                 pass

>>> a = sunkKlassen()
>>> info(a)
a          Min dokumenterade funktion... Jajjemen. Du. Missa ej min dokumentation, annars blir det att smaka min menstruation...
b          Men finare dokumenterat i b()!

Funktionen info() gör följande

  1. plockar ut alla medlemmar som är anropningsbara och lägger i variabeln "methodList".
  2. Sedan skapar den en funktion, "processFunc" som ersätter alla whitespaces med ett enda space.
  3. Sist skapar den en lista som består av metodnamnet och dess dokumentation, och så jämkar den ihop alla dessa med en linefeed ("\n").

Det ser svårare ut än vad det är; när du väl är van så är Pythonkod lättläst.

Detta var det svåraste kapitlet, och antagligen fattar du inget nu. Misströsta ej, det kommer när du experimenterat lite.

Nästa kapitel är kort och enkelt och mycket, mycket vackert.

Lektion 3: inbyggda ljuvheter

Till skillnad från C har Python inbyggda typer för:

  • strängar
  • listor
  • konstanta listor (som ej kan modifieras)
  • hashtabeller (kallade "dictionaries")

Alla är självfallet "ärvbara" och har stöd för det man behöver. Du provar lite strängteori:

>>> s = "mintglass sMaKaR anus"
>>> s[0]
'm'
>>> s[2:]
'ntglass sMaKaR anus'
>>> s.upper()
'MINTGLASS SMAKAR ANUS'
>>> s.upper()+"!!!"
'MINTGLASS SMAKAR ANUS!!!'
>>> s[-8:]
'KaR anus'
>>> s[:-5]+"..."
'mintglass sMaKaR...'
>>> s*2
'mintglass sMaKaR anusmintglass sMaKaR anus'
>>> "konstant e icke heller fel"[11:15]
'icke'

Som du räknat ut så går det att addera lite hur som helst och ljuvheten är stor. Strängar kan
skrivas med antingen ' eller ", det spelar ingen roll. Indexering av en sträng sker med:

  • [index] - ger ett specifikt tecken.
  • [startindex:ett_efter_sista_index] - ger en substräng. Om ett av indexen utelämnas så tas hela strängen "åt det hållet".

Negativa index refererar självklart till offsets från höger. Och sist men icke minst: även konstanta strängar är objekt! :)

I framtida lektion ska jag visa att sprintf även är inbyggd i strängar, och riktigt enkelt att använda. Och sist men inte minst så finns stöd för att dela upp och slå ihop strängar som är mycket kraftfullt.

Listor är minst lika ljuva. Du testar:

>>> l = ["a", "b", "alex", "huggesson", "z", "exempel"]
>>> l
['a', 'b', 'alex', 'huggesson', 'z', 'exempel']
>>> l[0]
'a'
>>> l[-1]
'example'
>>> l[2:4]
['alex', 'huggesson']
>>> l[1:-1]
['b', 'alex', 'huggesson', 'z']
>>> l.append("ny")
>>> l
['a', 'b', 'alex', 'huggesson', 'z', 'exempel', 'ny']
>>> l.extend(["tvenne", "element"])
>>> l
['a', 'b', 'alex', 'huggesson', 'z', 'exempel', 'ny', 'tvenne', 'element']

Det finns massor av bra sätt att söka i en sträng eller lista. Mer om detta senare.

Hur ofta vill man inte returnera flera värden från en funktion? Typ jämt! I C får man skicka pekare, i Python returnerar man bara på. Såhär kan en tilldelning se ut:

>>> x, y, z = 1, 2, 3

Eller en funktion som returnerar:

>>> def f():
return 1, 2, 3

>>> x, y, z = f()

Nästa lektion handlar om det ljuvaste av allt: något som Mark Pilgrim kallar "The Power of Introspection". Mmmm...

Lektion 2: Pythons fördelar

Python är dynamiskt typat. Det betyder att typningen undersöks runtime. Detta i stil med VB-skript, och i motsats till Java/C.

Python är starkt typat. Det betyder att ingen automagisk cast sker, man måste göra det uttryckligen. I stil med Java och i motsats till VB-skript.

I Python är allt ett objekt. En funktion, en klass, en instans, en sträng, en int, en lista, osv. Detta är en styrka vid snabb och enkel utveckling.

I Python är dokumentation inbyggt. Språket "rekommenderar" en att dokumentera och det går att få tag på dokumentationen runtime. Ljuvt!

I Python använder man inte braces som i C, utan i stället låter man indenteringsnivån avgöra vilket scope koden tillhör. Felaktig indentering -> kompileringsfel.

Åter till shellet. Skriv:

>>> def myFunction(data):
         print data

>>> myFunction(3.3)
3.3
>>> myFunction("Hej du glade")
Hej du glade

Som du ser så skiter myFunction i vad objektet "data" har för typ. Nu testar vi stark typning och omdefinierar myFunction:

>>> def myFunction(data1, data2):
         data = data1+data2
         print data

>>> myFunction(3.3, 5.5)
8.8
>>> myFunction(3.3, "Hej du glade")

Traceback (most recent call last):
File "", line 1, in
myFunction(3.3, "Hej du glade")
File "", line 2, in myFunction
data = data1+data2
TypeError: unsupported operand type(s) for +: 'float' and 'str'

En stack trace skrivs ut och resten är historia.

Att skicka ett funktionsobjekt är precis lika trivialt:

>>> def f1(param1, param2):
         param1(param2)

>>> def f2(data):
         print data+3

>>> f1(f2, 7)
10

Om man lägger en sträng direkt i början på en modul, klass eller funktion så blir det dokumentationssträngen, och hamnar som en medlemssträng i objektet och får
namnet __doc__. Såhär dokumenterar man:

>>> def minSuperFunction():
         "Detta är min supraledande funktion!"
         print "Jomenvisst!"

För att ta reda på dokumentationen kan du gå den tramsiga vägen:

>>> minSuperFunction.__doc__
'Detta \xe4r min supraledande funktion!'

Eller om du vill vara cool:

>>> help(minSuperFunction)
Help on function minSuperFunction in module __main__:

minSuperFunction()
         Detta är min supraledande funktion!

Den inbyggda functionen help() drar rekursivt igenom dokumentationen i ett objekt och skriver ut den snyggt.

Självdokumenterande kod är en utopi, men detta tar en ett steg närmare, inte sant?

Lektion 1: komma igång

Dessa lektioner är till för dig som kan programmera. Föredragsvis Java och C.

Installera Python på din dator. Vet du inte vad du ska installera så googla på "Python Windows Installer".

I Windows kör du Idle, och antagligen i Linux oxå (brukarfölja med som default). Python är portat och språket är portabelt; däremot skiljer sig vissa (få) system-specifika moduler sig åt. När du startar Idle så får du upp ett Python-shell. Där kan du skriva in Pythonkod som kompileras och körs runtime. Ljuvt!

Testa att starta Idle nu. I shellet skriver du in:

>>> x = 4.8
>>> y = 2.74
>>> x*y+5*(x+y)-30-x+(-y)
13.312000000000003

Självklart finns operationer för uphöjt till ochannat smått och gott som fattas i C++. Notera att returvärdet skrivs ut direkt i shellet om du inte tar hand om det. Som synes kan du använda Python som en avancerad miniräknare om du så behagar.

Och det behagar du, men du behagar även mycket mer -> lektion 2!