Kihagyás

6. gyakorlat

A gyakorlat anyaga

Ezen a gyakorlaton a cloc nevű eszközt (elérhető ezen a linken) fogjuk hívogatni Pythonból. Azért ezt, mert ad valami outputot, amivel aztán majd tudunk valamit kezdeni. A cloc (count lines of code) egy ingyenes program, ami annyit tud, hogy egy fájlról, vagy mappáról megmondja, hogy milyen nyelvű forrásból mennyi kódsor, komment sor, üres sor található. Az, hogy mit hívogatunk az órán, az tetszőleges, bármit lehet lokálisan használni, a feladat viszont ehhez az eszközhöz kapcsolódik. Aki nem szeretné betelepíteni, és a PATH˛-ból használni, az teljes elérési úttal is meghívhatja.

Debug alapok

  • Breakpoint elhelyezése a kódban
  • Feltételes breakpoint elhelyezése a kódban
  • Kivétel breakpoint elhelyezése a kódban
  • Futás közbeni kiértékelés

Subprocess

A subprocess modul segítségével (is) meghívhatunk másik alkalmazásokat Pythonból. Python 3.5 óta a run metódus használata a javasolt a korábbi változatok (pl.: call) helyett.

A modul leírása: https://docs.python.org/3/library/subprocess.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import subprocess


if __name__ == '__main__':
    # subprocess.run(["help"])
    # subprocess.run(["echo", "Húsvéti nyúl"])
    subprocess.run(["echo", "Húsvéti nyúl"], shell=True)

    subprocess.run(["cloc", "utils"])

    cp = subprocess.run(["cloc", "utils"])
    print(cp)
    if cp.returncode == 0:
        print("A program futása sikeres volt!")

    print("--- capture_output")
    cp = subprocess.run(["cloc", "utils"], capture_output=True)
    print(cp)

    print("--- capture_output és text")
    cp = subprocess.run(["cloc", "utils"], capture_output=True, text=True)
    print(cp)
    print("stdout", len(cp.stdout))
    print("stderr", len(cp.stderr))

    # Ha a returncode nem 0, itt egy CalledProcessError dobódik
    cp.check_returncode()

    # Ha az output nem érdekel
    cp = subprocess.run(["cloc", "utils"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=1)
    print(cp)
    try:
        cp = subprocess.run(["python", "-c", "import time; time.sleep(2)"], timeout=1)
    except subprocess.TimeoutExpired as cpe:
        print("A program nem lett kész időben!")

    cp = subprocess.run(["python", "-c", "print('Valamiéáőúű')"], stdout=subprocess.PIPE)
    print(cp.stdout)
    print(type(cp.stdout))
    print(cp.stdout.decode('utf-8'))

    print("--- input átadása")
    subprocess.run(["python", "-c", "print(input())"], input=b"Cica")
    subprocess.run(["python", "-c", "print(input())"], input="Valamiéáőúű".encode('utf-8'))

    print("--- stdin fájlból")
    with open("parancsok.txt", "r", encoding="utf8") as fp:
        subprocess.run(["python", "-c", "for i in range(2): print(input())"], stdin=fp)

    print("--- stdout fájlba")
    with open("zen_gondolatok.txt", "w", encoding="utf8") as fp:
        subprocess.run(["python", "-c", "import this"], stdout=fp)

    # cwd=None
    # check=False - ugyanaz, mint a visszateresi objektum check_returncode() metódusa
    # encoding=None, errors=None, text=None
    # env=None
    # universal_newlines=None

argparse

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import argparse

parser = argparse.ArgumentParser()
parser.version = '1.0'
parser.add_argument('-a', action='store', metavar='ALMAK_SZAMA')
parser.add_argument('-b', action='store_const', const=42)
parser.add_argument('-c', action='store_true')
parser.add_argument('-d', '--direkt-hamis', action='store_false')
parser.add_argument('-e', action='append')
parser.add_argument('-f', action='append_const', const=42)
parser.add_argument('-g', action='count', help="Mennyire nehéz az argparse használata")
parser.add_argument('-i', action='help')
parser.add_argument('-j', action='store', default="Jaj ne")
parser.add_argument('-v', action='version')
parser.add_argument('--input', action='store', type=int, nargs=3)
parser.add_argument('-x', action='count', required=True)
parser.add_argument('-p', choices=("alma", "körte", "barack"))

my_group = parser.add_mutually_exclusive_group()

my_group.add_argument('--verbose', action='store_true')
my_group.add_argument('--silent', action='store_true')

args = parser.parse_args()
print(args)
print(vars(args))

Collections

namedtuple

Egyszerű, nevesített tuple, hasonló a C-beli struct típushoz.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import collections

# Named tuples
Varazslo = collections.namedtuple("Varazslo", ['nev', 'haj_szin', 'haj_meret', 'szem_szin'])

harry = Varazslo("Harry Potter", "barna", "rövid", "zöld")
ron = Varazslo("Ron Weasly", "vörös", "rövid", "vörös")
hermione = Varazslo("Hermione Granger", "barna", "hosszú", "barna")

print(harry)
print("név", harry.nev)
print("név", harry[0])
print("harry dict", harry._asdict())
print("---")

print(ron)
ron = ron._replace(szem_szin="barna")
print(ron)
print("---")

malfoy_dict = {'nev': 'Draco Malfoy', 'haj_szin': 'szőke', 'haj_meret': 'rövid', 'szem_szin': 'kék'}
malfoy = Varazslo(**malfoy_dict)
print(malfoy)

piton_list = ["Perselus Piton", "fekete", "félhosszú", "barna"]
piton = Varazslo._make(piton_list)
print(piton)

# Ki kicsoda játék készítése

Counter

Egy ügyes dict leszármazott, ami képes elemek leszámolására.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import collections

jegyeim = [1, 3, 5, 5, 3, 2, 4, 5, 2, 2, 3, 4, 5, 5, 1, 5, 5, 3, 4, 3, 2, 1]

# Counter (ősosztálya a dict)
counter = collections.Counter(jegyeim)
print(counter.items())
print(counter.most_common())

counter.subtract([1, 1, 1])
print(counter)

deque

Két irányból láncolt sor. Gyakorlatilag olyan lista, amibe lehet mindkét irányba beszúrni és törölni.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import collections

# Deque
deque = collections.deque(["a", "b", "c"])
deque.append("d")
deque.appendleft("z")
print(deque)

deque.pop()
deque.popleft()
print(deque)

Adatosztályok

  • Python 3.7 óta
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import dataclasses
import random

@dataclasses.dataclass()
class Varazslo:
    nev: str
    haj_szin: str
    haj_meret: str
    szem_szin: str

    def __str__(self):
        return f"Varazslo(nev={self.nev}, haj_szin={self.haj_szin}, haj_meret={self.haj_meret}, szem_szin={self.szem_szin}"

    def varazsol(self):
        if "Ron" in self.nev:
            return "Leviossz" + ("á" * random.randint(6, 15))
        return "Leviósza"


if __name__ == '__main__':
    harry = Varazslo("Harry Potter", "barna", "rövid", "zöld")
    ron = Varazslo("Ron Weasly", "vörös", "rövid", "vörös")
    hermione = Varazslo("Hermione Granger", "barna", "hosszú", "barna")

    print(ron.varazsol())
    print(hermione.varazsol())

itertools

Az itertools modul sok olyan hasznos függvényt tartalmaz, amik iterátorokkal térnek vissza. Hasznos, memóriabarát módja ez az elembejárásnak (és generálásnak).

count

1
2
3
4
5
6
7
8
import itertools

for i in itertools.count(5, 5):
    if i == 35:
        break
    else:
        print(i, end=" ")
print("\n---")

repeat

1
2
3
4
5
import itertools

print("Jó sokszor megismételjük a legszebb számot")
print(list(itertools.repeat(2, 4)))
print("\n---")

zip

1
2
3
4
5
6
7
8
import itertools

szinek = ["sárga", "kék", "zöld", "barna", "piros"]
targyak = ["autó", "motor", "ház", "gomba"]

print(list(zip(szinek, targyak)))
print(list(itertools.zip_longest(szinek, targyak)))
print(list(itertools.zip_longest(szinek, targyak, fillvalue="valami")))

combinations/permutations

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import itertools

hallgatok = ["Harry Potter", "Ron Weasly", "Hermione Granger", "Ginny Weasley", ]
# "Tom Denem", "Sirius Black", "Dudley Dursley", "Albus Dumbledore",
# "Luna Lovegood", "Draco Malfoy", "Rubeus Hagrid", "Parvati Patil",
# "Neville Longbottom", "Romilda Vane"]

print("-- kombinációk")
print(list(itertools.combinations(hallgatok, 3)))
print(list(itertools.combinations_with_replacement(hallgatok, 3)))

print("-- permutációk")
print(list(itertools.permutations(hallgatok)))

cycle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import itertools

hallgatok = ["Harry Potter", "Ron Weasly", "Hermione Granger", "Ginny Weasley", ]

print("-- ciklizálás")
count = 0
for i in itertools.cycle(hallgatok):
    print(i)
    count += 1
    if count == 100:
        brea

product

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
lapok = ['A', 'K', 'Q', 'J', '10', '9', '8', '7', '6', '5', '4', '3', '2']
szinek = ['H', 'D', 'C', 'S']  # hearts, diamonds, clubs, and spades


def pakli():
    for lap in lapok:
        for szin in szinek:
            yield lap, szin


pakli_0 = pakli()
pakli_1 = ((lap, szin) for szin in szinek for lap in lapok)
pakli_2 = itertools.product(lapok, szinek)

print(list(pakli_0))
print(list(pakli_1))
print(list(pakli_2))

chain

1
2
3
4
5
hallgatok_1 = ["Harry Potter", "Ron Weasly", "Hermione Granger", "Ginny Weasley"]
hallgatok_2 = ["Tom Denem", "Sirius Black", "Dudley Dursley", "Albus Dumbledore"]

print(itertools.chain(hallgatok_1, hallgatok_2))
print(list(itertools.chain(hallgatok_1, hallgatok_2)))

Feladatok

CLOC okosító

  1. Készítsd el a run_cloc nevű függvényt, ami egy fájl útvonalát várja paraméterben. A függvény futtassa le a cloc eszközt a megadott forrásfájlra, a subprocess segítségével. A kimenetet kapd el, és figyelj rá, hogy maximum 5 másodpercig várj a függvény futására. A kimenetet a felhasználónak ne is írja ki, hanem csak tárold le. A letárolt outputot dolgozd fel az alábbiak szerint: készítsd egy dataclasst vagy namedtuple típust, ami a következőeket tárolja: path, language, code, comment, blank. Ezek pedig az eszköz kimenetéből kiolvasott adatok legyenek, az útvonalat kivéve, mert azt a függvény paramétereként kapjuk meg. A feldolgozás végén térj vissza ezzel a feltöltött objektummal.

  2. Készíts egy loc nevű függvényt az adatosztályba (vagy globális loc nevű függvényt, ha namedtuple-t használtál). Ez adja össze egy objektum kódsorait, kommenteit és üres sorait, ezzel megállapítva, hogy hány soros ténylegesen a fájl.

  3. Készíts run_cloc_dir nevű függvényt, ami egy mappa útvonalat vár, és a mappában lévő összes függvényre meghívja a run_cloc függvényt, majd az eredményeiket egy listában visszaadja. A függvénynek legyen egy recursive paramétere, ami megadja, hogy a mappán belül rekurzívan kell-e az összes fájl, vagy csak közvetlenül a mappában lévő fájlokat szeretnénk listázni.

  4. Készíts egy max_loc nevű függvényt, ami egy mappanevet, és a recursive paramétert várja. Használja fel a run_cloc_dir nevű függvényt, de az eredményeket dolgozza fel. A megírt loc függvény segítségével válaszd ki, hogy melyik az a fájl, amelyikben a legtöbb sor kód van, és ennek nevével térj vissza.

  5. Készíts egy loc_by_lang nevű függvényt, ami egy mappanevet, és a recursive paramétert várja. Használja fel a run_cloc_dir nevű függvényt, de az eredményeket dolgozza fel. A metódus adja össze, hogy melyik nyelvből mennyi volt az összes kódsor (nem a teljes loc). Ezzel a dictionaryvel térjen vissza.

  6. Készíts parancssori paraméter beolvasót az alábbi kapcsolókkal:

    • -p: egy útvonal, ahol kell futnia a toolnak (lehet mappaa vagy fájl is), szöveges típusú.
    • -r: egy igen/nem, ami megmondja, hogy rekurzív legyen-e a keresés.
    • -m, --max: egy igen/nem, ami megmondja, hogy csak a legtöbb sorból álló fájlra vagyunk-e kíváncsiak (max_loc). Nem működhet együtt a --stat kapcsolóval.
    • --stat: egy igen/nem, ami megmondja, hogy a statisztikát kérünk-e (loc_by_lang). Nem működhet együtt a --max kapcsolóval.
    • -s: az eredmények mentése csv fájlban. A kapcsoló várja a kimeneti útvonalat, ami szöveges típusú.

Kapcsolódó linkek


Utolsó frissítés: 2021-03-25 10:58:03