Wyzwanie Python #6: Rozwiązanie
Wyjątki w poprzednich zadaniach
Kalkulator
Aby pobrać liczbę i powtarzać pytanie o liczbę aż do otrzymania poprawnej wartości, stworzyliśmy funkcję pomocniczą pobierz_liczbe()
Aby zabezpieczyć się przed dzieleniem przez zero, które może wystąpić zarówno w przypadku dzielenia, jak i obliczania reszty z dzielenia, wzięliśmy w blok try-except cały kod obliczający wynik wraz z wypisaniem wyniku, tak aby następny kod, po bloku try-except, był już zapytaniem o kolejne działanie.
def pobierz_liczbe():
while True:
try:
liczba = float(input())
break
except ValueError:
print("Podany tekst nie jest liczbą")
return liczba
def main():
koniec = False
while not koniec:
print("Podaj w oddzielnych wierszach liczbę, operację matematyczną: +,-,*,/,%, a następnie kolejną liczbę:")
liczba1 = pobierz_liczbe()
operacja = input()
liczba2 = pobierz_liczbe()
try:
if operacja == "+":
wynik = liczba1 + liczba2
elif operacja == "-":
wynik = liczba1 - liczba2
elif operacja == "*":
wynik = liczba1 * liczba2
elif operacja == "/":
wynik = liczba1 / liczba2
elif operacja == "%":
wynik = liczba1 % liczba2
else:
print("Niepoprawna operacja")
break
print("Twój wynik to: " + str(wynik))
except ZeroDivisionError:
print("Nastąpiło dzielenie przez zero")
print("Chcesz wykonać kolejne działanie? Wpisz literę t lub n")
kolejne = input()
if kolejne == "n":
koniec = True
elif kolejne != "t":
print("Niepoprawny wybór")
break
if __name__ == "__main__":
main()
Ułamek
W tym wypadku nie możemy w implementacji naszej klasy złapać wyjątku. Nie wiemy, czy moment, gdy dzielimy ułamek przez zero jest absolutnym błędem krytycznym, który powinien awaryjnie zakończyć działanie programu, czy też można tę sytuację odratować. Dlatego tylko rzucamy wyjątek, a to na użytkowniku naszej klasy (w tym wypadku funkcja main()
) spoczywa odpowiedzialność, co z tym fantem zrobić.
Dodajmy, że zamiast rzucać wyjątek w funkcji dziel()
, rzucamy go w konstruktorze. Dzięki temu jednym kodem zabezpieczamy się także przed ręcznym stworzeniem ułamka o mianowniku zero.
import math
class Ulamek:
def __init__(self, licznik, mianownik):
if mianownik == 0:
raise ZeroDivisionError("Mianownik równy zero.")
self.licznik = licznik
self.mianownik = mianownik
def wypisz(self):
print(f"({self.licznik})/({self.mianownik})")
def skroc(self):
nwd = math.gcd(self.licznik, self.mianownik)
self.licznik //= nwd
self.mianownik //= nwd
@staticmethod
def dodaj(u1, u2):
lewy_licznik = u1.licznik * u2.mianownik
prawy_licznik = u2.licznik * u1.mianownik
wynik = Ulamek(lewy_licznik + prawy_licznik, u1.mianownik * u2.mianownik)
wynik.skroc()
return wynik
@staticmethod
def odejmij(u1, u2):
lewy_licznik = u1.licznik * u2.mianownik
prawy_licznik = u2.licznik * u1.mianownik
wynik = Ulamek(lewy_licznik - prawy_licznik, u1.mianownik * u2.mianownik)
wynik.skroc()
return wynik
@staticmethod
def mnoz(u1, u2):
wynik = Ulamek(u1.licznik * u2.licznik, u1.mianownik * u2.mianownik)
wynik.skroc()
return wynik
@staticmethod
def dziel(u1, u2):
wynik = Ulamek(u1.licznik * u2.mianownik, u1.mianownik * u2.licznik)
wynik.skroc()
return wynik
def main():
u1 = Ulamek(0, 4)
u2 = Ulamek(2, 6) # nieskrocony
try:
u3 = Ulamek.dziel(u2, u1)
except ZeroDivisionError as e:
print(e)
if __name__ == "__main__":
main()
Operacje arytmetyczne
Tutaj postępujemy bardzo podobnie do klasy Ulamek: znów rzucamy wyjątek w konstruktorze.
from abc import ABC, abstractmethod
import math
class Wezel(ABC):
@abstractmethod
def nazwa(self):
pass
def wypisz(self):
print(f"Wykonuję {self.nazwa()}.", end=' ')
@abstractmethod
def wartosc(self):
pass
class Liczba(Wezel):
def __init__(self, liczba):
self.liczba = liczba
def nazwa(self):
return "liczba"
def wypisz(self):
print(f"Jestem liczbą {self.liczba}")
def wartosc(self):
return self.liczba
class Suma(Wezel):
def __init__(self, skladnik1, skladnik2):
self.skladnik1 = skladnik1
self.skladnik2 = skladnik2
def nazwa(self):
return "dodawanie"
def wypisz(self):
self.skladnik1.wypisz()
self.skladnik2.wypisz()
super().wypisz()
print(f"{self.skladnik1.wartosc()}+{self.skladnik2.wartosc()}={self.wartosc()}")
def wartosc(self):
return self.skladnik1.wartosc() - self.skladnik2.wartosc()
class Roznica(Wezel):
def __init__(self, odjemna, odjemnik):
self.odjemna = odjemna
self.odjemnik = odjemnik
def nazwa(self):
return "odejmowanie"
def wypisz(self):
self.odjemna.wypisz()
self.odjemnik.wypisz()
super().wypisz()
print(f"{self.odjemna.wartosc()}-{self.odjemnik.wartosc()}={self.wartosc()}")
def wartosc(self):
return self.odjemna.wartosc() - self.odjemnik.wartosc()
class Iloczyn(Wezel):
def __init__(self, czynnik1, czynnik2):
self.czynnik1 = czynnik1
self.czynnik2 = czynnik2
def nazwa(self):
return "mnożenie"
def wypisz(self):
self.czynnik1.wypisz()
self.czynnik2.wypisz()
super().wypisz()
print(f"{self.czynnik1.wartosc()}*{self.czynnik2.wartosc()}={self.wartosc()}")
def wartosc(self):
return self.czynnik1.wartosc() * self.czynnik2.wartosc()
class Iloraz(Wezel):
def __init__(self, dzielna, dzielnik):
if dzielnik.wartosc() == 0:
raise ZeroDivisionError("Wartość dzielnika równa zero.")
self.dzielna = dzielna
self.dzielnik = dzielnik
def nazwa(self):
return "dzielenie"
def wypisz(self):
self.dzielna.wypisz()
self.dzielnik.wypisz()
super().wypisz()
print(f"{self.dzielna.wartosc()}/{self.dzielnik.wartosc()}={self.wartosc()}")
def wartosc(self):
return self.dzielna.wartosc() / self.dzielnik.wartosc()
class Silnia(Wezel):
def __init__(self, liczba):
self.liczba = liczba
def nazwa(self):
return "silnia"
def wypisz(self):
self.liczba.wypisz()
super().wypisz()
print(f"{self.liczba.wartosc()}!={self.wartosc()}")
def wartosc(self):
return math.factorial(self.liczba.wartosc())
def main():
minus_jeden = Liczba(-1)
cztery = Liczba(4)
piec = Liczba(5)
siedem = Liczba(7)
osiem = Liczba(8)
zero = Liczba(0)
try:
dodawanie = Suma(piec, siedem)
odejmowanie = Roznica(osiem, cztery)
mnozenie = Iloczyn(dodawanie, odejmowanie)
dzielenie = Iloraz(mnozenie, zero)
silnia = Silnia(dzielenie)
silnia.wypisz()
except ZeroDivisionError as e:
print(e)
if __name__ == "__main__":
main()
Liczenie linii
To zadanie jest raczej samowyjaśniające się. Zwróćmy uwagę, że obsługujemy przypadek, gdy nie ma pliku na dysku.
def main():
plik_we = "/sciezka/do/pliku.txt"
plik_wy = "/sciezka/do/pliku2.txt"
try:
with open(plik_we) as we:
wiersze = we.readlines()
liczba_wierszy = len(wiersze)
print(f"Liczba linii w pliku to {liczba_wierszy}")
with open(plik_wy, "w") as wy:
wy.writelines([plik_we, '\n', str(liczba_wierszy)])
except FileNotFoundError:
print("Nie znaleziono pliku")
if __name__ == "__main__":
main()
Sprzątanie
To zadanie jest dość proste, jednak podejdźmy do niego ze szczególną ostrożnością, gdyż jest w nim kod kasujący pliki. Dlatego ze szczególną uwagą wybierzmy folder, w którym będziemy działać. Zmodyfikowaliśmy trochę kod względem oryginalnej treści: kasujemy pliki zmodyfikowane w ciągu ostatnich dwóch minut, a także o rozmiarze mniejszym niż 1 MB. Dlatego wystarczy w celach testowych utworzyć pusty plik tekstowy w folderze (najlepiej stworzonym specjalnie na potrzeby tego ćwiczenia) i w ciągu dwóch minut uruchomić kod.
Przejdźmy do samego kodu. Pozyskujemy listę plików w ustalonym folderze, a następnie dla każdego z nich wykonujemy następujące działania: tworzymy ściężkę bezwzględną, której będziemy używać, aby skasować plik. Pobieramy informacje o pliku, w tym rozmiar i datę modyfikacji. Następnie je przekształcamy: rozmiar w bajtach zamieniamy na rozmiar w MB (dzielimy przez $1024^2$, bo 1024 bajtów to jeden kilobajt, a 1024 kilobajty to jeden megabajt), a datę zamieniamy na łatwiejszy w użyciu format. Następnie robimy nieomawiany wcześniej ruch: od aktualnej daty odejmujemy datę modyfikacji. Dzięki temu dostajemy typ różnicy czasowej (timedelta
). Obiekt tej klasy przechowuje mikrosekundy, sekundy oraz dni. Dlatego, aby obliczyć lata czy minuty, należy samemu dokonać mnożenia. Zwróćmy uwagę, że ma znaczenie, co odejmiemy od czego, bo możemy dostać ujemną różnicę. Wypisujemy wiele informacji na temat pliku na ekran, a następnie kasujemy pliki spełniające warunki.
import os
import datetime
def main():
katalog = "/sciezka/do/folderu"
pliki = os.listdir(katalog)
print(pliki)
for plik in pliki:
sciezka_bezwzgledna = os.path.join(katalog, plik)
(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(sciezka_bezwzgledna)
rozmiar_MB = size/(1024**2)
data_modyfikacji = datetime.datetime.fromtimestamp(mtime)
roznica = datetime.datetime.now() - data_modyfikacji
print(f"{sciezka_bezwzgledna}, size = {size}, rozmiar MB: {rozmiar_MB}, mtime = {data_modyfikacji}, rok={data_modyfikacji.year}, roznica={roznica}, wiecej niz rok = {roznica.days >= 365}")
if roznica.seconds < 2*60 and rozmiar_MB < 1:
os.remove(sciezka_bezwzgledna)
if __name__ == "__main__":
main()



