Frågor och svar om biblioteket och extension

Allmänna frågor om biblioteket

Hur hittar jag en modul eller applikation för att utföra uppgift X?

Kontrollera the Library Reference för att se om det finns en relevant modul i standardbiblioteket. (Så småningom lär du dig vad som finns i standardbiblioteket och kan då hoppa över det här steget)

För tredjepartspaket, sök i Python Package Index eller prova Google eller en annan webbsökmotor. Om du söker efter ”Python” plus ett eller två nyckelord för det ämne du är intresserad av hittar du vanligtvis något användbart.

Var är källfilen math.py (socket.py, regex.py, etc.)?

Om du inte kan hitta en källfil för en modul kan det vara en inbyggd eller dynamiskt laddad modul som implementerats i C, C++ eller annat kompilerat språk. I det här fallet kanske du inte har källfilen eller så kan den vara något i stil med mathmodule.c, någonstans i en C-källkatalog (inte på Python Path).

Det finns (minst) tre olika typer av moduler i Python:

  1. moduler skrivna i Python (.py);

  2. moduler skrivna i C och dynamiskt laddade (.dll, .pyd, .so, .sl, etc);

  3. moduler skrivna i C och länkade med tolken; för att få en lista över dessa, skriv:

    import sys
    print(sys.builtin_module_names)
    

Hur gör jag ett Python-skript körbart på Unix?

Du måste göra två saker: skriptfilens läge måste vara exekverbart och den första raden måste börja med #! följt av sökvägen till Python-tolken.

Det första görs genom att köra chmod +x scriptfile eller kanske chmod 755 scriptfile.

Det andra kan göras på ett antal olika sätt. Det mest okomplicerade sättet är att skriva

#!/usr/local/bin/python

som den allra första raden i din fil, med sökvägen till den plats där Python-tolken är installerad på din plattform.

Om du vill att skriptet ska vara oberoende av var Python-tolken finns, kan du använda programmet env. Nästan alla Unix-varianter stöder följande, förutsatt att Python-tolken finns i en katalog på användarens PATH:

#!/usr/bin/env python

Gör inte detta för CGI-skript. Variabeln PATH för CGI-skript är ofta mycket minimal, så du måste använda det faktiska absoluta sökvägsnamnet för tolken.

Ibland är en användares miljö så full att programmet /usr/bin/env misslyckas; eller så finns det inget env-program alls. I så fall kan du prova följande hack (tack vare Alex Rezinsky):

#! /bin/sh
""":"
exekvera python $0 ${1+"$@"}
"""

Den lilla nackdelen är att detta definierar skriptets __doc__-sträng. Du kan dock åtgärda det genom att lägga till

__doc__ = """...Vad som helst..."""

Finns det ett curses/termcap-paket för Python?

För Unix-varianter: Standarddistributionen av Python-källkod innehåller en curses-modul i underkatalogen Modules, men den är inte kompilerad som standard. (Observera att detta inte är tillgängligt i Windows-distributionen - det finns ingen curses-modul för Windows)

Modulen curses stöder de grundläggande funktionerna i curses samt många ytterligare funktioner från ncurses och SYSV curses, t.ex. färg, stöd för alternativa teckenuppsättningar, block och musstöd. Detta innebär att modulen inte är kompatibel med operativsystem som endast har BSD curses, men det verkar inte finnas några operativsystem som för närvarande underhålls som faller inom denna kategori.

Finns det en motsvarighet till C:s onexit() i Python?

Modulen atexit tillhandahåller en registerfunktion som liknar C:s onexit().

Varför fungerar inte mina signalhanterare?

Det vanligaste problemet är att signalhanteraren deklareras med fel argumentlista. Det kallas som

handler(signum, ram)

så den bör deklareras med två parametrar:

def handler(signum, ram):
    ...

Vanliga uppgifter

Hur testar jag ett Python-program eller en Python-komponent?

Python levereras med två testramverk. Modulen doctest hittar exempel i dokumentationen för en modul, kör dem och jämför resultatet med det förväntade resultatet som anges i dokumentationen.

Modulen unittest är ett mer avancerat testramverk som bygger på testramverken för Java och Smalltalk.

För att göra testningen enklare bör du använda en bra modulär design i ditt program. Programmet bör ha nästan all funktionalitet inkapslad i antingen funktioner eller klassmetoder – och detta har ibland den överraskande och förtjusande effekten att programmet körs snabbare (eftersom lokala variabelåtkomster är snabbare än globala åtkomster). Dessutom bör programmet undvika att vara beroende av muterande globala variabler, eftersom detta gör testning mycket svårare att genomföra.

Den ”globala huvudlogiken” i ditt program kan vara så enkel som

om __name__ == "__main__":
    main_logic()

längst ner i huvudmodulen i ditt program.

När ditt program är organiserat som en hanterbar samling funktions- och klassbeteenden bör du skriva testfunktioner som utövar beteendena. En testsvit som automatiserar en sekvens av tester kan associeras med varje modul. Det här låter som en massa arbete, men eftersom Python är så kortfattat och flexibelt är det förvånansvärt enkelt. Du kan göra kodningen mycket trevligare och roligare genom att skriva dina testfunktioner parallellt med ”produktionskoden”, eftersom det gör det lätt att hitta buggar och till och med designfel tidigare.

”Stödmoduler” som inte är avsedda att vara huvudmodulen i ett program kan innehålla ett självtest av modulen:

if __name__ == "__main__":
    self_test()

Även program som interagerar med komplexa externa gränssnitt kan testas när de externa gränssnitten inte är tillgängliga genom att använda ”falska” gränssnitt som implementerats i Python.

Hur skapar jag dokumentation från dokumentsträngar?

Modulen pydoc kan skapa HTML från doc-strängarna i din Python-källkod. Ett alternativ för att skapa API-dokumentation enbart från docstrings är epydoc. Sphinx kan också inkludera innehåll från docstrings.

Hur får jag en enda knapptryckning i taget?

För Unix-varianter finns det flera lösningar. Det är enkelt att göra detta med hjälp av curses, men curses är en ganska stor modul att lära sig.

Trådar

Hur programmerar jag med trådar?

Var noga med att använda modulen threading och inte modulen _thread. Modulen threading bygger praktiska abstraktioner ovanpå de primitiver på låg nivå som tillhandahålls av modulen _thread.

Ingen av mina trådar verkar köras: varför?

Så snart huvudtråden avslutas dödas alla trådar. Din huvudtråd körs för snabbt, vilket gör att trådarna inte hinner göra något arbete.

En enkel lösning är att lägga till en sleep i slutet av programmet som är tillräckligt lång för att alla trådar ska hinna bli klara:

import gängning, tid

def thread_task(namn, n):
    för i i intervall(n):
        print(namn, i)

för i inom intervallet(10):
    T = threading.Thread(target=thread_task, args=(str(i), i))
    T.start()

time.sleep(10) # <---------------------------!

Men nu (på många plattformar) körs trådarna inte parallellt, utan verkar köras sekventiellt, en i taget! Anledningen är att OS:ets trådschemaläggare inte startar en ny tråd förrän den föregående tråden har blockerats.

En enkel lösning är att lägga till en liten sömn i början av körfunktionen:

def thread_task(namn, n):
    time.sleep(0.001) # <--------------------!
    för i i intervallet(n):
        print(namn, i)

för i i intervallet(10):
    T = threading.Thread(target=thread_task, args=(str(i), i))
    T.start()

tid.sömn(10)

Istället för att försöka gissa ett bra fördröjningsvärde för time.sleep() är det bättre att använda någon form av semaformekanism. En idé är att använda modulen queue för att skapa ett köobjekt, låta varje tråd lägga till en token till kön när den är klar och låta huvudtråden läsa så många tokens från kön som det finns trådar.

Hur delar jag upp arbetet mellan ett antal arbetstrådar?

Det enklaste sättet är att använda modulen concurrent.futures, särskilt klassen ThreadPoolExecutor.

Eller, om du vill ha fin kontroll över dispatching-algoritmen, kan du skriva din egen logik manuellt. Använd modulen queue för att skapa en kö som innehåller en lista med jobb. Klassen Queue underhåller en lista med objekt och har en metod .put(obj) som lägger till objekt i kön och en metod .get() som returnerar dem. Klassen tar hand om den låsning som krävs för att säkerställa att varje jobb delas ut exakt en gång.

Här är ett trivialt exempel:

import trådning, , tid

# Arbetartråden tar bort jobb från kön.  När kön är tom antar den
# antar den att det inte kommer att finnas mer arbete och avslutas.
# (Realistiskt sett körs arbetare tills de avslutas.)
def arbetare():
    print('Kör arbetare')
    time.sleep(0.1)
    medan True:
        try:
            arg = q.get(block=False)
        except queue.Empty:
            print('Worker', threading.current_thread(), end=' ')
            print('kö tom')
            break
        annars:
            print('Worker', threading.current_thread(), end=' ')
            print('kör med argument', arg)
            tid.sömn(0.5)

# Skapa kö
q = queue.Queue()

# Starta en pool med 5 arbetare
för i i intervallet(5):
    t = threading.Thread(target=arbetare, name='arbetare %i' % (i+1))
    t.start()

# Börja lägga till arbete i kön
för i i intervallet(50):
    q.put(i)

# Ge trådarna tid att köra
print('Huvudtråden sover')
time.sleep(5)

När detta körs kommer det att ge följande resultat:

Running worker
Running worker
Running worker
Running worker
Running worker
Main thread sleeping
Worker <Thread(worker 1, started 130283832797456)> running with argument 0
Worker <Thread(worker 2, started 130283824404752)> running with argument 1
Worker <Thread(worker 3, started 130283816012048)> running with argument 2
Worker <Thread(worker 4, started 130283807619344)> running with argument 3
Worker <Thread(worker 5, started 130283799226640)> running with argument 4
Worker <Thread(worker 1, started 130283832797456)> running with argument 5
...

Läs modulens dokumentation för mer information; klassen Queue ger ett användbart gränssnitt.

Vilka typer av mutation av globala värden är tråd-säkra?

En global interpreter lock (GIL) används internt för att säkerställa att endast en tråd körs i Python VM åt gången. I allmänhet erbjuder Python att växla mellan trådar endast mellan bytecode-instruktioner; hur ofta den växlar kan ställas in via sys.setswitchinterval(). Varje bytecode-instruktion och därmed all C-implementeringskod som nås från varje instruktion är därför atomisk ur ett Python-programs synvinkel.

I teorin innebär detta att en exakt redovisning kräver en exakt förståelse av PVM:s bytecode-implementering. I praktiken innebär det att operationer på delade variabler av inbyggda datatyper (ints, lists, dicts, etc) som ”ser atomiska ut” verkligen är det.

Till exempel är följande operationer alla atomära (L, L1, L2 är listor, D, D1, D2 är dikter, x, y är objekt, i, j är ints):

L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.fält = y
D[x] = y
D1.update(D2)
D.nycklar()

De här är inte:

i = i+1
L.append(L[-1])
L[i] = L[j]
D[x] = D[x] + 1

Operationer som ersätter andra objekt kan åberopa dessa andra objekts __del__()-metod när deras referensantal når noll, och det kan påverka saker och ting. Detta gäller särskilt för massuppdateringar av lexikon och listor. När du är osäker, använd en mutex!

Kan vi inte bli av med det globala tolklåset?

Den global interpreter lock (GIL) ses ofta som ett hinder för Pythons användning på avancerade servermaskiner med flera processorer, eftersom ett flertrådat Python-program i praktiken bara använder en CPU, på grund av att (nästan) all Python-kod bara kan köras medan GIL hålls.

Med godkännande av PEP 703 pågår nu arbetet med att ta bort GIL från CPython-implementeringen av Python. Inledningsvis kommer det att implementeras som en valfri kompilatorflagga när tolken byggs, och därför kommer separata byggen att finnas tillgängliga med och utan GIL. På lång sikt är förhoppningen att man ska kunna nöja sig med en enda version, när man väl har förstått prestandakonsekvenserna av att ta bort GIL. Python 3.13 kommer sannolikt att vara den första utgåvan som innehåller detta arbete, även om det kanske inte är helt funktionellt i den här utgåvan.

Det nuvarande arbetet med att ta bort GIL är baserat på en fork av Python 3.9 med GIL borttagen av Sam Gross. Dessförinnan, under Python 1.5, implementerade Greg Stein faktiskt en omfattande patchuppsättning (”free threading”-patcherna) som tog bort GIL och ersatte den med finkornig låsning. Adam Olsen gjorde ett liknande experiment i sitt python-safethread-projekt. Tyvärr uppvisade båda dessa tidigare experiment en kraftig minskning av prestandan för en tråd (minst 30% slägre) på grund av mängden finkornig låsning som krävs för att kompensera för borttagningen av GIL. Python 3.9-gaffeln är det första försöket att ta bort GIL med en acceptabel prestandapåverkan.

Närvaron av GIL i nuvarande Python-utgåvor betyder inte att du inte kan använda Python bra på multi-CPU-maskiner! Du måste bara vara kreativ när det gäller att dela upp arbetet mellan flera processer snarare än flera trådar. Klassen ProcessPoolExecutor i den nya concurrent.futures-modulen ger ett enkelt sätt att göra det; multiprocessing-modulen ger ett API på lägre nivå om du vill ha mer kontroll över utskick av uppgifter.

Om du använder ett C-tillägg för att utföra en tidskrävande uppgift kan tillägget släppa GIL medan exekveringstråden befinner sig i C-koden och låta andra trådar få lite arbete gjort. Vissa standardbiblioteksmoduler som zlib och hashlib gör redan detta.

En alternativ metod för att minska effekten av GIL är att göra GIL till ett lås per tolkningsstatus i stället för ett globalt lås. Detta implementerades först i Python 3.12 och finns tillgängligt i C API. Ett Python-gränssnitt för det förväntas i Python 3.13. Den största begränsningen för det just nu är sannolikt tilläggsmoduler från tredje part, eftersom dessa måste skrivas med flera tolkar i åtanke för att vara användbara, så många äldre tilläggsmoduler kommer inte att kunna användas.

Inmatning och utmatning

Hur tar jag bort en fil? (Och andra frågor om filer…)

Använd os.remove(filename) eller os.unlink(filename); för dokumentation, se modulen os. De två funktionerna är identiska; unlink() är helt enkelt namnet på Unix systemanrop för den här funktionen.

För att ta bort en katalog, använd os.rmdir(); använd os.mkdir() för att skapa en. os.makedirs(path) skapar alla mellanliggande kataloger i path som inte finns. os.removedirs(path) tar bort mellanliggande kataloger så länge de är tomma; om du vill ta bort ett helt katalogträd och dess innehåll, använd shutil.rmtree().

Om du vill byta namn på en fil använder du os.rename(old_path, new_path).

För att trunkera en fil öppnar du den med f = open(filename, "rb+") och använder f.truncate(offset); offset är standardvärdet för den aktuella sökpositionen. Det finns också os.ftruncate(fd, offset) för filer som öppnas med os.open(), där fd är filbeskrivaren (ett litet heltal).

Modulen shutil innehåller också ett antal funktioner för att arbeta med filer, bland annat copyfile(), copytree() och rmtree().

Hur kopierar jag en fil?

Modulen shutil innehåller en copyfile()-funktion. Observera att på Windows NTFS-volymer kopieras inte alternativa dataströmmar eller resursförgreningar på macOS HFS+-volymer, även om båda nu sällan används. Den kopierar inte heller filbehörigheter och metadata, men om du använder shutil.copy2() istället bevaras det mesta (men inte allt).

Hur läser (eller skriver) jag binära data?

För att läsa eller skriva komplexa binära dataformat är det bäst att använda modulen struct. Den låter dig ta en sträng som innehåller binära data (vanligtvis siffror) och konvertera den till Python-objekt; och vice versa.

Följande kod läser till exempel två 2-bytes heltal och ett 4-bytes heltal i big-endian-format från en fil:

import struct

with open(filename, "rb") as f:
    s = f.read(8)
    x, y, z = struct.unpack(">hhl", s)

Bokstaven ”>” i formatsträngen tvingar fram big-endian-data; bokstaven ”h” läser ett ”kort heltal” (2 byte) och ”l” läser ett ”långt heltal” (4 byte) från strängen.

För data som är mer reguljär (t.ex. en homogen lista med ints eller floats) kan du också använda modulen array.

Anteckning

För att läsa och skriva binära data är det obligatoriskt att öppna filen i binärt läge (här genom att skicka "rb" till open()). Om du istället använder "r" (standard) kommer filen att öppnas i textläge och f.read() kommer att returnera str-objekt istället för bytes-objekt.

Jag verkar inte kunna använda os.read() på en pipe som skapats med os.popen(); varför?

os.read() är en lågnivåfunktion som tar en filbeskrivare, ett litet heltal som representerar den öppnade filen. os.popen() skapar ett filobjekt på hög nivå, samma typ som returneras av den inbyggda funktionen open(). För att läsa n byte från en pipe p som skapats med os.popen() måste du alltså använda p.read(n).

Hur kommer jag åt den seriella (RS232) porten?

För Win32, OSX, Linux, BSD, Jython, IronPython:

För Unix, se ett Usenet-inlägg av Mitch Chapman:

Varför stänger inte sys.stdout (stdin, stderr) verkligen sys.stdout?

Python filobjekt är ett abstraktionslager på hög nivå för C-filbeskrivare på låg nivå.

För de flesta filobjekt som du skapar i Python via den inbyggda open()-funktionen, markerar f.close() Python-filobjektet som stängt ur Pythons synvinkel, och ser också till att stänga den underliggande C-filbeskrivaren. Detta sker också automatiskt i f’s destruktor, när f blir skräp.

Men stdin, stdout och stderr behandlas speciellt av Python, på grund av den speciella status som de också har i C. Att köra sys.stdout.close() markerar filobjektet på Python-nivå som stängt, men stänger inte den associerade C-filbeskrivaren.

För att stänga den underliggande C-filbeskrivaren för en av dessa tre bör du först vara säker på att det är vad du verkligen vill göra (t.ex. kan du förvirra tilläggsmoduler som försöker göra I/O). Om det är det, använd os.close():

os.close(stdin.fileno())
os.close(stdout.fileno())
os.close(stderr.fileno())

Eller så kan du använda de numeriska konstanterna 0, 1 respektive 2.

Programmering av nätverk/internet

Vilka WWW-verktyg finns det för Python?

Se kapitlen Internetprotokoll och support och Datahantering på Internet i bibliotekets referensmanual. Python har många moduler som hjälper dig att bygga webbsystem på server- och klientsidan.

En sammanfattning av tillgängliga ramverk upprätthålls av Paul Boddie på https://wiki.python.org/moin/WebProgramming .

Vilken modul ska jag använda för att generera HTML?

Du kan hitta en samling användbara länkar på wikisidan för webbprogrammering.

Hur skickar jag e-post från ett Python-skript?

Använd standardbiblioteksmodulen smtplib.

Här är en mycket enkel interaktiv e-postavsändare som använder den. Den här metoden fungerar på alla värdar som har stöd för en SMTP-lyssnare:

import sys, smtplib

fromaddr = input("From: ")
toaddrs  = input("To: ").split(',')
print("Enter message, end with ^D:")
msg = ''
while True:
    line = sys.stdin.readline()
    if not line:
        break
    msg += line

# The actual mail send
server = smtplib.SMTP('localhost')
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

Ett alternativ för Unix använder sendmail. Platsen för sendmail-programmet varierar mellan olika system; ibland är det /usr/lib/sendmail, ibland /usr/sbin/sendmail. Manualsidan för sendmail hjälper dig. Här är några exempel på kod:

import os

SENDMAIL = "/usr/sbin/sendmail"  # sendmail location
p = os.popen("%s -t -i" % SENDMAIL, "w")
p.write("To: receiver@example.com\n")
p.write("Subject: test\n")
p.write("\n")  # blank line separating headers from body
p.write("Some text\n")
p.write("some more text\n")
sts = p.close()
if sts != 0:
    print("Sendmail exit status", sts)

Hur undviker jag blockering i connect()-metoden för ett uttag?

Modulen select används ofta för att hjälpa till med asynkron I/O på socklar.

För att förhindra att TCP-anslutningen blockeras kan du ställa in uttaget till icke-blockerande läge. När du sedan gör connect() kommer du antingen att ansluta omedelbart (osannolikt) eller få ett undantag som innehåller felnumret som .errno. errno.EINPROGRESS indikerar att anslutningen pågår, men inte har avslutats ännu. Olika operativsystem kommer att returnera olika värden, så du måste kontrollera vad som returneras på ditt system.

Du kan använda metoden connect_ex() för att undvika att skapa ett undantag. Den kommer bara att returnera errno värdet. För att polla kan du anropa connect_ex() igen senare – 0 eller errno.EISCONN indikerar att du är ansluten – eller så kan du skicka denna socket till select.select() för att kontrollera om den är skrivbar.

Anteckning

Modulen asyncio tillhandahåller ett allmänt asynkront bibliotek med en enda tråd och samtidiga trådar, som kan användas för att skriva icke-blockerande nätverkskod. Tredjepartsbiblioteket Twisted är ett populärt och funktionsrikt alternativ.

Databaser

Finns det några gränssnitt till databaspaket i Python?

Ja.

Gränssnitt för diskbaserade hashar som DBM och GDBM ingår också i standard Python. Det finns också modulen sqlite3, som tillhandahåller en lättviktig diskbaserad relationsdatabas.

Stöd för de flesta relationsdatabaser finns tillgängligt. Se wikisidan DatabaseProgramming för mer information.

Hur implementerar man persistenta objekt i Python?

Biblioteksmodulen pickle löser detta på ett mycket allmänt sätt (även om du fortfarande inte kan lagra saker som öppna filer, sockets eller fönster), och biblioteksmodulen shelve använder pickle och (g)dbm för att skapa beständiga mappningar som innehåller godtyckliga Python-objekt.

Matematik och numerik

Hur genererar jag slumpmässiga nummer i Python?

Standardmodulen random implementerar en slumptalsgenerator. Användningen är enkel:

import random
random.random()

Detta returnerar ett slumpmässigt flyttal i intervallet [0, 1].

Det finns också många andra specialiserade generatorer i denna modul, t.ex:

  • randrange(a, b) väljer ett heltal i intervallet [a, b].

  • uniform(a, b) väljer ett flyttal i intervallet [a, b].

  • normalvariate(mean, sdev) samplar den normala (gaussiska) fördelningen.

Vissa funktioner på högre nivå arbetar direkt med sekvenser, t.ex:

  • choice(S) väljer ett slumpmässigt element från en given sekvens.

  • shuffle(L) blandar en lista på plats, d.v.s. permuterar den slumpmässigt.

Det finns också en klass Random som du kan instansiera för att skapa oberoende multipla slumptalsgeneratorer.