Fájlműveletek és parancssor

Megmutatjuk, hogy önálló Python scriptek hogyan kezelik a bemeneti paramétereiket és hogyan tudunk fájlokat írni és olvasni

Fájl olvasása

A Python - ahogyan a legtöbb programnyelv - a fájlokat egy fájl objektumon keresztül olvassa és írja. Nézzük meg, hogy hogyan működik!

Az open(filename[, mode]) függvény egy fájlobjektumot ad vissza, ahol a mode lehet 'r' (olvasás), 'w' (írás), 'r+' (mindkettő), vagy bináris fájlokra 'rb', 'wb', 'r+b'. Python 2.7 esetén a Windowson az utóbbiakat érdemes használni, mert bináris fájl esetén a beolvasás-visszaírás közben a sorvége jel megváltozhat. Linuxon mindegy.

In [1]:
f = open('E0.csv') # megnyitja olvasasra, visszaad egy file-objektumot
print f
<open file 'E0.csv', mode 'r' at 0x7efe01bc7810>

Egyelőre ez csak egy fájl objektum. A mintafájlunk az angol Premier League 2015/16-os idényének a statisztikáit tartalmazza. Most olvassunk is valamit! Az f.read() kiolvassa a fájlt teljes tartalmát egy sztringbe. Nem printeljük ki az egészet, mert túl sok.

In [2]:
f = open('E0.csv')
content = f.read()
print content[:100]
Div,Date,HomeTeam,AwayTeam,FTHG,FTAG,FTR,HTHG,HTAG,HTR,Referee,HS,AS,HST,AST,HF,AF,HC,AC,HY,AY,HR,AR

Olvassuk ki most csak az első sort!

In [3]:
f = open('E0.csv')
first_line = f.readline()
print first_line
second_line = f.readline()
print second_line
Div,Date,HomeTeam,AwayTeam,FTHG,FTAG,FTR,HTHG,HTAG,HTR,Referee,HS,AS,HST,AST,HF,AF,HC,AC,HY,AY,HR,AR,B365H,B365D,B365A,BWH,BWD,BWA,IWH,IWD,IWA,LBH,LBD,LBA,PSH,PSD,PSA,WHH,WHD,WHA,VCH,VCD,VCA,Bb1X2,BbMxH,BbAvH,BbMxD,BbAvD,BbMxA,BbAvA,BbOU,BbMx>2.5,BbAv>2.5,BbMx<2.5,BbAv<2.5,BbAH,BbAHh,BbMxAHH,BbAvAHH,BbMxAHA,BbAvAHA

E0,08/08/15,Bournemouth,Aston Villa,0,1,A,0,0,D,M Clattenburg,11,7,2,3,13,13,6,3,3,4,0,0,2,3.6,4,2,3.3,3.7,2.1,3.3,3.3,2.05,3.3,4,1.95,3.65,4.27,1.91,3.5,4,2,3.5,4.2,45,2.1,1.96,3.65,3.48,4.33,3.98,43,2.11,2.02,1.88,1.79,26,-0.5,1.98,1.93,1.99,1.92

Ez még mindig nagyon fárasztó. De szerencsére a fájl objektum iterálható.

In [4]:
f = open('E0.csv')
L = []
for line in f:
    L.append(line)
print L[20]    
E0,17/08/15,Liverpool,Bournemouth,1,0,H,1,0,H,C Pawson,18,13,2,2,11,18,6,8,1,4,0,0,1.45,4.75,8,1.48,4.75,7.25,1.4,4.4,7.3,1.45,4.5,7.5,1.44,4.74,8.6,1.5,4.2,6.5,1.45,4.75,8.5,43,1.5,1.44,5,4.46,8.6,7.71,42,1.83,1.75,2.2,2.05,25,-1,1.76,1.72,2.26,2.17

Az L lista a fájl sorait tartalmazza. GGWP.

Fájl írása

Képzeljük el, hogy Liverpool szurkolók vagyunk és nekünk csak a kedvenc csapatunk eredményei számítanak. Írjuk ki egy fájlba őket! Az olvasáshoz hasonlóan szükségünk lesz egy fájl objektumra, de ahhoz, hogy írni tudjuk, másként kell megnyitni. Először egy egyszerű példa. Az open(filename, 'wb') írásra nyitja meg a fájlt, de ha írunk, vigyázzunk, hogy zárjuk is be!

In [5]:
f = open('Liverpool.csv', 'wb')
f.write('YNWA')
f.close()

Megjegyzés: olvasáshoz az open('E0.csv', 'rb') parancsot illik használni, de alapértelmezésben olvasásra nyitunk meg egy fájlt.

Térjünk vissza az eredeti példához! Soronként beolvassuk a fájlt, és azt a sort, ahol a Liverpool szerepel, elmentjük. Ne felejtsük el, hogy a fejlécre továbbra is szükségünk van!

In [6]:
f = open('E0.csv')
L = [f.readline()]
for line in f:
    if 'Liverpool' in line:
        L.append(line)
with open('Liverpool.csv', 'wb') as f:
    for l in L:
        f.write(l)
f.close()

Python, csv és json

Az előző fájl .csv kiterjesztése a comma separated values-ra utal. Az ilyen fájlokban egy sorban egy rekord szerepel, a rekordok adatait pedig vesszővel -- választhatunk más karaktert pl. tab, pontosvessző stb. -- választjuk el. Pythonnal könnyű kezelni az ilyen fájlokat. Erre való a csv modul. A with open(filename, 'rb') as f pontosan ugyanahhoz az eredményhez vezet mintha f = open(filename, 'rb')-t írtunk volna. Ha lehet, használjuk az előbbit, hisz így pontosan látjuk, meddig olvassuk a fájlunkat!

In [7]:
import csv
L=[]
with open('E0.csv', 'rb') as csvfile:
    reader = csv.reader(csvfile, delimiter=',', quotechar='"')
    for row in reader:
        L.append(row)
print L[0]
print L[19]
['Div', 'Date', 'HomeTeam', 'AwayTeam', 'FTHG', 'FTAG', 'FTR', 'HTHG', 'HTAG', 'HTR', 'Referee', 'HS', 'AS', 'HST', 'AST', 'HF', 'AF', 'HC', 'AC', 'HY', 'AY', 'HR', 'AR', 'B365H', 'B365D', 'B365A', 'BWH', 'BWD', 'BWA', 'IWH', 'IWD', 'IWA', 'LBH', 'LBD', 'LBA', 'PSH', 'PSD', 'PSA', 'WHH', 'WHD', 'WHA', 'VCH', 'VCD', 'VCA', 'Bb1X2', 'BbMxH', 'BbAvH', 'BbMxD', 'BbAvD', 'BbMxA', 'BbAvA', 'BbOU', 'BbMx>2.5', 'BbAv>2.5', 'BbMx<2.5', 'BbAv<2.5', 'BbAH', 'BbAHh', 'BbMxAHH', 'BbAvAHH', 'BbMxAHA', 'BbAvAHA']
['E0', '16/08/15', 'Man City', 'Chelsea', '3', '0', 'H', '1', '0', 'H', 'M Atkinson', '18', '10', '8', '3', '19', '13', '5', '1', '4', '2', '0', '0', '2.1', '3.5', '3.75', '2.1', '3.4', '3.7', '2.1', '3.3', '3.3', '2.1', '3.4', '3.75', '2.08', '3.56', '3.87', '2.15', '3.2', '3.6', '2.1', '3.5', '3.9', '43', '2.17', '2.09', '3.56', '3.4', '3.9', '3.66', '42', '2.05', '1.98', '1.88', '1.82', '28', '-0.5', '2.12', '2.06', '1.87', '1.81']

A különbség szembetűnő. A csv.reader egyből listát csinál nekünk a sorokból. Ráadásul megadhatjuk neki az elválasztó karaktert, valamint a quotechar-t is, ami az idézőjel karaktert jelöli ki. Ez azért fontos néhány esetben, mert sokszor számok és sztringek vegyesen vannak egy csv fájlban, ilyenkor a sztringeket idézőjelbe szokták tenni, amit érdemes felismernünk.

csv olvasása szótárba

Ha jól megnézzük az adatokat, nekünk nem feltétlenül listákra lenne szükségünk, hanem szótárakra. A fájl ugyanis minden meccsre ugyanazokat az adatokat tárolja, mi pedig szívesebben hivatkozunk indexek helyett nevekkel dolgokra. Erre is van lehetőség.

In [8]:
import csv
L=[]
with open('E0.csv', 'rb') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        L.append(row)
print L[0]
print L[1]
{'BbAHh': '-0.5', 'HY': '3', 'BbMx>2.5': '2.11', 'HTHG': '0', 'HR': '0', 'HS': '11', 'VCA': '4.2', 'BbMxD': '3.65', 'AwayTeam': 'Aston Villa', 'BbAvD': '3.48', 'PSD': '3.65', 'BbAvA': '3.98', 'HC': '6', 'HF': '13', 'Bb1X2': '45', 'BbAvH': '1.96', 'WHD': '3.5', 'Referee': 'M Clattenburg', 'WHH': '1.91', 'WHA': '4', 'IWA': '3.3', 'AST': '3', 'BbMxH': '2.1', 'HTAG': '0', 'BbAv>2.5': '2.02', 'IWH': '2.1', 'LBA': '4', 'BWA': '3.7', 'BWD': '3.3', 'LBD': '3.3', 'HST': '2', 'PSA': '4.27', 'Date': '08/08/15', 'LBH': '2.05', 'BbMxAHA': '1.99', 'BbAvAHA': '1.92', 'BbAvAHH': '1.93', 'IWD': '3.3', 'AC': '3', 'FTR': 'A', 'VCD': '3.5', 'AF': '13', 'VCH': '2', 'FTHG': '0', 'BWH': '2', 'AS': '7', 'AR': '0', 'AY': '4', 'Div': 'E0', 'PSH': '1.95', 'B365H': '2', 'HomeTeam': 'Bournemouth', 'B365D': '3.6', 'B365A': '4', 'BbMx<2.5': '1.88', 'BbMxAHH': '1.98', 'BbAv<2.5': '1.79', 'HTR': 'D', 'BbAH': '26', 'BbOU': '43', 'FTAG': '1', 'BbMxA': '4.33'}
{'BbAHh': '-1.5', 'HY': '1', 'BbMx>2.5': '1.88', 'HTHG': '2', 'HR': '1', 'HS': '11', 'VCA': '9.5', 'BbMxD': '5', 'AwayTeam': 'Swansea', 'BbAvD': '4.66', 'PSD': '4.92', 'BbAvA': '9.57', 'HC': '4', 'HF': '15', 'Bb1X2': '45', 'BbAvH': '1.37', 'WHD': '4', 'Referee': 'M Oliver', 'WHH': '1.4', 'WHA': '10', 'IWA': '8.3', 'AST': '10', 'BbMxH': '1.43', 'HTAG': '1', 'BbAv>2.5': '1.8', 'IWH': '1.33', 'LBA': '10', 'BWA': '9', 'BWD': '4.75', 'LBD': '4.5', 'HST': '3', 'PSA': '10.39', 'Date': '08/08/15', 'LBH': '1.4', 'BbMxAHA': '1.8', 'BbAvAHA': '1.73', 'BbAvAHH': '2.16', 'IWD': '4.8', 'AC': '8', 'FTR': 'D', 'VCD': '5', 'AF': '16', 'VCH': '1.4', 'FTHG': '2', 'BWH': '1.4', 'AS': '18', 'AR': '0', 'AY': '3', 'Div': 'E0', 'PSH': '1.39', 'B365H': '1.36', 'HomeTeam': 'Chelsea', 'B365D': '5', 'B365A': '11', 'BbMx<2.5': '2.07', 'BbMxAHH': '2.24', 'BbAv<2.5': '1.99', 'HTR': 'H', 'BbAH': '27', 'BbOU': '43', 'FTAG': '2', 'BbMxA': '11.26'}

Tároljuk most el a Liverpool mérkőzéseinek legfontosabb adatait. Ezek a 'Date', 'HomeTeam', 'AwayTeam', 'FTHG'(Full Time Home Goals), 'FTAG' (Full Time Away Goals), 'FTR' (Full Time Result)! A tároláshoz a csv.DictWriter lesz segítségünkre. a writer.writeheader() a fejlécet írja ki fájlba, a writer.writerows() pedig egy mozdulattal kiírja az összes adatunkat. A fieldnames paraméterben adhatjuk meg, hogy milyen attribútumokra van szükségünk, az extrasaction='ignore' pedig pusztán azért kell, hogy a többi adatot ne írja bele.

In [9]:
import csv
L=[]
with open('E0.csv', 'rb') as csvfile:
    reader = csv.DictReader(csvfile)
    for x in reader:
        if x['HomeTeam'] == 'Liverpool' or x['AwayTeam'] == 'Liverpool':
            L.append(x)
csvfile.close()
with open('Liverpool.csv', 'wb') as output:
    fields = ['Date', 'HomeTeam', 'AwayTeam', 'FTHG', 'FTAG', 'FTR']
    writer = csv.DictWriter(output, fieldnames=fields, extrasaction='ignore')
    writer.writeheader()
    writer.writerows(L)
output.close()

A json formátum

JavaScript Object Notation. tárolhatunk benne számokat, sztringeket, listát, szótárat. Sőt, tetszőlegesen egymásba ágyazhatunk szótárakat, listákat, listák listáját, szótárak listáját, szótárak szótárát, listák szótárát. Listák elemeit vesszővel választjuk el, a szótárakat pedig mint a Pythonban key:value módon adjuk meg. { "Liverpool" : { "Players": [ "Steven Gerrard", "Bill Shankly" ], "Results" : [ { "HomeTeam":"Liverpool", "AwayTeam":"Tottenham", "HTG":1, "ATG":1 }, { "HomeTeam":"West Ham", "AwayTeam":"Liverpool", "HTG":2, "ATG":0 } ], "Points":1, "Goals Scored":1, "Goals Condceded":3 } }

Természetesen a Pythonnal a json-t is kényelmesen lehet kezelni. Egyszerűen beolvassuk a fájlt és kiírjuk a képernyőre. Látjuk, hogy Python szótár keletkezett belőle, tehát hivatkozhatunk a kulcsaira. Az u'Steven Gerrard' azt jelenti, hogy unicode sztringet olvastunk be.

In [10]:
import json
with open('Liverpool.json') as data_file:    
    data = json.load(data_file)

print data
print data['Liverpool']['Players']
{u'Liverpool': {u'Players': [u'Steven Gerrard', u'Bill Shankly'], u'Goals Scored': 1, u'Points': 1, u'Goals Condceded': 3, u'Results': [{u'HTG': 1, u'AwayTeam': u'Tottenham', u'HomeTeam': u'Liverpool', u'ATG': 1}, {u'HTG': 2, u'AwayTeam': u'Liverpool', u'HomeTeam': u'West Ham', u'ATG': 0}]}}
[u'Steven Gerrard', u'Bill Shankly']

Írjuk ki egy fájlba a meccsek eredményeit! A külalakra is figyelünk, erre való a sort_keys, az indent és a separators. A json.dumps(obj) tetszőleges Python objectet json sztringgé alakít, így ezt egyszerűen kiírjuk a fájlba!

In [11]:
import json
with open('Liverpool.json') as data_file:    
    data = json.load(data_file)
data_file.close()
with open('Liverpool_matches.json', 'wb') as f:
    f.write(json.dumps(data['Liverpool']['Results'], 
            sort_keys=True, indent=4, separators=(',', ': ')))

json.dump(JSON_formaju_szoveg, file): kiírás fájlba
json.dumps(objektum): objektum JSON formátumúvá konvertálása
json.load(file): a file-ban lévő JSON formátumú dokumentumot Python objektummá konvertál
json.loads(JSON_formaju_szoveg): JSON formátumú dokumentumot Python objektummá konvertál
Részletek a https://docs.python.org/2/library/json.html.

Parancssori (command line) argumentumok

Szeretnénk a Python programunknak kívülről átadni paraméterértékeket!

A sys csomag

Elmentünk .py végződéssel egy fájlt. Ezt vagy a rendszer felismeri mint Python scriptet vagy megfelelő paranccsal indítjuk. Első programunkkal kiírjuk a bemeneti paraméterek számát és listáját. Az első elem mindig a script neve. A paramétereket a sys.argv listában tárolja a Python. Ehhez kell az import sys csomag betöltése. Minden argumentum karakterlánc.

A következő kódot írjuk ki egy fájlba, és futassuk parancssorból a megfelelő paranccsal.

In [ ]:
import sys

print 'Az argumentumok szama: ', len(sys.argv)
print 'Az argumentumok listaja:', str(sys.argv)
In [ ]:
%run parancssori.py arg1 arg2

Jelen esetben a %run pusztán azért kell, hogy szimulálni tudjuk, milyen is a Unix command line:

In [ ]:
$python parancssori.py arg1 arg2

Az ilyen paramétereket hívjuk pozicionális paramétereknek, hiszen a sys.argv listában elfoglalt helyük alapján azonosítjük őket.

Feladat: emeljünk egy számot adott hatványra! Ha az alap és a kitevő is egész, akkor számoljuk a hatványt egészek hatványaként, különben lebegőpontosként. A két számot parancssori argumentumként adjuk át a programnak.

A következő kódot másoljuk egy fájlba és parancssorból futtassuk!

In [ ]:
import sys

def is_intstring(s):
    try:
        int(s)
        return True
    except ValueError:
        return False

a = []

for i in range(1,3):
    if is_intstring(sys.argv[i]):
        a.append(int(sys.argv[i]))
    else:
        a.append(float(sys.argv[i]))

print a[0] ** a[1]

A futtatások eredményei:

In [ ]:
%run hatvany.py 4.2 3
74.088

$python hatvany.py 2 100
1267650600228229401496703205376

argparse

A sys.argv tömböt is feldolgozhatjuk, de ezt is megtette helyettünk már valaki. Az argparse modul segít nekünk egészen komplex command line paraméterek beolvasásában is. Nézzünk példát egy flag-re. Ezek kvázi kétállípotú kapcsolók, amelyek befolyásolják a kód működését, nézzünk egy példát a verbosity-re. Ha a felhasználó szeretné, követheti a lépéseket.

In [ ]:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
    print "verbosity turned on"
In [ ]:
%run parser.py --verbosity 1
In [ ]:
%run parser.py

Ez minden egész értéket elfogad a --verbosity után. Még ennél is elegánsabb az action='store_true'. Most ráadásul elég csak -v-vel meghívni

In [ ]:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true")
args = parser.parse_args()
if args.verbose:
   print "verbosity turned on"
In [ ]:
%run parser2.py
%run parser2.py --verbose
%run parser2.py -v

Sőt, a help menü automatikusan elkészül. Ehhez kellett a help="increase output verbosity".

In [ ]:
%run parser2.py --help

Vissza a csv fájlunkhoz. Szeretnénk megtudni, hogy egy adott csapatnak hány olyan mérkőzése volt, amelyen legalább x gólt rúgott. A csapat nevét és a gólok számát is olvassuk be a parancssorból! Az action='store' az args szótár megdelelő kulcsához társítja az értéket, a type pedig meghatározza a típusát. Megjegyezzük, hogy ezek az argumentumok opcionálisak, így nem árt, ha alapértelmezett értéke is van (default).

In [ ]:
import argparse
import csv

parser = argparse.ArgumentParser()
parser.add_argument("-t", "--team", help="The team we are looking for", action="store", type=str, default='Liverpool')
parser.add_argument("-g", "--goals", help="Number of minimum goals scored", action="store", type=float, default=0)
args = parser.parse_args()

m = 0
team = args.team
goals = args.goals
with open('E0.csv', 'rb') as csvfile:
    reader = csv.DictReader(csvfile)
    for x in reader:
        if x['HomeTeam'] == team and float(x['FTHG']) >= goals:
            m += 1
        elif x['AwayTeam'] == team and float(x['FTAG']) >= goals:
            m += 1
print m
In [ ]:
%run goals.py -h
In [ ]:
%run goals.py -g 1
In [ ]: