Nie pisz (prawie) tego samego 100 razy

Automatyzacja powtarzalnego tekstu za pomocą Pythona

Python
Nauka
Narzędzia
Wspominałem już, że nie znoszę mechanicznej pracy. A królową wśród prac mechanicznych jest pisanie wiele razy prawie tego samego tekstu, który różni się tylko w szczegółach. Zdarza się to non-stop. Jednocześnie jest to ten rodzaj pracy, który można niskim kosztem zautomatyzować. W tym wpisie postaram się pokazać, jak można uniknąć góry bezsensownej pracy za pomocą prostych pętli i funkcji print() w Pythonie.
Autor

Jakub Jędrusiak

Opublikowano

12 maja 2023

W tym tekście opisuję generowanie nowego tekstu o określonej strukturze. Do wyszukiwania i zmieniania określone rzeczy w istniejącym już tekście służą wyrażenia regularne (RegEx), o których piszę w tym tekście. Łącznie to bardzo proste, a jednocześnie bardzo potężne narzędzia, które pozwalają szybko i niskim kosztem odjąć dużo bezsensownej, mechanicznej pracy każdemu. Nie tylko naukowcom czy studentom, ale każdemu, kto pisze tekst na komputerze.

To, co tutaj opiszę, pierwszy raz poważnie wykorzystałem, gdy pomagałem swojej siostrze w pracy. Miała ona wydłużyć plik, w którym zapisywane były teksty, jakie mają pojawić się w live’ie tego dnia (siostra pracuje przy kanale na YouTube). Plik ten miał prostą strukturę.

Data:11.05.2023
I

II

III

Gdy całość się kończyła, osoba odpowiedzialna dopisywała tę strukturę na ileś dni do przodu i tak co jakiś czas. Jest to ten rodzaj pracy, którego nie znoszę i który jest łatwy do zautomatyzowania. Można to zrobić z kilku powodów, z których najważniejszy jest ten – plik miał przewidywalną, z góry określoną strukturę. Miał konkretne stałe elementy i konkretne elementy zmienne. Tutaj elementem zmiennym była data, która zmienia się w sposób przewidywalny1. Mamy więc określony wzór, schemat, który tylko musimy wypełnić datami. To też zrobiłem i w ten sposób wygenerowałem dla siostry plik dla rok do przodu. Powiedziała w pracy, że miała trochę czasu, to dopisała więcej. Podobno się zdziwili.

Ta cecha, tzn. przewidywalność jakiegoś tekstu, pozwala nam zautomatyzować jego pisanie. Nieważne, czy tym tekstem są oznaczenia kolumn (MMPI_1, MMPI_2, …, MMPI_567), czy złożone zagnieżdżone struktury np. pytań i odpowiedzi w ankiecie, jeśli tekst jest przewidywalny, da się go wygenerować.

1 Python

W tym tekście wykorzystamy sobie język programowania o nazwie Python. Tego typu praca z tekstem jest tak podstawowa, że można ją wykonać w prawie każdym języku programowania (w tym w R za pomocą paste() czy paste0()), ale tutaj wykorzystamy Pythona, bo to chyba najpopularniejszy język programowania w ogóle, drugi najpopularniejszy w statystyce, a przy tym jest to język ogólnego przeznaczenia. A także ma nazwę od Monty’ego Pythona, więc wiadomo, że warto. Specjalistą od Pythona nie jestem, moim pierwszym językiem programowania jest R, ale co tam, żyje się raz.

Żeby móc robić cokolwiek w Pythonie, musimy zainstalować sobie interpreter stąd. Interpreter to coś w rodzaju programu, który potrafi czytać i wykonywać kod w danym języku. Można powiedzieć w pewnym uproszczeniu, że instalujemy sobie Pythona. Koniecznie zaznaczcie w trakcie instalacji, żeby dodać Pythona do PATH. Teoretycznie tyle wystarczy, ale żeby uprzyjemnić proces pisania, fajnie jest przygotować sobie jakieś IDE (program do programowania), np. Visual Studio Code albo chociaż porządny edytor tekstu w stylu Notepad++.

Gdy to zrobimy, możemy dotrzeć do Pythona na kilka sposobów. Podstawowy to wejście w terminal (w Windowsie PowerShell albo wiersz polecenia) i wpisanie tam Python. Gdy potwierdzimy enterem, dostaniemy konsolę Pythona, gdzie możemy wpisywać komendy. Drugi sposób, przez IDE, to stworzenie pliku z rozszerzeniem .py (np. „skrypt.py” albo „znowu_dają_mi_bezsensowna_robote.py”) zapisywanie w nim naszych komend. Drugi sposób przyda się, gdy chcemy sobie komendy zachować na przyszłość albo piszemy coś bardziej skomplikowanego, na wiele linijek. IDE często mają specjalny guzik do uruchomienia takiego kodu2. Możemy też w wierszu polecenia wpisać Python C:\ścieżka\do\pliku.py, np. Python "C:\Users\Jakub\Desktop\RSES.py". Ważne – w „suchym" wierszu polecenia, nie w konsoli Pythona.

Konsola Pythona w PowerShell

Konsola Pythona w konsoli w Linuxie

3 Pętle

Pętle to w językach programowania sposób, żeby wiele razy zrobić to samo albo prawie to samo. W (prawie) każdym języku programowania znajdziemy dwa rodzaje pętli – for i while.

3.1 Pętla for

Pętla for jest najprostszym rodzajem pętli i tym, z czego będziemy stale korzystać. Omówmy sobie ją na przykładzie generowania nazw kolumn.

for i in range(1, 11):
    print(f"MMPI_{i}")
MMPI_1
MMPI_2
MMPI_3
MMPI_4
MMPI_5
MMPI_6
MMPI_7
MMPI_8
MMPI_9
MMPI_10

i jest nazwą dla zmiennej, która po kolei przyjmie wartości od 1 do 10. Najpierw wszystko, co znajduje się w pętli, wykona się tak, jakby i miało wartość 1. Potem wykona się to znowu, ale z i = 2 itd. To jest podstawowy sposób działania zmiennej for. Potem mamy słowo in, a za nim zbiór wartości, które i ma po kolei przyjmować. W tym wypadku tym zbiorem jest funkcja range(), która sama generuje nam liczby od 1 do 10.

Dlaczego jednak napisałem range(1, 11) a nie range(1, 10)? Python działa tutaj specyficznie. Wynika to z faktu, że w informatyce liczy się od 0, nie od 1. Jeśli do funkcji range() wrzucę tylko jedną liczbę, czyli na przykład range(10), to dostanę 10 elementów. Ponieważ jednak pierwszy element to 0, to będą to liczby od 0 do 9. Mogę podać dwie liczby, żeby powiedzieć funkcji range(), od czego ma zacząć, ale wtedy muszę mieć w głowie, że skoro range(0, 10) oznacza 10 liczb od 0 do 9, to liczby od 1 do 10 muszę zapisać jako range(1, 11). Innymi słowy koniec skali nie wlicza się do zakresu.

Jeśli piszemy to w konsoli, a nie w pliku, możemy zapisać taką pętlę w jednej linijce – for i in range(1, 11): print(f"MMPI_{i}"). Możemy też zapisać samo for i in range(1, 11): (nie zapominając o dwukropku) i potwierdzić enterem. W obu wypadkach wyświetli nam się w konsoli wielokropek i będziemy mogli dopisywać kolejne komendy z pętli. Gdy będziemy usatysfakcjonowani, klikamy enter po raz kolejny, a pętla wykonuje się.

Tak jak wspomniałem, domyślnie print() wyrzuca do konsoli to, co tej funkcji podaliśmy, dodając na koniec nową linię. Możemy jednak chcieć, żeby nasze elementy pojawiły się po przecinku albo oddzielone spacjami (albo jedno i drugie) i wtedy możemy zmienić argument end.

for i in range(1, 11):
    print(f"MMPI_{i}", end = ", ")
MMPI_1, MMPI_2, MMPI_3, MMPI_4, MMPI_5, MMPI_6, MMPI_7, MMPI_8, MMPI_9, MMPI_10, 

Co prawda po ostatnim elemencie też dostajemy przecinek i spację, ale to już możemy usunąć ręcznie. W Pythonie też da się to zaprogramować, ale nie chcę za bardzo gmatwać.

3.2 Pętla while

Pętla while to bardziej podstawowy, prosty rodzaj pętli. Większość pętli while da się napisać w formie pętli for, dlatego nie będę się nad tym jakoś szczególnie rozwodził, ale warto wiedzieć, że coś takiego istnieje. Napiszmy przykład z poprzedniej sekcji w postaci pętli while.

i = 1
while i <= 10:
    print(f"MMPI_{i}")
    i = i + 1  # ewentualnie i += 1
MMPI_1
MMPI_2
MMPI_3
MMPI_4
MMPI_5
MMPI_6
MMPI_7
MMPI_8
MMPI_9
MMPI_10

W pętli while potrzebujemy jakiejś wcześniej określonej zmiennej, w tym wypadku i. Pierwszą rzeczą, którą while robi, jest sprawdzenie, czy warunek jest prawdziwy. Prawdą jest, że 1 jest mniejsze lub równe 10, więc while puszcza wszystko, co znajduje się w środku pętli. Instrukcja print() jest identyczna. Kolejna linijka może wydawać się nieco tajemnicza. Służy ona powiększeniu i o 1. Matematycznie zapis i = i + 1 może wydawać się dziwny, ale trzeba pamiętać, że = nie oznacza tutaj porównania (to się robi poprzez ==), tylko przypisanie. Można więc tę komendę przeczytać „Niech i przyjmie wartość równą aktualnej wartości i plus jeden”. W skrócie możemy to zapisać jako i += 1. Po co to robimy? Bo w następnym kroku pętla while znów sprawdzi, czy warunek jest prawdziwy. Teraz i = 2, a 2 to ciągle mniej niż 10, więc pętla wykona się znów. Tak będzie robić aż do momentu, w którym warunek nie będzie prawdziwy, a wiec w tym wypadku aż i nie przyjmie wartości 11. Jeśli nie umieściłbym w kodzie linijki i = i + 1, warunek i <= 10 byłby zawsze prawdziwy i pętla działałaby wiecznie. Czy raczej do wyczerpania pamięci.

4 Generowanie tekstu

Weźmy sobie na warsztat bardziej złożony przykład. Niedawno musiałem generować bardzo skomplikowany tekst, który stał się częścią ankiety w PsyToolKit. Jest to świetne narzędzie do prowadzenia ankiet i eksperymentów, głównie psychologicznych. Jego największą zaletą – według mnie – jest to, że zarówno ankiety, jak i eksperymenty mogą być pisane w postaci zwykłego tekstu3. Pozwala to na olbrzymią giętkość, jaką zapewniają języki programowania, ale także daje duże możliwości automatyzacji. O PsyToolKit na pewno jeszcze w przyszłości napiszę.

4.1 Ankieta w PsyToolKit

Pokażę teraz, jak sprawnie przerobić kwestionariusz na ankietę w PsyToolKit. Najsprawniej byłoby, co prawda, użyć programiku PsyToolKit Questionnaire Formatter, który opiera się na tym, co tutaj opisuję. Poznajmy ten mechanizm od kuchni, żeby w razie czego móc go dopasować do własnych, specyficznych celów, niekoniecznie związanych w ogóle z PsyToolKit.

Załóżmy, że chcielibyśmy wykorzystać w naszym badaniu kwestionariusz samooceny Rosenberga (1965). Musimy go w takim razie zapisać tak, jak PsyToolKit każe nam formatować pytania do ankiety. Struktura pytania w PsyToolKit wygląda tak:

l: RSES_1
t: radio
q: I feel that I am a person of worth, at least on an equal plane with others.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

Po pierwsze mamy l, czyli label. Posłuży nam to jako wewnętrzna „nazwa” pozycji testowej i nagłówek kolumny w bazie danych. Dalej mamy t, czyli type, gdzie radio oznacza pytanie jednokrotnego wyboru. Inne typy znajdziemy w dokumentacji. Następnie mamy q, czyli question, właściwa treść pozycji testowej i pod nią odpowiedzi wypisane od myślników. Możemy tu dodawać inne rzeczy (np. o: random, żeby kolejność odpowiedzi była losowa), ale załóżmy, że na ten moment tyle nam wystarczy.

Po pierwsze spróbujmy zidentyfikować, co w naszym schemacie jest stałe i co się zmienia. Tutaj zmieniają się dwie rzeczy – treść pozycji testowej i numerek przy RSES. Cała reszta jest identyczna dla każdej pozycji testowej.

Skoro musimy mieć w naszych pytaniach treść pozycji testowej, musimy nasz kwestionariusz wkleić do skryptu. Zapiszemy go w postaci listy.

RSES = [
    "I feel that I am a person of worth, at least on an equal plane with others.",
    "I feel that I have a number of good qualities.",
    "All in all, I am inclined to feel that I am a failure.",
    "I am able to do things as well as most other people.",
    "I feel I do not have much to be proud of.",
    "I take a positive attitude toward myself.",
    "On the whole, I am satisfied with myself.",
    "I wish I could have more respect for myself.",
    "I certainly feel useless at times.",
    "At times I think I am no good at all."
]

Cała lista jest nawiasach kwadratowych, każdy item jest w cudzysłowie, zaś itemy rozdzielone są przecinkami. Całą listę zapisałem do zmiennej o nazwie RSES. Teraz możemy powiedzieć Pythonowi, żeby zrobił całą serię pytań w stylu PsyToolKit, gdzie po q za każdym razem wstawi jedną z pozycji testowych.

for item in RSES:
    print("l: RSES_1")
    print("t: radio")
    print(f"q: {item}")
    print("- Strongly Agree\n- Agree\n- Disagree\n- Strongly Disagree")
    print() # pusta linijka
l: RSES_1
t: radio
q: I feel that I am a person of worth, at least on an equal plane with others.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_1
t: radio
q: I feel that I have a number of good qualities.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_1
t: radio
q: All in all, I am inclined to feel that I am a failure.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_1
t: radio
q: I am able to do things as well as most other people.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_1
t: radio
q: I feel I do not have much to be proud of.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_1
t: radio
q: I take a positive attitude toward myself.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_1
t: radio
q: On the whole, I am satisfied with myself.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_1
t: radio
q: I wish I could have more respect for myself.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_1
t: radio
q: I certainly feel useless at times.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_1
t: radio
q: At times I think I am no good at all.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

Jak widzimy, nasza zmienna w pętli (1) nie musi nazywać się i oraz (2) nie musi być liczbą. Jak widzimy, możemy wykonać pętlę za każdym razem przypisując do zmiennej kolejny tekst z listy. Każdą linijkę możemy zapisać w osobnej komendzie print() lub też całość wpisać w jedną komendę, zaznaczając nowe linijki za pomocą \n. Tak zrobiłem w przedostatniej linijce.

Nasz wynik ma jednak problem – każde pytanie nazywa się RSES_1. Liczba po RSES_ musi się zmieniać. Tym razem jest to trudniejsze niż wcześniej, bo item nie jest tutaj liczbą, tylko treścią pytania, więc nie możemy zapisać RSES_{item}. Z pomocą przychodzi nam jednak funkcja enumerate(). Pozwala ona przerobić listę na tzw. krotki (ang. tuples, tutaj 2-tuples czyli dwukrotki). Każda taka dwukrotka zawiera numer pozycji na liście (licząc od 0) oraz samą pozycję. Numer jest pierwszy, więc dostaniemy się do niego pisząc item[0]. Jeśli chcemy dostać treść pozycji testowej, zapiszemy item[1]. Całość wyglądałaby więc tak:

for item in enumerate(RSES):
    print(f"l: RSES_{item[0] + 1}")
    print("t: radio")
    print(f"q: {item[1]}")
    print("- Strongly Agree\n- Agree\n- Disagree\n- Strongly Disagree")
    print() # pusta linijka
l: RSES_1
t: radio
q: I feel that I am a person of worth, at least on an equal plane with others.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_2
t: radio
q: I feel that I have a number of good qualities.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_3
t: radio
q: All in all, I am inclined to feel that I am a failure.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_4
t: radio
q: I am able to do things as well as most other people.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_5
t: radio
q: I feel I do not have much to be proud of.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_6
t: radio
q: I take a positive attitude toward myself.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_7
t: radio
q: On the whole, I am satisfied with myself.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_8
t: radio
q: I wish I could have more respect for myself.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_9
t: radio
q: I certainly feel useless at times.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

l: RSES_10
t: radio
q: At times I think I am no good at all.
- Strongly Agree
- Agree
- Disagree
- Strongly Disagree

Zwróćmy uwagę, że w pierwszym print() napisałem item[0] + 1. item[0] to numer pozycji testowej, ale czemu + 1? Bo liczenie w informatyce zaczyna się od 0 (co ciągle powoduje problemy u całej reszty ludzkości), więc jeśli chcę mieć numerację od 1 do 10 zamiast od 0 do 9, to do każdego numeru muszę dodać 1.

4.2 Zapisywanie do pliku

Wynik działania takiej funkcji możemy od razu zapisać do pliku tekstowego za pomocą specjalnego operatora > w PowerShell4. Da się to zrobić nie wychodząc z Pythona, ale to niepotrzebnie skomplikowane. Druga opcja to po prostu skopiować wygenerowany tekst z konsoli. Jeśli ktoś rzadko z niej korzysta, to ostrzegam, że do kopiowania i wklejania zamiast Ctrl+C i Ctrl+V w konsoli używamy Ctrl+Shift+C i Ctrl+Shift+V. Głównie dlatego, że Ctrl+C ma tam inną funkcję – przerywa aktualnie wykonywane zadanie. Ten sposób może jednak nie być odpowiedni, jeśli tekst jest długi, bo wtedy konsola może zjeść nam kilka (lub bardzo dużo) linijek. Jak więc wykorzystać >? W PowerShell (nie w konsoli Pythona! w zwykłym, gołym PowerShell) wpisujemy coś takiego:

Python "C:\ścieżka\do\skryptu.py" > "C:\ścieżka\do\pliku.txt"

Jako podpowiedź mogę podrzucić, że Windows 11 pozwala kopiować ścieżki po kliknięciu na plik prawym przyciskiem myszy. W Windowsie 10 też możemy sobie w ten sposób ułatwić życie, tylko klikając prawy przycisk myszy musimy jeszcze przytrzymać shift. Ostateczna komenda mogłaby więc wyglądać tak:

Python "C:\Users\Jakub\Desktop\RSES.py" > "C:\Users\Jakub\Desktop\RSES.txt"

Powoduje to zapisanie tego, co normalnie skrypt wydrukowałby w konsoli, w pliku RSES.txt na pulpicie. Rozszerzenie .txt jest konieczne. Oczywiście jeśli Twoja nazwa użytkownika to Jakub. Niestety wpisywanie własnych ścieżek jest konieczne.

4.3 Daty

Wróćmy do pierwotnego przykładu z plikiem mojej siostry. Jest to przykład o tyle specyficzny, że zmiennym elementem jest tam data. Daty zmieniają się przewidywalne, ale potrzebują specjalnych funkcji, które ogarną takie rzeczy jak to, że różne miesiące mają różną liczbę dni, istnieją lata przestępne itd. Kiedy rzeczywiście miałem ten problem, użyłem funkcji date w Linuksie, która sama z siebie pozwala na robienie takich rzeczy. Większość osób (niestety) nie korzysta z linuksa, dlatego na potrzeby tego wpisu zaadaptuję to rozwiązanie do Pythona. Albo chociaż spróbuję.

Żeby operować na datach, musimy na szczycie skryptu (lub najpierw w konsoli) zapisać:

import datetime

Załaduje to pakiet datetime pozwalający operować na datach. Robimy to tylko raz na daną sesję, czyli jak raz załadujemy ten pakiet, możemy z niego korzystać dopóty, dopóki nie wyjdziemy z Pythona. Jeśli chcemy dostać się do funkcji z pakietu datetime, musimy zapisać je z „przedrostkiem" datetime., jak zobaczymy za chwilę.

Przypomnijmy strukturę pliku, który chcemy stworzyć:

Data:11.05.2023
I

II

III

Po pierwsze musimy ustalić, od jakiej daty chcemy zacząć. Możemy wykorzystać dzisiejszą datę wpisując datetime.date.today(). Możemy też wybrać datę początkową arbitralnie, używając czegoś w rodzaju datetime.date(2023, 5, 11). Data jest w kolejności ISO 8601, czyli rok, miesiąc, dzień.

Po drugie będziemy musieli dodawać do naszej daty dni. Robimy to funkcją datetime.timedelta(days = 1). W tej formie do naszej daty dodamy jeden dzień. Takie coś rzeczywiście do daty dodajemy, czyli piszemy na przykład datetime.date(2023, 5, 11) + datetime.timedelta(days = 1). Wynikiem będzie tutaj 12 maja 2023 roku.

Po trzecie nasza data musi być w określonym formacie, w tym wypadku DD.MM.RRRR. Domyślnie daty wyświetlają się w formacie ISO 8601, czyli RRRR-MM-DD. Formatować daty można metodą strftime(). Metody to szczególny rodzaj funkcji, który wykorzystujemy tak, że doklejamy je po kropce do nazwy naszego obiektu np. z datą. Najlepiej będzie to widać w przykładzie. Do samej metody wrzucamy zakodowany format, w jakim datę chcemy uzyskać. Wykorzystamy tutaj specjalne kody, których listę możemy znaleźć tutaj. Potrzebny nam format zakodujemy jako "%d.%m.%Y".

Zbierając to wszystko do kupy uzyskujemy coś takiego:

import datetime

start_date = datetime.date(2023, 5, 11)

for i in range(7):
    date = start_date + datetime.timedelta(days = i)
    date_formatted = date.strftime("%d.%m.%Y")
    print(f"Data:{date_formatted}")
    print("I\n\nII\n\nIII\n\n")
Data:11.05.2023
I

II

III


Data:12.05.2023
I

II

III


Data:13.05.2023
I

II

III


Data:14.05.2023
I

II

III


Data:15.05.2023
I

II

III


Data:16.05.2023
I

II

III


Data:17.05.2023
I

II

III

Wykorzystałem tutaj kilka zmiennych, które nazwałem start_date, date i date_formatted. Nazwy zmiennych mogą być jakiekolwiek. Wybrałem takie, żeby to było czytelne. Jak to w programowaniu, możemy to napisać na parę sposobów. Dla przykładu tutaj datę już sformatowaną zapisałem w osobnej zmiennej, ale mógłbym też napisać:

import datetime

start_date = datetime.date(2023, 5, 11)

for i in range(7):
    date = start_date + datetime.timedelta(days = i)
    print(f"Data:{date.strftime('%d.%m.%Y')}")
    print("I\n\nII\n\nIII\n\n")

…albo w ogóle wszystko zapisać już wewnątrz pętli:

import datetime

for i in range(7):
    date = datetime.date(2023, 5, 11) + datetime.timedelta(days = i)
    print(f"Data:{date.strftime('%d.%m.%Y')}")
    print("I\n\nII\n\nIII\n\n")

…i to też zadziała. Zależy co uznajemy za bardziej czytelne. Zwróćmy uwagę, że piszemy days = i, a nie days = {i}. Nawiasy klamrowe potrzebne są tylko w f-strings. Pamiętamy jeszcze, że domyślnie range(7) generuje liczby od 0 do 6, więc na początku timedelta() dodaje 0 dni, potem 1 dzień, 2 dni i aż do 6 dni. Tym razem jest to nam na rękę, bo dzięki temu pierwszą datą jest wybrana przez nas data, a nie dzień później. Uzyskujemy więc tydzień rozpiski. Siła automatyzacji polega na tym, że mając te 4 linijki kodu, taki sam nakład pracy potrzebny jest do zrobienia takiej rozpiski dla tygodnia, miesiąca czy 30 lat5.

5 Podsumowanie

Opanowanie automatyzacji wymaga sporo praktyki i początkowo może zajmować więcej czasu, niż wykonanie jakiejś pracy ręcznie. Jednak z doświadczeniem przychodzi efektywność. Już opanowanie podstaw sprawia, że często możemy oszczędzić sobie wielu, wielu godzin pracy, a jeśli poświęcimy na to trochę więcej, możemy wydłużyć sobie życie o naprawdę sporo wolnego. Podsumujmy to, czego się dziś nauczyliśmy.

  1. Możemy automatyzować generowanie tekstu, w którym jakaś część podlega przewidywalnym zmianom.

  2. Za wyświetlanie tekstu odpowiada funkcja print().

  3. Możemy powtórzyć jakiś tekst określoną liczbę razy i zaplanować ewentualne zmiany w tym tekście za pomocą pętli for.

  4. Możemy podstawić do ciągu znaków wartości zmiennych za pomocą f-strings, np. f"MMPI_{i}".

  5. Ciągi liczb do pętli generujemy funkcją range(), przy czym domyślnie liczy ona od 0. Możemy do niej wrzucić jedną liczbę albo koniec i początek ciągu, który chcemy uzyskać. Koniec domyślnie nie wlicza się do ciągu.

  6. Daty to szczególne wartości, którymi zawiadują funkcje z pakietu datetime.

  7. Zmienne z datami tworzymy za pomocą date(), różnice w datach liczymy za pośrednictwem timedelta(), a także możemy formatować daty poprzez metodę strftime().

  8. Wyniki działania takich skryptów zapisujemy do plików operatorem >. Robimy to w konsoli systemowej (np. PowerShell), a nie w konsoli Pythona.

Przypisy

  1. Ciekawostką może być tutaj wczesny kalendarz hebrajski. Kalendarz hebrajski jest księżycowy, ma 12 miesięcy, ale w latach przestępnych dodawany jest 13. miesiąc – adar szeni. Takich lat przestępnych musi przypaść 7 w ciągu 19 lat. Obecnie jest to skodyfikowane (zob. cykl Metona), ale początkowo nie wiadomo było, które konkretnie lata w cyklu mają być przestępne i było to ad hoc ustalane przez społeczność żydowską. Tym samym lata przestępne pojawiały się znienacka i data wcale nie zmieniała się w przewidywalny sposób. Miało to poważne konsekwencje dla daty Wielkanocy, bo zanim został ustalony stabilny wzór (Wielkanocy, nie lat przestępnych w kalendarzu żydowskim, to nadeszło później), nigdy nie wiadomo było, kiedy wypadnie 14 nisan, czyli rocznica śmierci Jezusa według kalendarza żydowskiego.↩︎

  2. Instrukcję, jak uruchamiać takie pliki bezpośrednio w Notepad++, znajdziemy np. tutaj.↩︎

  3. Jest też easy mode, który przypomina generatory na stronach typu Formularze Google.↩︎

  4. Sam korzystam z linuksa (I use arch, BTW), ale nie łudzę się – większość osób, które to czytają, to windowsiarze. W linuksie operator > działa identycznie.↩︎

  5. Sprawdziłem, taki skrypt generuje u mnie 30 lat rozpiski w 47 milisekund (!), a gotowy plik ma 310 kB.↩︎