Posts Tagged ‘python’

Pycon Sweden 2015

maj 16, 2015

I tisdags gick den numera årliga python konferensen pycon sweden av stapeln. Likt förra året var det en fantastiskt trevlig upplevelse.

pyconsweden

Första dagen

En stor del av föredragen behandlade data-analys och datorinlärning. Mycket av detta gick mig över huvudet men intressant att få lite insikt i vilka möjligheter det moderna samhället ger till att samla och analysera data. Också intressant var att gå igenom hur man tränar upp ett neuralt nätverk för bildanalys. Föredragen var väldigt tekniska och eftersom de var relativt korta var de rätt övergripande och gick inte jättedjupt.

Ett annat föredrag som jag tyckte var intressant var föredraget om U2F, en standard för tvåfaktor-autentisering. Dain Nilsson som stod för föredraget arbetar för Yubico och därför var särskild fokus på deras lösning YubiKey. YubiKey är en usb-dongel som agerar som ena faktorn i autentiseringen och verkar vara mycket smidig.

Kvällen

Efter sista föredraget bjöds det på en gratis drink i baren och folk minglade. Jag pratade med diverse människor, en amerikan, en finländare som jobbade med databaser och ett helt gäng andra. Vi åt middag ett gäng och hamnade i slutändan på Oliver Twist.

Dag Två

Andra dagen inleddes med ett föredrag av Kate Heddleston om etik och moral. Vad är vårt ansvar som programmerare när tekniken vi bygger används för hatbrott, förföljelse, riktad reklam, etc? Inga raka svar gavs men det är frågor som måste lyftas fram och diskuteras öppet.

I övrigt var andra dagen lite mer blandad än den första. Ett av de mer intressanta föredragen handlade om Python i inbyggda system. Presentatören använde python i produktionskod men listade mest problem användningen av python resulterade i. Generellt kändes det ganska negativt.

Konferensens sista föredrag var Python for Humans, en diskussion om hur pythonmoduler bör skrivas. Föredraget grundades i PEP20, som är språkets riktlinje men som frångåtts i mångt och mycket. Tanken är att det ska finnas ett (och gärna endast ett) intuitivt, lätt sätt att åstadkomma något. Mycket tankeväckande och nästan inspirerande.

Föredragen spelades in och kommer inom kort läggas ut på hemsidan.

pickle

september 25, 2014
Dillinlagda gurkor*

Dillinlagda gurkor*

Till mitt lilla spel ville jag lägga in möjligheten att spara och ladda spel och funderade länge på hur man gjorde det på bästa sätt. Jag hittade efter ett tag pickle i pythons standardbibliotek som visade sig vara ett enkelt sätt att hantera detta på.

Det pickle-modulen gör är att serialisera python-objekt till en fil. Med detta menas att pickle omvandlar objektet till en serie bytes som sedan kan skrivas till fil. Man kan jämföra det med att i C dumpa en struct via

typedef struct {
int pos[2];
int size[2];
char name[256];
} data_t

data_t d;
write(file, d, sizeof(d));

Detta fungerar för struct:ar som enbart innehåller data, men innehåller den referenser blir det hela mer komplicerat och kräver särskild implementation. Eftersom python ser allt som objekt, blir Pickle lite mer intelligent och serialiserar all data som lagras i objektet samt alla andra objekt som objektet hänvisar till (rekursivt hela vägen ner till basobjektet object) samt funktionerna (kod är också objekt) som ingår i objektet.

För att spara all data i mitt spelarobjekt räcker det med

pickle.dump(player, open(player.save_slot + "/player.data", 'wb'))

vilket ser ganska enkelt ut, tyvärr finns det komplikationer. När jag försökte detta första gången fick jag följande felmeddelande

TypeError: can’t pickle Surface objects

Detta beror på att objektet måste ha bland andra metoderna __getstate__() och __setstate__() för att läsa ut samtliga objekt i objektet och pygame-objekten saknar detta (åtminstone Surface och Sound och i dessa fall är det nog vettigt att hantera utanför pickle pga. datamängden).

För att gå runt det här problemet måste de felande objekten raderas från listan av objekt som sparas och sedan manuellt återställas när objekten laddas in igen. Detta görs genom att implementera en egen variant av __getstate__() och __setstate__():

def __getstate__(self):
print 'getting state!'
state = dict(self.__dict__)
#remove unpicklables
state = {s:state[s] for s in state if s != pygame.Surface and s != pygame.Sound}
return state

def __setstate__(self, state):
self.__dict__ = state
self.a = animate.animationGroup()
self.restore_unpicklables()

När pickle.dump sparar objektet anropas mitt objekts __getstate__() och innan uppslagsverket med alla objekt returneras plockas alla objekt som inte kan serialiseras bort (här pygame.Surface-objekt och pygame.Sound-objekt). När sedan objektet skall återställas kallar pickle.load()__setstate__() för att återställa objektets interna uppslagsverk anropas funktioner för att återskapa de borttagna objekten.

Det här metoden att hantera sparfiler på har både brister och fördelar men fungerar bra under utvecklingen och tog en halvtimme att skriva ihop. Den stora fördelen var att jag snabbt kunde komma igång och få någonting som fungerar. Den stora nackdelen är att spelarobjektets blir exakt som det var när det sparades och ifall variabler eller nya funktioner tillkommit kommer dessa saknas vilket kan leda till strul. Sedan bör man nämna att picklade objekt är relativt osäkra i och med att allt lagras i ett helt öppet format och inga kontroller genomförs innan datan laddas in.

* Bilden kommer härifrån, rekommenderas för inspiration för heminlagda gurkor!

PyCon Sweden 2014

maj 30, 2014

Tisdag till Onsdag förra veckan gick första upplagan av PyCon Sweden av stapeln och ett par hundra python intresserade människor trängde ihop sig i KTHs Q-hus för att lyssna på föredrag och umgås. Jag var där, lyssnade på intressanta föredrag och pratade med ett gäng intressanta personer.

Administrativt fungerade det mesta bra, finfina luncher och bra info (även om schemat släpptes rätt sent). Inga katastrofer och stämningen var bra. Lunchuppehållen var rätt långa vilket gav tid till att umgås och prata med folk.

Pythonistas på grönbete.

Pythonistas på grönbete.

Föreläsningarna var överlag intressanta och innefattade många olika ämnen, allt ifrån att bygga ett tag-in-system för ett gym till hur kryptografi fungerar på internet och vad svt pejl sysslar med.

Den i särklass bästa föreläsningen var Kenneth Reitz föredrag om den splittring som håller på att ske i Python. Skillnaderna mellan python2 och python3 skapar sprickor i python-gemenskapen. Dels mellan användare av de olika varianterna (min mjukvara kan inte köra på din dator för att du har en äldre version av python), och dels mellan användare och utvecklare (de flesta användare använder fortfarande python2 medan utvecklare är mer intresserade av python 3). Enligt en snabb överslagsräkning är det endast 4.3% av användarna som använder python3. Detta är problematiskt då det lämnar mycket buggar i bibliotek som portats till python3 till och med i standard biblioteket finns det mycket oegentligheter. Lösningen är enkel men svår, vi måste engagera oss. Vi måste göra vad vi kan för att ena vår gemenskap. Stöd python3, stöd python2, ansträng dig, rapportera de buggar du hittar!

På tåget hem reflekterade jag över hur fantastiskt det är med den gemenskap som finns. Alla dessa människor som rest in för att hålla ett föredrag utan att få mer än fri entré till konferensen. Alla dessa smarta människor som tar den tid det krävs för att utbilda och dela kunskap med sådana som mig. Alla dessa människor som har slitit i veckor för att fixa alla luncher, lokaler, scheman och alla andra detaljer som skall klaffa.

Ett stort tack till arrangörerna, föreläsare och deltagare!

Resurs-cachning i python

november 10, 2013

I Pixel Wizard instansieras nya objekt relativt ofta under spelets gång, eldbollar och andra magier som spelaren skapar och projektiler som fiender skjuter iväg. Objekten skapas raskt men när grafik och ljud laddas märks en tydlig prestandaminskning. För att komma runt detta skrev jag ett mycket enkelt system för att cacha ljud och grafik:

"""Resource loader for images and sound"""

import pygame
import re

resource_dict = {}

def load(filename):
    if not filename in resource_dict:
        if re.match(".+\.wav", filename):
            r = pygame.mixer.Sound(filename)
        elif re.match(".+\.png", filename):
            r = pygame.image.load(filename)
        resource_dict[filename] = r
    return resource_dict[filename]

def clear():
    resource_dict = {}

Koden ovan utnyttjar pythons dictionary datatyp för att enkelt med väldigt få rader kod bygga ett caching system. resource_dict är en dictionary där en sökväg/filnamn används som nyckel och värdet är ett objekt som innehåller en referens till ett objekt för datat som filen innehåller.

load() kontrollerar ifall sökvägen filename är en nyckel i resource_dict, om den finns returneras objektet som är kopplat till nyckeln. Ifall den inte finns laddas datat från filen (en kontroll ifall det är en bild eller en ljudfil görs) och sökvägen läggs in i dictionaryn tillsammans med det nya objektet, efter detta returneras objektet.

clear() nyttjar pythons garbage collector och tar helt enkelt bort referensen till objektet, när alla andra referenser till objektet har tagits bort kommer det raderas automatiskt.

Detta system gör också att samma grafik inte laddas in flera gånger även om ett det finns 17 kopior av en fiende refererar grafiken (och ljuden) till ett och samma objekt.

Flera monster men ett grafikobjekt

Flera monster men ett grafikobjekt

Pixel Wizard

oktober 27, 2013

(ber om ursäkt för hackigheten i videon, nånting gick inte bra när jag laddade upp den på youtube)Pixel Wizard är mitt metroidvania-inspirerade plattformspel som jag nyttjar för att utöka mina python-kunskaper.

Spelet är skrivet i python med pygame-modulen för grafik, ljud och hantering av joypad/tangentbord. Jag började projektet för mer än ett år sedan och har arbetat på det i perioder, fördelen med att använda python är att det går förhållandevis snabbt att lägga till saker. Existerande klasser kan utökas och garbage collection och dictionaries kan användas för att snabbt ge resultat. Nackdelen är att det aldrig blir lika snabbt som ett spel skrivet i c++ men det viktiga är inte resultatet utan att göra det och att jag lär mig nya saker.

Jag har i många fall inte varit så noga med att tänka igenom saker och ting utan att bygga nytt och när det sedan finns städa upp. Denna filosofi ger snabba resultat men det har lämnat mycket kod som är halvt odokumenterad och som är i behov av att städas, men eftersom jag är ensam i projektet fungerar det bra.

Det som har tagit mycket mer tid än jag räknat med är att skapa grafik (byggblock, fiender och annat) och bygga nivåer. Koden är enkel men sedan krävs det att prova en hel del för att förstå hur det hela fungerar rent spelmässigt. Kikar men på grafiken så syns det att det är något av en amatör bakom penseln men det är kul och jag tror jag blir bättre på att pixla grafik och animera.

Projektet är fortfarande roligt och jag kommer arbeta vidare på det som ett hobby-projekt under lediga stunder. Förhoppningsvis kommer jag att lägga upp ett par ytterligare blogg-poster angående olika delar i arbetet och tekniker jag använder.

Alla kommentarer är välkomna!

Python i samväldet

oktober 3, 2013

Den 20-23:e september gick PyConUK av stapeln, och lyckades ta mig dit trots en tågresa från Gatwick med mängder av stopp. Ett längre stopp i Bletchley lät mig ta en snabb språngmarsch genom Museet på Bletchley Park.

En Enigma maskin

En Enigma maskin

Själva konferensen började på fredagsmorgonen med frukost och välkomnande. Det genomgående temat var ”To be perfect is to change”, vilket innebar att mycket förändringar skedde med lite förvarning. Det var inte alltid åhörare hamnade på rätt föreläsning och det var inte alltid föreläsarna hamnade i rätt rum.

Zeth önskar oss välkomna.

Zeth önskar oss välkomna.

Det var lite rörigt men det vara roligt hela tiden, en stackars föreläsare startade för tidigt men räddade upp situationen genom att dra föreläsningen en gång till.

Det var många bra och intressanta föreläsningar och mycket av dem finns upplagda här. Några personliga favoriter var

  • Cooking high quality software -I denna föreläsning utsattes den brett flinande publiken för en serie dåliga skämt av en man med suspekt fransk brytning iförd kockkläder. Parallell efter krystad parallell mellan mjukvara och restaurangmatlagning levererades till åhörarnas jubel. Det var roligt men samtidigt tog den upp många av de moderna/trendiga mjukvara-utvecklingsteknikerna.
  • All-singing all-dancing python bytecodeLarry Hastings gick igenom pythons byte-kod och förklarade hur den fungerade och varför det var en dålig idé att använda den. För att förstå pythons byte kod måste man ha en djup förståelse om hur språket fungerar, så mycket gick mig över huvudet. Detta till trots var det en väldigt intressant föreläsning.
  • Turtles – Detta var två seminarier om olika förbättringar av turtle, ett ramverk för att underlätta undervisning av programmering med hjälp av sköldpaddor. I grunden kan man endast styra sköldpaddorna genom att rotera dem och förflytta dom framåt och bakåt. physical turtle ger sköldpaddorna möjlighet att kollidera med väggar och ninja turtle är ett projekt för att modularisera turtle och ge modulen ett fräschare utseende.
  • Ubiqutous Larch: Ett ramverk för samarbete via webben. Anteckningsblock där dokumentation, python-kod och java script kan skrivas tillsammans med andra. Koden exekveras live och förändringar i koden slår igenom direkt. Uppenbarligen påminner den en del om iPython.

På lördag eftermiddag fanns möjligheten att delta i en kod-dojo, där man i grupp försöker lösa något problem. Vår uppgift var att implementera ett enkelt plattforms-spel i minecraft där man flyttades till startpositionen ifall man trillade ner från plattformen. Arbetsformen var väldigt intressant och alla deltog glatt. Jag skulle gärna vilja delta i fler sådana här dojos, möjligheten att lära är stor.

Konferensens var inte bara seminarier och övningar utan också en hel del socialiserande. På fredagen var det en enkel middag på en närbelägen pub och på lördagen var det en ordentlig middagsbjudning. Det diskuterades allt ifrån programmeringstekniska frågor till feminism till Monty Python.

Monty Python-referenser: The Larch, dead-parrot-state, Maynard och wafer thin mint.

Jag lärde mig mycket, den mest grundläggande upptäckten var unpack-tuple operatorn (*). Andra grundläggande upptäckter var pakethanteraren pypi samt det mycket praktiska virtualenv.

Det var en kul konferens och det har gett mig mersmak både för python och konferenser generellt.

Mutable/immutable i Python

december 12, 2012

I python har en klass två huvudattribut: huruvida den är

  • möjlig att förändra (mutable)
  • ifall den är unikt identifierbar (hashable)

Dessa koncept är relativt enkla att förstå sig på när man inte funderar aktivt på saken men försöker man precisera vad som gör ett objekt förändringsbart och vad som gör det unikt identifierbart tycker jag det är klurigt.

Till exempel är alla numeriska variabler immutable vilket innebär att man inte kan skicka t.ex en integer till en funktion göra ett antal operationer och sedan returnera och tro att den integer man använt är förändrad.

Ex:

def add_two(i):
  i + 2

a = 1
add_two(a)
print a

i exemplet ovan kommer a fortfarande vara 1 i print-satsen eftersom i funktionen add_two kommer en kopia användas då a är immutable. I C används pekare för att skilja på referenser och värden men ingen sådan mekanism verkar finnas i python. jag har själv lyckats skapa ett eget förändringsbart (mutable) heltal men jag är lite osäker på vad som håller den förändringsbar och ifall jag på något vis kan uppdatera den så att den plötsligt inte är förändringsbar (immutable). Någon python-guru där ute som har något råd?

mitt grundläggande förändringsbara heltal:

class mutable_int():
    a = 1
    def __init__(self):
        pass
    def __setattribute__(self, v):
        self.a = v
    def __add__(self, v):
        self.a += v
    def __getattribute(self):
        return self.a
    def __repr__(self):
        return str(self.a)

Men hur konstruerar jag en klass som inte är förändringsbar?

Jag återkommer vid tillfälle och frågar om den andra aspekten av variabler, hashningsbarhet.

Profilering med python

juli 20, 2012

För att ta reda på vad som tar tid i ett program brukar man säga att man profilerar programmet. Detta är inte mycket mer än ett fint ord för att på ett standardiserat sätt att ta tid på en process och analysera var tid spenderas. Profilering är ett fantastiskt verktyg ifall man vet att man har problem med att någon operation tar mer tid än den bör. Utan profilering kan man endast göra intelligenta gissningar och försöka uppskatta huruvida en förändring av programmet har gjort någon nytta och snabbat upp den långsamma sektorn.

I python ingår standardbiblioteket cProfile för detta ändamål. Biblioteket/modulen är relativt enkelt att använda och ger väldigt tydliga resultat.

Mitt pyparallax-projekt tyckte jag ibland var lite segare vid utritning än önskvärt och efter lite studier lyckades jag skapa ett enkelt skript för att profilera modulen:

import pygame
import parallax
import cProfile # import profiler module 
# init pygame
pygame.init()
screen = pygame.display.set_mode((640, 480), pygame.DOUBLEBUF)
# init parallax
surface ps = parallax.ParallaxSurface()
ps.add('../demo/p2.png', 10)
ps.add('../demo/p1.png', 10)
# create a profiler object
p = cProfile.Profile()
# run ps.draw via the profiling object
for i in range(1000):
  p.runcall(ps.draw, screen)
# create and print stats p.create_stats() p.print_stats()

I korthet

  • importeras modulen för profilering
  • normal initiering genomförs
  • profileringsobjektet p skapas
  • p.run_call() anropas med ps.draw angiven som funktion att köra
  • profilerings information skrivs ut

detta resulterade i

  ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  1000    0.122    0.000   36.688    0.037 parallax.py:50(draw)
  4000   36.547    0.009   36.547    0.009 {method 'blit' of 'pygame.Surface' objects}
  1000    0.003    0.000    0.003    0.000 {method 'disable' of '_lsprof.Profiler' objects}
  1000    0.003    0.000    0.003    0.000 {method 'get_height' of 'pygame.Surface' objects}
  3000    0.015    0.000    0.015    0.000 {method 'get_width' of 'pygame.Surface' objects}

Som man ser i tabellen ovan spenderas klart mest tid i blit-operationen. Efter att läst på lite grann får jag reda på att ifall man använder färg-mappad-transparens (color keyed) dvs, man låter en särskild färg representera transparens, måste varje pixel utvärderas för sig. Snabbt inser jag att man enkelt kan ta bort detta från det bakersta lagret (som alltid måste vara heltäckande). Kod för detta sätts in i parallax-modulen

        if len(self.levels) > 0:
            image.set_colorkey((0xff, 0x00, 0xea))

dvs. aktivera endast färgmappad transparens ifall detta inte är det första (bakersta) lagret.

Vid körning av profilering-skriptet fås nu


ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  1000    0.105    0.000   15.414    0.015 parallax.py:50(draw)
  4000   15.293    0.004   15.293    0.004 {method 'blit' of 'pygame.Surface' objects}
  1000    0.003    0.000    0.003    0.000 {method 'disable' of '_lsprof.Profiler' objects}
  1000    0.003    0.000    0.003    0.000 {method 'get_height' of 'pygame.Surface' objects}
  3000    0.013    0.000    0.013    0.000 {method 'get_width' of 'pygame.Surface' objects}

cumtime har nu halverats vilket är gott nog åt mig. Ytterligare besparingar kan göras genom att buffra olika lager av bakgrunden och endast uppdatera delarna ifall någon av underlagren har uppdaterats. Detta kommer dock öka komplexiteten markant och är i dagsläget inte motiverat.

pylint

juni 30, 2012

Pylint är ett verktyg för att utföra statiska tester av python-kod. Statiska tester innebär att koden läses igenom manuellt och kontrolleras mot en standard. I grund-konfigurationen testas koden mot PEP8 men regler kan läggas till och tas bort som det passar.

Jag testade pylint på mitt parallax-projekt och efter första körningen fick jag massor av varningar, brott mot standarden samt ett regelrätt fel:

Messages by category
--------------------

+-----------+-------+---------+-----------+
|type       |number |previous |difference |
+===========+=======+=========+===========+
|convention |21     |21       |=          |
+-----------+-------+---------+-----------+
|refactor   |1      |1        |=          |
+-----------+-------+---------+-----------+
|warning    |293    |293      |=          |
+-----------+-------+---------+-----------+
|error      |1      |1        |=          |
+-----------+-------+---------+-----------+

Detta ledde till en slutpoäng på -81.43/10, uppenbarligen har jag lite att göra ifall standarden ska uppfyllas.

En av de enkla varningarna var

W: 49: Found indentation with tabs instead of spaces

men detta löses rätt snabbt genom en snabb sök-och-ersätt.

Något som såg besvärligt ut var den långa raddan varningar om icke-använda importerade objekt

[...]
W: 22: Unused import K_HOME from wildcard import
W: 22: Unused import GL_ACCUM_RED_SIZE from wildcard import
W: 22: Unused import BLEND_RGB_MIN from wildcard import
W: 22: Unused import K_RIGHT from wildcard import
W: 22: Unused import GL_RED_SIZE from wildcard import
W: 22: Unused import HAT_RIGHT from wildcard import
W: 22: Unused import color from wildcard import
W: 22: Unused import K_GREATER from wildcard import
[...]

dessa orsakades av den generösa importeringen av bra att ha definitioner

from pygame.locals import *

Lyckligtvis var lösningen enkel eftersom ingen av dessa definitioner användes så raden togs prompt bort.

Ett regelrätt fel hade också smugit sig in

E: 47:parallaxSurface.addLevel: Undefined variable 'message'

message returnerades i en exception handler

     try:
            image = (pygame.image.load(imagePath))
        except:
            printf "couldn't open image:", imagePath
            raise SystemExit, message

Problemet löses snabbt genom att ändra printf-raden till

            message = "couldn't open image: " + imagePath
            printf message

Meddelanden märkta C indikerar brott mot standarden. T.ex

C: 55: Line too long (81/80)

Detta är ett klassiskt meddelande som härstammar från tidernas begynnelse när terminalerna endast var 80 tecken breda och för att koden skall läsas lätt på samtliga plattformar skall denna längd inte överskridas.

När ovanstående problem lösts landade slutbetyget på 3.24/10!

Utöver ett par enkla varningar återstod ett par besvärliga feltyper:

C: 23:parallaxSubSurface: Missing docstring

R: 23:parallaxSubSurface: Too few public methods (0/2)

C: 35:parallaxSurface.__init__: Invalid name "parallaxLevels" (should match [a-z_][a-z0-9_]{2,30}$)

De första två rör klassen parallaxSubsurface och den saknade dokumentations-strängen* är lätt ordnad men Too few public methods meddelandet säger egentligen att mitt sätt att använda en klass för att organisera information är fel och jag måste nog fundera ett tag innan jag gör något.

Det sista meddelandet säger att mina variabelnamn strider mot standarden. Jag är väldigt förtjust i camelCase, men för lite extra poäng gör jag mycket!

Resultatet av övningen kan ses i denna commit. Skillnaden är inte särskilt stor men läsbarheten är en aning bättre samtidigt som ett regelrätt fel har lösts. Det slutgiltiga resultatet jag uppnådde var 9.68, det återstående felet är refaktoreringsförslaget pga. att min hjälpklass saknar metoder. Denna ska jag fundera på och se ifall jag kan göra samma sak på ett annat sätt som är lika tydligt. Jag måste säga att jag blev positivt överraskad av det här verktyget och kommer fortsätta använda det i mina python-projekt.

* docstring är en del av pythons inbyggda dokumentationssystem. Dokumentation av en klass läggs in som en sträng först i klassen (precis efter den deklarerats). Samma sak gäller för moduler och funktioner.

Besynnerligheter

februari 16, 2012

Via diaspora blir man snabbt bekant med nya fräscha nördigheter, nu senast en presentation av ruby och java scripts små nycker och besynnerligheter. I samma anda hittade jag också (via planet Debian) en beskrivning av en av pythons egenheter:

>>> x = ['a', 'b']
>>> x
['a', 'b']
>>> x += 'c'
>>> x
['a', 'b', 'c']
>>> x += ''
>>> x
['a', 'b', 'c']
>>> x += ['']
>>> x
['a', 'b', 'c', '']

Det tog ett tag för mig att förstå hur detta fungerade trots att jag läst förklaringen pga. lite handikapp från C där ” betyder ett tecken medan i python representerar det en sträng. Koden ovan visar att ifall man lägger till en tom sträng (”) till en lista med tecken läggs inget till men om man lägger till en lista med ett tomt tecken ([”]) till en list läggs ett tomt tecken till. Detta innebär att det tomma tecknet är ett giltigt tecken men kan inte läggas in i en lista med den vanliga +-operatorn. däremot kan + operatorn lägga till en lista som redan innehåller den tomma strängen. Det här beror på att python bara kan addera listor, i fallet

x += y

där x är en lista måste y också vara en lista. Men i exemplet ovan adderades strängar till listan, detta går endast eftersom strängar kan ses som en lista av tecken. den tomma strängen (”) anses då vara en lista med 0 element och då adderas ingenting. om vi deklarerar en lista med en tom sträng ([”]) kan vi dock utföra operationen för i detta fallet har vi en lista med 1 element (den tomma strängen).