7. gyakorlat¶
Warning
Figyelem! A héten kiadásra kerül a harmnadik beadandó, 8 pontért! Bővebb információ CooSpace-en.
A gyakorlat anyaga¶
Python virtuális környezet¶
A Python egyik legnagyobb erőssége, hogy rengeteg minden el van hozzá készítve, akár a standard függvénykönyvtár részeként, akár letölthető külső függvénykönyvtárként. Erről már biztosan beszéltünk korábban is. Ez eddig világos, de vajon hogy működni a valóságban? Írunk a programot, amihez egy csomó ilyen külső dolgot használunk. És utána?! Felírjuk egy lapra, hogy miket szedtünk le, és reméljük, hogy a felhasználó/megrendelő is telepítette az adott csomagokat, azonos, vagy kompatibilis verziókat, és nem lesz ebből gond? Nem teljesen így lesz, bár a megoldás alapjaiban hasonlítani fog, de jó volna erre valamilyen garancia is.
Erre megoldás lesz a virtualenv
használata, amely segítségével virtuális, szeparált környezeteket hozhatunk létre, és amiket azon belül telepítünk, azt később le is tudjuk majd kérni, így publikálás előtt pontos listát készíthetünk a telepítendő csomagokról.
Ebből a listából aztán bárki egy paranccsal telepíthet mindent.
Illetve a szeparált, virtuális környezetben telepített csomagok nem keverednek egyéb virtuális környezetekkel, sem pedig a globális csomagokkal (alapesetben), így különböző verziókat is használhatunk a különböző környezetekben.
Oké, ez jól hangzik. Mi kell ehhez? Ehhez és még sok egyéb csomag telepítéséhez a pip
csomagkezelő fog kelleni.
pip¶
A pip (package installer for Python) egy csomagkezelő rendszer, amely operációs rendszertől függetlenül használható mindegyiken, és sok hasznos dolgot tud. Alapvetően, a Python 3.4 verziótól kezdődően tartalmazza a pip csomagkezelőt. Ha esetleg nincs telepítve mégse, akkor le lehet tölteni a hivatalos oldalról.
A pip alapvetően a PyPI (Python Package Index, https://pypi.org/) oldalról telepít csomagokat.
Akkor telepítsünk vele fel egy csomagot, például a virtualenv
-et. Ehhez parancssorba be kell pötyögnünk a pip install virtualenv
parancsot (adminisztrátorként). Ez a legutóbbi stabil kiadást fogja telepíteni, de ha nem elégszünk meg ezzel, és mindenképpen a legfrissebb fejlesztői változatra van szükségünk, a pip segítségével bármit telepíthetünk egy bármilyen git verziókövetőből, ebben az esetben a pip install git+https://github.com/pypa/virtualenv.git@main
parancsot kell kiadnunk.
Természetesen felhasználói módban is telepíthetjük a virtualenv
csomagot, a parancs után a --user
kapcsolót kiadva.
Amennyiben nincsenek a környezeti változók megfelelően beállítva, a python -m
paranccsal lehet modulokat "megszólítani".
Hozzunk létre egy virtuális környezetet:
Ezzel elkészült a virtuális környezet a projektünkhöz, mostmár csak aktiválnunk kellene
-
Linux:
source gyak7/bin/activate
-
Windows:
gyak7\Scripts\activate
Az aktív környezetből kilépni a deactivate
paranccsal tudunk. Amíg nem lépünk ki, minden telepítés a virtuális környezetben történik majd.
Például telepítünk valamit:
pip install cowsay
A telepített cowsay
modult parancssorból tudjuk használni. Próbáljuk ki!
Amennyiben minden szükséges külső függvénykönyvtárat telepítettünk, valahogy rögzíteni kéne, hogy a felhasználó is pontosan azokat a verziókat telepíthesse:
Ehhez a pip freeze
parancsot használjuk első körben, ez a standard kimenetre kiírja az összes, környezetben telepített függvénykönyvtárat.
Ezt irányítsuk egy fájlba, jellemzően requirements.txt
szokott lenni.
A requirements.txt
fájlt általában a projekt gyökerébe tesszük, ezt felismeri a PyCharm is, és sok más IDE is.
Ha kapunk valahonnan egy projektet, ahol ilyet látunk, nem kell megijedni, nem kézzel telepítünk mindent, a pip ebben is segít:
pip install -r requirements.txt
Ez mindent úgy telepít fel, ahogy az a requirements.txt fájlban rögzítve van. A telepítést általában célszerű egy virtuális környezetbe végezni.
Ha pedig egy virtuális környezetre nincs szükségünk, csak kitöröljük a mappáját: rm -rf gyak7/
Letöltés az internetről¶
Ez pont megoldható a Python beépített függvénykönyvtárával is, az alábbi módon:
import json
import os
import urllib.request
import shutil
HP_API = "https://hp-api.onrender.com/api/characters"
if os.path.exists("tmp"):
shutil.rmtree("tmp")
os.mkdir("tmp")
with urllib.request.urlopen(HP_API) as f:
html = f.read().decode('utf-8')
data = json.loads(html)
for character in data:
print(f"{character['name']} - {character['image']}")
print(character)
# Első megoldás
# with urllib.request.urlopen(character['image']) as img, \
# open(os.path.join("tmp", os.path.basename(character['image'])), "wb") as out:
# out.write(img.read())
# Második megoldás
# urllib.request.urlretrieve(character['image'], os.path.join("tmp", os.path.basename(character['image'])))
requests¶
Azonban a külső requests
modul segítségével jóval egyszerűbb dolgunk van, és jóval több dolgot csinálhatunk jóval gyorsabban.
import requests
from requests.exceptions import HTTPError
HP_API = "https://hp-api.onrender.com/api/characters"
response = requests.get(HP_API)
print("Válasz", response.status_code)
if response:
# A válasz, szövegként
print(response.text)
# byte folyamként
print(response.content)
# A válasz JSON formátumra alakítva
print(response.json())
# A válasz header-ök
print(response.headers)
print(response.headers['Content-Type'])
print(response.headers['content-type'])
try:
response = requests.get('https://api.github.com/invalid')
# Kivétel dobása nem megfelelő válasz esetén
response.raise_for_status()
except HTTPError as http_err:
print(f'HTTP error occurred: {http_err}')
except Exception as err:
print(f'Other error occurred: {err}')
else:
print('Success!')
response = requests.get(
'https://api.github.com/search/repositories',
params={'q': 'requests+language:python'},
headers={'Accept': 'application/vnd.github.v3.text-match+json'},
)
print(response.json())
response = requests.post('https://httpbin.org/post', data=[('key', 'value')])
print(response.json())
response = requests.post('https://httpbin.org/post', data={'key': 'value'})
print(response.json())
response = requests.post('https://httpbin.org/post', json={'key':'value'})
print(response.json())
requests.put('https://httpbin.org/put', data={'key': 'value'})
requests.delete('https://httpbin.org/delete')
requests.head('https://httpbin.org/get')
requests.patch('https://httpbin.org/patch', data={'key': 'value'})
requests.options('https://httpbin.org/get')
response = requests.delete('https://httpbin.org/delete')
print(response.json())
# Ha ellenőrizni szeretnénk, hogy konkrétan mit küldtünk, arra a response objektumban található request szolgál.
# Ez tárolja az eredeti kérést
print("eredeti kérés DELETE", response.request)
response = requests.post('https://httpbin.org/post', data=[('key', 'value')])
print("eredeti kérés POST", response.request)
print("eredeti kérés HEADERS", response.request.headers)
print("eredeti kérés URL", response.request.url)
arrow¶
Az Arrow segítségével még magasabb szintű dátum és időkezelés valósítható meg. Az Arrow csomagról bővebben: https://arrow.readthedocs.io/en/latest/
import arrow
import datetime
# Jelenlegi idő
utc = arrow.utcnow()
print("utc", utc)
print("utc local", utc.to('local'))
print("utc local pacific", utc.to('local').to("US/Pacific"))
print("---")
utc = arrow.now("local")
print("arrow.now", utc)
print(utc.humanize(locale="hu_HU"))
print(arrow.now('America/New_York'))
print("---")
# Idő lekérése
ido = arrow.get("2021-03-24T17:55:27.777527+01:00")
print(ido)
print(ido.humanize(locale="hu_HU"))
ido = ido.shift(hours=-1)
print("-1 ora:", ido)
print(ido.humanize(locale="hu_HU"))
ido = ido.shift(days=-2, hours=-1, minutes=10)
print("-2 nap -1 ora -10 perc:", ido)
print(ido.humanize(locale="hu_HU"))
print(ido.humanize(locale="ko_kr"))
print(ido.humanize(granularity="minute"))
print(ido.humanize(granularity=["hour", "minute"]))
print(ido.humanize(only_distance=True, granularity=["day", "minute"]))
print(ido.format())
print(ido.timestamp())
print(arrow.get(1616425527.777527))
print(arrow.get('2013-05-05 12:30:45', 'YYYY-MM-DD HH:mm:ss'))
print(arrow.get("Tomorrow (2019-10-31) is Halloween!", "YYYY-MM-DD"))
print(arrow.get('2013-05-05 T \n 12:30:45\t123456', 'YYYY-MM-DD T HH:mm:ss S', normalize_whitespace=True))
print("type", type(ido))
print("type", type(ido.naive))
print("datetime tipus", ido.naive)
print("---")
# Datetime támogatás
print(arrow.get(datetime.datetime.utcnow()))
print(arrow.get(datetime.date(2013, 5, 5)))
print("---")
print(arrow.utcnow().span('hour'))
print(arrow.utcnow().floor('hour'))
print(arrow.utcnow().ceil('hour'))
start = datetime.datetime(2020, 4, 5, 10, 30)
end = datetime.datetime(2020, 4, 5, 16, 15)
for r in arrow.Arrow.range('hour', start, end):
print("range", r)
karacsony = arrow.utcnow().replace(month=12, day=25)
napok_karacsonyig = (karacsony - arrow.utcnow()).days
print(f"Karácsonyig {napok_karacsonyig} nap van hátra!")
print((karacsony - arrow.utcnow()))
print(type(karacsony - arrow.utcnow()))
Fire¶
A Fire segítségével még egyszerűbben valósítható meg a parancssori argumentumkezelés. Bővebben például itt: https://github.com/google/python-fire/blob/master/docs/guide.md
import datetime
import fire
def pentek13(elmult_evek):
"""
Az utóbbi hány évben számoljuk ki a péntek 13-ak számát.
:param elmult_evek: az elmúlt évek száma
:return: hányszor volt péntek 13
"""
most = datetime.date.today()
mikortol = most.year - elmult_evek
ev_szamlalo = dict()
for ev in range(mikortol, most.year):
for honap in range(1, 13):
datum_13 = datetime.date(ev, honap, 13)
if datum_13.weekday() == 4:
if datum_13.year not in ev_szamlalo:
ev_szamlalo[datum_13.year] = 0
ev_szamlalo[datum_13.year] += 1
return ev_szamlalo
if __name__ == '__main__':
fire.Fire(pentek13)
Több metódus modulokba szervezése
import fire
def add_simple(a, b, c):
return a + b + c
def add_args_default(a, *args):
return a + sum(args)
if __name__ == '__main__':
fire.Fire({'add3': add_simple, 'add': add_args_default})
Osztályok használatával OO parancssori argumentum beolvasó hierarchia jöhet létre.
import fire
class Cake(object):
def __init__(self, slices=10, flavour='chocolate'):
self._slices = slices
self._flavour = flavour
def price(self):
return self.slices * 390
def like(self, favorite_flavour):
f1 = set(self._flavour)
f2 = set(favorite_flavour)
res = f1 & f2
return len(res) >= ((len(f1) + len(f2)) // 2)
def __str__(self):
return '[Cake, slices=%d, flavour=%s]' % (self._slices, self._flavour)
if __name__ == '__main__':
fire.Fire(Cake)
Órai feladatok¶
Megjegyzés
Az órai feladatok alatt lévő feladat(sor)ok azok a feladat(sor)ok, amelyek megoldása beleszámít a zh javításért megoldandó feladatok közé. Ezen feladat(sor)okból 6 darab megoldása szükséges ahhoz, hogy egy zh javíthatóvá váljon. Ha külön nem jelezzük, a teljes feladatsor megoldása esetén jár a zh javításért szerezhető pont.
Info
A teljes feladatsor megoldása szükséges a zh javításért járó pont megszerzéséért.
Harry Potter API¶
A Harry Potter API-ról letölthető adatok alapján oldjuk meg az alábbi feladatokat:
- Készíts adatosztályt a karakterekhez (Karakter néven)
- Töltsd be a JSON fájlból az adatosztályba a karakterek adatait. Készíts egy `statisztika.txt` nevű fájlt, amiben benne van, hogy melyik házból hány ember van az adathalmazban, milyen hajszínű emberből van a legtöbb, milyen szemszínű emberből van a legtöbb, illetve kik azok az adatbázisban, akik nem emberek.
- Alakítsd át a struktúrát, hogy házakra bontva tartalmazza az adatokat. Az `image` tulajdonságot töröld ki, és mentsd el az eredményt a `hogwarts-by-house.json` fájlba.
Korábbi feladatok átírása¶
A korábbi dátumos/argumentumkezelős feladatok átírása a most tanult modulok segítéségével
Új modulok felfedezése¶
A feladat egy új modul megkeresése és kipróbálása, ezt a legkönnyebben a https://pypi.org/ oldalról teheted meg. Tetszőlegesen lehet olyan modul, ami érdekel, vagy a későbbiekben tudod használni. A CooSpace órai feladatnál a kipróbált kódokat is fel kell tenni.