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 a fájlokat egy fájl objektumon keresztül olvassa és írja:

Az open(filename[, mode]) függvény egy fájlobjektumot ad vissza, ahol a mode lehet 'r' (read, olvasás), 'w' (write, írás), 'r+' (mindkettő), 'a' (append, hozzáírás), 'a+' (append+read) vagy bináris fájlokra 'rb', 'wb', 'r+b', 'ab', 'a+b'.

In [1]:
f = open('E0.csv')  # megnyitja olvasásra, visszaad egy file-objektumot
print(f)
print(type(f))
<_io.TextIOWrapper name='E0.csv' mode='r' encoding='UTF-8'>
<class '_io.TextIOWrapper'>

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 read() metódus kiolvassa a fájl teljes tartalmát egy sztringbe. Nem printeljük ki az egészet, mert túl nagy, csak az első 100 karaktert.

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! Ehhez használhatjuk a readline() metódust.

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

Még egyszerűbb lehetőséget nyújt, hogy a fájl objektum soronként iterálható.

Figyeljük meg, hogy újsor karakter van minden sor végén.

In [4]:
f = open('E0.csv')
lst = []
for line in f:
    lst.append(line)
print(lst[:4])
['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\n', '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\n', 'E0,08/08/15,Chelsea,Swansea,2,2,D,2,1,H,M Oliver,11,18,3,10,15,16,4,8,1,3,1,0,1.36,5,11,1.4,4.75,9,1.33,4.8,8.3,1.4,4.5,10,1.39,4.92,10.39,1.4,4,10,1.4,5,9.5,45,1.43,1.37,5,4.66,11.26,9.57,43,1.88,1.8,2.07,1.99,27,-1.5,2.24,2.16,1.8,1.73\n', 'E0,08/08/15,Everton,Watford,2,2,D,0,1,A,M Jones,10,11,5,5,7,13,8,2,1,2,0,0,1.7,3.9,5.5,1.7,3.5,5,1.7,3.6,4.7,1.75,3.8,5,1.7,3.95,5.62,1.73,3.5,5,1.73,3.9,5.4,45,1.75,1.69,4,3.76,5.77,5.25,44,1.93,1.84,2.03,1.96,26,-1,2.28,2.18,1.76,1.71\n']

Az lst lista a fájl sorait tartalmazza. A sorokat .split(",")-tel cellákká is tudjuk tördelni, de erről mindjárt részletesebben.

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, 'w') írásra nyitja meg a fájlt, de ha írunk, vigyázzunk, hogy zárjuk is be!

In [5]:
f = open('Liverpool.csv', 'w')
f.write('YNWA')  # You'll Never Walk Alone
f.close()

Vagy egy ekvivalens megoldás a with paranccsal, amely után nem szükséges használni a close() metódust, mert a with hatóköréből kilépve azt bezárja. Olvashatóbb, biztonságosabb kódot eredményez:

In [6]:
with open('Liverpool.csv', 'w') as f:
    f.write('When you walk through a storm\n')

Addjunk hozzá a már lezárt fájlhoz még egy sort:

In [7]:
with open('Liverpool.csv', 'a') as f:
    f.write('Hold your head up high\n')

Megjegyzés: szöveges fájl olvasásához az open('E0.csv', 'r') parancsot használjuk, de alapértelmezésben olvasásra nyitunk meg egy fájlt, ezért az r elhagyható.

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

In [8]:
f = open('E0.csv')
lst = [f.readline()]   # fejléc

for line in f:
    if 'Liverpool' in line:
        lst.append(line)

with open('Liverpool.csv', 'w') as f:
    for l in lst:
        f.write(l)

Bináris fájlok írása, olvasása

Írjunk ki néhány számot bájtokban, majd olvassuk vissza!

In [9]:
with open("binfile.bin", "wb") as f:
    numbs = [1, 2, 4, 8, 16, 32, 31]
    arr = bytearray(numbs)
    f.write(arr)
In [10]:
with open("binfile.bin", "rb") as f:
    numbs = list(f.read())
    print(numbs)
[1, 2, 4, 8, 16, 32, 31]

csv fájl kezelése Pythonban

Az előző fájl .csv kiterjesztése a comma separated values-ra utal (ld. Wikipedia). Az ilyen fájlok egyszerű szövegfájlok, melyek egy sorában egy rekord szerepel, a rekordok adatait pedig vesszővel – választhatunk más karaktert, pl. tab, pontosvessző stb. – választjuk el. Ilyen formátumba menthetők a táblázatkezelők (pl. Excel, libreoffice,...) által kezelt táblázatok. Pythonnal könnyű kezelni az ilyen fájlokat (ld. a Python dokumentációban). Erre való a csv modul.

In [11]:
import csv

l = []
with open('E0.csv', 'r') 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 a delimiter opcióval, valamint a fájlban használt idézőjel karaktert a quotechar opcióval. 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 a csv modul felismer és nem nekünk kell foglalkozni vele.

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 [12]:
import csv

l = []
with open('E0.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        l.append(row)

print(l[0])
OrderedDict([('Div', 'E0'), ('Date', '08/08/15'), ('HomeTeam', 'Bournemouth'), ('AwayTeam', 'Aston Villa'), ('FTHG', '0'), ('FTAG', '1'), ('FTR', 'A'), ('HTHG', '0'), ('HTAG', '0'), ('HTR', 'D'), ('Referee', 'M Clattenburg'), ('HS', '11'), ('AS', '7'), ('HST', '2'), ('AST', '3'), ('HF', '13'), ('AF', '13'), ('HC', '6'), ('AC', '3'), ('HY', '3'), ('AY', '4'), ('HR', '0'), ('AR', '0'), ('B365H', '2'), ('B365D', '3.6'), ('B365A', '4'), ('BWH', '2'), ('BWD', '3.3'), ('BWA', '3.7'), ('IWH', '2.1'), ('IWD', '3.3'), ('IWA', '3.3'), ('LBH', '2.05'), ('LBD', '3.3'), ('LBA', '4'), ('PSH', '1.95'), ('PSD', '3.65'), ('PSA', '4.27'), ('WHH', '1.91'), ('WHD', '3.5'), ('WHA', '4'), ('VCH', '2'), ('VCD', '3.5'), ('VCA', '4.2'), ('Bb1X2', '45'), ('BbMxH', '2.1'), ('BbAvH', '1.96'), ('BbMxD', '3.65'), ('BbAvD', '3.48'), ('BbMxA', '4.33'), ('BbAvA', '3.98'), ('BbOU', '43'), ('BbMx>2.5', '2.11'), ('BbAv>2.5', '2.02'), ('BbMx<2.5', '1.88'), ('BbAv<2.5', '1.79'), ('BbAH', '26'), ('BbAHh', '-0.5'), ('BbMxAHH', '1.98'), ('BbAvAHH', '1.93'), ('BbMxAHA', '1.99'), ('BbAvAHA', '1.92')])

Nem azt kaptuk, amire számítottunk: az OrderedDict, azaz a rendezett szótár a dict osztály alosztálya, amely a létrehozás sorrendjét is jegyzi, ellentétben a szokásos szótárral. Egyébként a rendezett szótárakra ugyanazok a metódusok használhatók, mint a szótárakra. Szótárként való kiírásuk így történhet:

In [13]:
print(dict(l[0]))
{'Div': 'E0', 'Date': '08/08/15', 'HomeTeam': 'Bournemouth', 'AwayTeam': 'Aston Villa', 'FTHG': '0', 'FTAG': '1', 'FTR': 'A', 'HTHG': '0', 'HTAG': '0', 'HTR': 'D', 'Referee': 'M Clattenburg', 'HS': '11', 'AS': '7', 'HST': '2', 'AST': '3', 'HF': '13', 'AF': '13', 'HC': '6', 'AC': '3', 'HY': '3', 'AY': '4', 'HR': '0', 'AR': '0', 'B365H': '2', 'B365D': '3.6', 'B365A': '4', 'BWH': '2', 'BWD': '3.3', 'BWA': '3.7', 'IWH': '2.1', 'IWD': '3.3', 'IWA': '3.3', 'LBH': '2.05', 'LBD': '3.3', 'LBA': '4', 'PSH': '1.95', 'PSD': '3.65', 'PSA': '4.27', 'WHH': '1.91', 'WHD': '3.5', 'WHA': '4', 'VCH': '2', 'VCD': '3.5', 'VCA': '4.2', 'Bb1X2': '45', 'BbMxH': '2.1', 'BbAvH': '1.96', 'BbMxD': '3.65', 'BbAvD': '3.48', 'BbMxA': '4.33', 'BbAvA': '3.98', 'BbOU': '43', 'BbMx>2.5': '2.11', 'BbAv>2.5': '2.02', 'BbMx<2.5': '1.88', 'BbAv<2.5': '1.79', 'BbAH': '26', 'BbAHh': '-0.5', 'BbMxAHH': '1.98', 'BbAvAHH': '1.93', 'BbMxAHA': '1.99', 'BbAvAHA': '1.92'}

Egy kis kitérő: ha rendezett szótárat akarunk létrehozni, kezelni, be kell tölteni a collections modult (itt most nem kellett, a csv megtette). Az alábbi példa azt mutatja, hogy a rendezett szótárak összehasonlításánál számít a létrehozás sorrendje:

In [14]:
import collections

d1 = {}
d1['x'] = 'X'
d1['y'] = 'Y'

d2 = {}
d2['y'] = 'Y'
d2['x'] = 'X'

print(d1 == d2)

d1 = collections.OrderedDict()
d1['x'] = 'X'
d1['y'] = 'Y'

d2 = collections.OrderedDict()
d2['y'] = 'Y'
d2['x'] = 'X'

print(d1 == d2)
True
False

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 writeheader() metódus a fejlécet írja ki fájlba, a 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 [15]:
import csv

l = []
with open('E0.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for x in reader:
        if x['HomeTeam'] == 'Liverpool' or x['AwayTeam'] == 'Liverpool':
            l.append(x)

with open('Liverpool.csv', 'w') as output:
    fields = ['Date', 'HomeTeam', 'AwayTeam', 'FTHG', 'FTAG', 'FTR']
    writer = csv.DictWriter(output, fieldnames=fields, extrasaction='ignore')
    writer.writeheader()
    writer.writerows(l)

json formátum kezelése Pythonban

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
    }
}

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.

In [16]:
import json

with open('Liverpool.json') as data_file:    
    data = json.load(data_file)

print(data)
print(data['Liverpool']['Players'])
{'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}}
['Steven Gerrard', '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 [17]:
import json

with open('Liverpool.json') as data_file:    
    data = json.load(data_file)

with open('Liverpool_matches.json', 'w') 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ú sztringet Python objektummá konvertál
Részletek a Python dokumentációban.

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 (list of str).

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

import sys

print('Az argumentumok száma:  ', len(sys.argv))
print('Az argumentumok listája:', sys.argv)
In [18]:
! python3 parancssori.py arg1 arg2
Az argumentumok száma:   3
Az argumentumok listája: ['parancssori.py', 'arg1', 'arg2']

A ! jelzi, hogy azt a cellát parancssorba futtassa a notebook.

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!

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 [19]:
!python3 hatvany.py 4.2 3

!python3 hatvany.py 2 100
74.08800000000001
1267650600228229401496703205376

Linux alatt, ha a Python fájl első sora

#!/usr/bin/python3

és a fájl futtatható (ha nem, a chmod +x filename parancs azzá teszi), akkor a python3 parancs elhagyható:

In [20]:
!hatvany.py 2 1000
10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376
In [ ]: