Vanlig Python-syntax inom datavetenskap (grundläggande)
De senaste dagarna har jag läst boken Data Science from Scrach (PDF-länk), en utmärkt och lättförståelig introduktion till datavetenskap. Ett av kapitlen presenterar grundläggande Python-syntax och avancerad syntax som ofta används inom datavetenskap. Jag tyckte att beskrivningen var så bra, koncis och tydlig att jag bestämde mig för att översätta den och lägga upp den här som en minnesanteckning.
Vanlig Python-syntax inom datavetenskap (grundläggande)
Vanlig Python-syntax inom datavetenskap (avancerad)
Detta kapitel fokuserar på att introducera grundläggande Python-syntax och funktioner som är mycket användbara inom databearbetning (baserat på Python 2.7).
Indentering
Många språk använder parenteser för att kontrollera kodblock, men Python använder indentering:
for i in [1, 2, 3, 4, 5]:
print i # Första raden i "for i"-loopen
for j in [1, 2, 3, 4, 5]:
print j # Första raden i "for j"-loopen
print i + j # Sista raden i "for j"-loopen
print i # Sista raden i "for i"-loopen
print "done looping"
Detta gör Python-kod mycket lättläst, men det betyder också att du ständigt måste vara uppmärksam på formateringen. Mellanrum inom parenteser ignoreras, vilket är användbart när man skriver långa uttryck:
long_winded_computation = (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20)
Det gör också koden mer läsbar:
list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
easier_to_read_list_of_lists = [ [1, 2, 3],
[4 ,5 ,6 ],
[7 ,8 ,9 ] ]
Flerradiga satser
Man kan använda ett backslash för att indikera att två rader är sammankopplade (detta är sällan användbart):
two_plus_three = 2 + \
3
Moduler
Oavsett om det är inbyggda Python-moduler eller tredjepartsmoduler som du har laddat ner själv, måste de importeras manuellt för att kunna användas.
- Importera helt enkelt hela modulen direkt:
import re
my_regex = re.compile("[0-9]+", re.I)
Modulen re som importeras här används för reguljära uttryck. Efter att ha importerat en modul kan du direkt anropa specifika funktioner genom att använda modulnamnet som prefix (re.).
- Om modulnamnet som ska importeras redan används i koden, kan modulen mappas till ett annat namn vid importen:
import re as regex
my_regex = regex.compile("[0-9]+", regex.I)
- Om du är busig kan du importera hela modulen till det nuvarande namnområdet, vilket oavsiktligt kan skriva över variabler du redan har definierat:
match = 10
from re import * # Modulen re har en funktion som heter match
print match # Skriver ut match-funktionen
Eftersom du är en bra person litar jag på att du inte kommer att göra så.
Aritmetik
Python 2.7 använder heltaldivision som standard, så $ 5 / 2 = 2 $. Men ofta vill vi inte ha heltaldivision, så vi kan importera denna modul:
from __future__ import division
Efter importen blir $5 / 2 = 2.5$.
Heltalsdivision: $5 // 2 = 2$.
Funktioner
Funktionsdefinition
En funktion är en regel som kan ta emot noll eller fler indata och returnera en viss utdata. I Python definierar vi en funktion med def funktionsnamn(parametrar):
def double(x):
"""Här kan du skriva en förklaring av funktionens syfte
till exempel att den här funktionen multiplicerar indata med 2"""
# Här kan du skriva funktionens brödtext, kom ihåg att indentera
return x * 2
Funktionsanvändning
I Python är funktioner “första klassens medborgare”, vilket innebär att vi kan tilldela dem till variabler eller skicka dem som argument till andra funktioner:
def apply_to_one(f):
"""Anropar funktionen f och använder 1 som argument"""
return f(1)
my_double = double # double refererar till funktionen som definierades i föregående avsnitt
x = apply_to_one(my_double) # x är lika med 2
Anonyma funktioner
Man kan också skapa anonyma funktioner med lambda:
y = apply_to_one(lambda x: x + 4) # Är lika med 5
Man kan tilldela en lambda-funktion till en variabel, men de flesta rekommenderar att man istället använder def:
another_double = lambda x: 2 * x # Rekommenderas inte
def another_double(x): return 2 * x # Rekommenderad metod
Tillägg:
lambdaär bara ett uttryck; funktionskroppen är mycket enklare än fördef.- Kroppen i en
lambdaär ett uttryck, inte ett kodblock. Man kan bara kapsla in begränsad logik i ettlambda-uttryck.
Funktionsparameteröverföring
Funktionsparametrar kan definieras med standardvärden. Om inga argument anges vid anropet används standardvärdena, annars används de angivna värdena:
def my_print(message="my default message"):
print message
my_print("hello") # Skriver ut "hello"
my_print() # Skriver ut "my default message"
Ibland är det också praktiskt att specificera argumenten direkt med deras namn:
def subtract(a=0, b=0):
return a - b
subtract(10, 5) # Returnerar 5
subtract(0, 5) # Returnerar -5
subtract(b=5) # Samma som ovan, returnerar -5
Strängar
Man kan använda enkla eller dubbla citattecken för att skapa strängar (citattecknen måste matcha):
single_quoted_string = 'data science'
double_quoted_string = "data science"
Använd backslash för att representera escape-sekvenser, t.ex.:
tab_string = "\t" # Representerar tab-tecknet
len(tab_string) # Är lika med 1
När du vill använda backslash i sig (för Windows-kataloger eller reguljära uttryck), kan du definiera det med en rå sträng r"":
not_tab_string = r"\t" # Representerar tecknen '\' och 't'
len(not_tab_string) # Är lika med 2
Skapa flerradiga strängar med tre dubbla citattecken:
multi_line_string = """Detta är första raden
Detta är andra raden
Detta är tredje raden"""
Undantagshantering
När ett program stöter på ett fel, kastar Python ett undantag (exception). Om vi inte hanterar det, kommer programmet att avbrytas. Man kan fånga undantag med try- och except-satser:
try:
print 0 / 0
except ZeroDivisionError:
print "Kan inte dela med noll"
Även om undantag ofta ses som något negativt i andra språk, kan en robust undantagshantering i Python göra din kod mer koncis och ren.
Listor
Skapa listor
Listor är enkla ordnade samlingar och är en av de mest grundläggande datastrukturerna i Python (liknar arrayer i andra språk, men listor har några extra egenskaper). Så här skapar du en lista:
integer_list = [1, 2, 3]
heterogeneous_list = ["string", 0.1, True]
list_of_lists = [ integer_list, heterogeneous_list, [] ]
list_length = len(integer_list) # Är lika med 3
list_sum = sum(integer_list) # Är lika med 6
Åtkomst till värden i listor
Du kan komma åt värden i en lista genom att indexera med hakparenteser:
x = range(10) # Listan x blir [0, 1, ..., 9]
zero = x[0] # Är lika med 0, listindex börjar från 0
one = x[1] # Är lika med 1
nine = x[-1] # Är lika med 9, det sista elementet i listan
eight = x[-2] # Är lika med 8, det näst sista elementet i listan
x[0] = -1 # Listan x är nu [-1, 1, 2, 3, ..., 9]
Dela listor
Man kan dela upp listor med hakparenteser (slicing):
first_three = x[:3] # [-1, 1, 2]
three_to_end = x[3:] # [3, 4, ..., 9]
one_to_four = x[1:5] # [1, 2, 3, 4]
last_three = x[-3:] # [7, 8, 9]
without_first_and_last = x[1:-1] # [1, 2, ..., 8]
copy_of_x = x[:] # [-1, 1, 2, ..., 9]
Man kan använda in för att kontrollera om ett element finns i en lista:
1 in [1, 2, 3] # True
0 in [1, 2, 3] # False
Denna metod för att söka efter element är ineffektiv och bör endast användas när listan är liten eller när du inte är bekymrad över söktiden.
Sammanfoga listor
I Python är det lätt att sammanfoga två listor:
x = [1, 2, 3]
x.extend([4, 5, 6]) # x är nu [1,2,3,4,5,6]
Om du inte vill modifiera den ursprungliga listan x, kan du använda ‘plus’-operatorn för att skapa en ny lista:
x = [1, 2, 3]
y = x + [4, 5, 6] # y är nu [1, 2, 3, 4, 5, 6]; x är oförändrad
Det är vanligt att lägga till ett element i listan på följande sätt:
x = [1, 2, 3]
x.append(0) # x är nu [1, 2, 3, 0]
y = x[-1] # Är lika med 0
z = len(x) # Är lika med 4
Lista-uppackning
Om du vet hur många element det finns i en lista, är det enkelt att packa upp den:
x, y = [1, 2] # x är nu 1, y är 2
Om antalet element på båda sidor av tilldelningen inte stämmer överens, får du ett ValueError. Därför använder vi oftare understreck för att lagra resten av listan:
_, y = [1, 2] # y == 2, oavsett det första elementet
Tupler
Listor och tupler är mycket lika. Den enda skillnaden är att elementen i en tuple inte kan modifieras.
Skapa tupler
Man kan skapa tupler med parenteser eller utan några parenteser alls:
my_tuple = (1, 2)
other_tuple = 3, 4
my_list[1] = 3 # my_list är nu [1, 3]
try:
my_tuple[1] = 3
except TypeError:
print "Kan inte modifiera en tuple"
Tupler är mycket praktiska för att returnera flera värden från en funktion:
def sum_and_product(x, y):
return (x + y),(x * y)
sp = sum_and_product(2, 3) # Är lika med (5, 6)
s, p = sum_and_product(5, 10) # s = 15, p = 50
Tupler (och listor) stöder tilldelning av flera element samtidigt:
x, y = 1, 2 # x är nu 1, y är 2
x, y = y, x # Byter värdena på två variabler i Python; x är nu 2, y är 1
Ordlistor
Skapa ordlistor
En annan grundläggande datastruktur i Python är ordlistan (dictionary), som låter dig snabbt hämta ett värde med hjälp av en nyckel (key):
empty_dict = {} # Ett mycket Python-iskt sätt att definiera en tom ordlista
empty_dict2 = dict() # Ett mindre Python-iskt sätt att definiera en tom ordlista
grades = { "Joel" : 80, "Tim" : 95 } # Ordlistelagring
Söka i ordlistor
Du kan använda hakparenteser med nyckeln för att hitta det motsvarande värdet:
joels_grade = grades["Joel"] # Är lika med 80
Om nyckeln du söker inte finns i ordlistan, kommer ett KeyError att returneras:
try:
kates_grade = grades["Kate"]
except KeyError:
print "no grade for Kate!"
Man kan använda in för att kontrollera om en nyckel finns i ordlistan:
joel_has_grade = "Joel" in grades # True
kate_has_grade = "Kate" in grades # False
Ordlistor har en metod som kan returnera ett standardvärde när nyckeln som söks inte finns i ordlistan (istället för att kasta ett undantag):
joels_grade = grades.get("Joel", 0) # Är lika med 80
kates_grade = grades.get("Kate", 0) # Är lika med 0
no_ones_grade = grades.get("No One") # Returnerar standardvärdet None
Modifiera ordlistor
Man kan använda hakparenteser för att skapa eller modifiera nyckel-värde-par i en ordlista:
grades["Tim"] = 99 # Ersätter det gamla värdet
grades["Kate"] = 100 # Lägger till ett nyckel-värde-par
num_students = len(grades) # Är lika med 3
Vi kommer ofta att använda ordlistor på detta sätt för att representera datastrukturer:
tweet = {
"user" : "joelgrus",
"text" : "Data Science is Awesome",
"retweet_count" : 100,
"hashtags" : ["#data", "#science", "#datascience", "#awesome", "#yolo"]
}
Förutom att söka efter specifika nycklar, kan vi också manipulera alla nycklar på följande sätt:
tweet_keys = tweet.keys() # Får en lista med nycklar
tweet_values = tweet.values() # Får en lista med värden
tweet_items = tweet.items() # Får en lista med (nyckel, värde)-tupler
"user" in tweet_keys # Returnerar True, använder den mindre effektiva "in"-sökningen i listan
"user" in tweet # Mer Python-iskt, använder den effektiva "in"-sökningen i ordlistan
"joelgrus" in tweet_values # True
Nycklarna i en ordlista är unika, och listor kan inte användas som nycklar. Om du behöver en sammansatt nyckel, kan du använda en tuple, eller på något sätt konvertera nyckeln till en sträng.
Standardordlistor
Om du försöker räkna frekvensen av varje ord i ett dokument, är ett uppenbart tillvägagångssätt att skapa en ordlista där ordet är nyckeln och frekvensen det motsvarande värdet. Sedan itererar du genom dokumentet och ökar värdet med 1 för ord som redan finns, och lägger till ett nytt nyckel-värde-par för ord som inte har setts tidigare:
word_counts = {}
for word in document:
if word in word_counts:
word_counts[word] += 1
else:
word_counts[word] = 1
Du kan förstås också hantera en saknad nyckel genom att “försöka först och fånga sedan” (EAFP-principen) på följande sätt:
word_counts = {}
for word in document:
try:
word_counts[word] += 1
except KeyError:
word_counts[word] = 1
En tredje metod är att använda get, som är utmärkt för att hantera saknade nycklar:
word_counts = {}
for word in document:
previous_count = word_counts.get(word, 0)
word_counts[word] = previous_count + 1
En defaultdict fungerar som en vanlig ordlista, med den enda skillnaden att när du försöker slå upp en nyckel som inte finns, skapar defaultdict automatiskt ett nyckel-värde-par med den nyckel du angav. För att använda defaultdict måste du importera collections-biblioteket:
from collections import defaultdict
word_counts = defaultdict(int) # int() genererar 0
for word in document:
word_counts[word] += 1
defaultdict är också mycket användbar med listor, vanliga ordlistor och till och med egna funktioner:
dd_list = defaultdict(list) # list() genererar en tom lista
dd_list[2].append(1) # dd_list är nu {2: [1]}
dd_dict = defaultdict(dict) # dict() genererar en tom ordlista
dd_dict["Joel"]["City"] = "Seattle" # dd_dict innehåller nu { "Joel" : { "City" : "Seattle"}}
dd_pair = defaultdict(lambda: [0, 0]) # Skapar en ordlista där värdet för en nyckel är en lista
dd_pair[2][1] = 1 # dd_pair innehåller nu {2: [0,1]}
Denna metod är mycket användbar, eftersom vi i framtiden slipper kontrollera om en nyckel existerar när vi vill hämta vissa värden från ordlistan.
Räknare (Counter)
En Counter kan direkt omvandla en uppsättning värden till ett ordlisteliknande objekt, där nyckeln är ett element från uppsättningen och det motsvarande värdet är antalet gånger det elementet förekommer. Detta används ofta vid skapandet av histogram:
from collections import Counter
c = Counter([0, 1, 2, 0]) # c är (ungefär) { 0 : 2, 1 : 1, 2 : 1 }
På så sätt får vi ett mycket bekvämt sätt att räkna ordfrekvenser:
word_counts = Counter(document)
En Counter har också en mycket användbar metod, most_common, som direkt kan ge de mest frekventa orden och deras motsvarande frekvenser:
# Skriver ut de 10 mest frekventa orden och deras antal
for word, count in word_counts.most_common(10):
print word, count
Mängder (Sets)
En annan datastruktur i Python är mängden (set), som är en samling av unika element.
Man kan skapa en mängd och lägga till element på detta sätt:
s = set()
s.add(1) # s är { 1 }
s.add(2) # s är { 1, 2 }
s.add(2) # s är { 1, 2 }
x = len(s) # Är lika med 2
y = 2 in s # Är lika med True
z = 3 in s # Är lika med False
Två huvudsakliga anledningar att använda mängder:
För det första är in-operationen i mängder mycket effektiv. När antalet element i en datamängd är mycket stort, är det betydligt mer lämpligt att söka efter element i en mängd än i en lista:
stopwords_list = ["a","an","at"] + hundreds_of_other_words + ["yet", "you"]
"zip" in stopwords_list # Misslyckas, måste kontrollera varje element
stopwords_set = set(stopwords_list)
"zip" in stopwords_set # Sökningen lyckas, och är mycket snabb
För det andra är det mycket bekvämt att använda mängder för att få fram de unika elementen i en datamängd:
item_list = [1, 2, 3, 1, 2, 3]
num_items = len(item_list) # 6
item_set = set(item_list) # {1, 2, 3}
num_distinct_items = len(item_set) # 3
distinct_item_list = list(item_set) # [1, 2, 3]
I praktiken används dock mängder inte lika ofta som ordlistor och listor.
Villkorssatser
I de flesta programmeringsspråk kan du använda if för att uttrycka villkorliga grenar på detta sätt:
if 1 > 2:
message = "if only 1 were greater than two…"
elif 1 > 3:
message = "elif stands for 'else if'"
else:
message = "when all else fails use else (if you want to)"
Du kan också skriva villkorssatser på en enda rad på detta sätt, men det är sällan användbart:
parity = "even" if x % 2 == 0 else "odd"
Slingor
while-slingor
En while-slinga i Python:
x = 0
while x < 10:
print x, "is less than 10"
x += 1
for-slingor
Mer vanligt är att använda en for-in-slinga:
for x in range(10):
print x, "is less than 10"
För mer komplexa logiska uttryck kan continue- och break-satser användas:
for x in range(10):
if x == 3:
continue # Går direkt till nästa iteration
if x == 5:
break # Avslutar loopen helt
print x
Resultatet kommer att skriva ut 0, 1, 2 och 4.
Sanningsvärde (Truthiness)
Booleanska variabler i Python, Booleans, används på ungefär samma sätt som i andra språk, den enda skillnaden är att den första bokstaven måste vara stor:
one_is_less_than_two = 1 < 2 # Är True
true_equals_false = True == False # Är False
Python använder None för att indikera att ett värde saknas, liknande null i andra språk:
x = None
print x == None # Skriver ut True, inte lika elegant
print x is None # Skriver ut True, mer elegant
Python låter dig använda andra värden istället för booleans; följande är alla ekvivalenta med False:
- False
- None
- [] (en tom lista)
- {} (en tom ordlista)
- “”
- set()
- 0
- 0.0
På liknande sätt finns det många ekvivalenta värden för True, vilket gör det mycket bekvämt att kontrollera tomma listor, tomma strängar, tomma ordlistor och så vidare.
Naturligtvis, om du inte kan förutse resultatet, kan fel uppstå under användning:
s = some_function_that_returns_a_string()
if s:
first_char = s[0]
else:
first_char = ""
Ett enklare tillvägagångssätt, som har samma effekt som ovanstående:
first_char = s and s[0]
Om det första värdet är sant, returneras det andra värdet, annars returneras det första värdet.
På liknande sätt, om x kan vara ett nummer eller None, kan man på detta sätt säkerställa att x är ett nummer:
safe_x = x or 0
Python har också funktionen all, som returnerar True om varje element är True. Funktionen any returnerar True om minst ett element är True. Till exempel, för en lista där varje element är ‘sant’, kommer all-funktionen att returnera True, annars False:
all([True, 1, { 3 }]) # True
all([True, 1, {}]) # False, {} är ekvivalent med "False"
any([True, 1, {}]) # True
all([]) # True, det finns inget element som är ekvivalent med "False"
any([]) # False, det finns inget element som är ekvivalent med "True"
Vidare läsning:
Vanlig Python-syntax inom datavetenskap (avancerad)