Jesteśmy na terytorium wrogiego państwa, a naszą misją jest przejęcie planów inwazji. Mamy bezpośredni dostęp do systemu, na którym się znajdują. Problem jest taki, że nie znamy hasła, jednak odrobiliśmy pracę domową i poznaliśmy jego część. Musimy, na podstawie fragmentu, który znamy, wygenerować potencjalne hasła, aby jak najszybciej dostać się do systemu i przejąć plany. Zaczynajmy!

Co wiemy?

  1. Losy kraju są w naszych rękach.
  2. Znamy fragment hasła, a dokładnie jego bazowe słowo, które podsłuchaliśmy. Brzmi ono – ‚analityk’
  3. Wiemy, że ~76% haseł, jakie ustawiają ludzie, składa się z hasła bazowego, zakończonego cyfrą, przy czym któraś litera jest zamieniona na wielką. Może to być hasło ‚Analityk7’, albo ‚analityK3’.
  4. Na szczęście znamy język programowania Python, programowanie rekurencyjne oraz umiemy pisać skrypty.
  5. Nasz agent równie wie, że proces nauki języka Python, jest o wiele skuteczniejszy, jeżeli sami powtarzamy ćwiczenia i je modyfikujemy 🙂

Potencjalne rozwiązanie.

Na myśl, nasuwają się dwie funkcje:

  1. dodajLiczbę – Funkcja, przyjmująca jako parametr hasło, natomiast zwracająca listę zawierającą hasło + liczbę
  2. zamieńLiterę – funkcja, przyjmująca jako parametr hasło, natomiast zwracająca listę zawierającą listę tego samego hasło, z jedną literą wielką.

Potencjalnie, mogli byśmy pobierać hasło, generować, dla niego, wszystkie wariacje z liczbę (dzięki funkcji dodajLiczbę) następnie tworzyć wariacje z jedną literą wielką (dzięki funkcji zamieńLitere).

Zobaczmy jak mogła by wyglądać taka funkcja, mająca dodać liczbę:

def dodajCyfre (haslo, liczba, nowaLista):
    nowaLista.append(haslo + str(liczba))
    if liczba < 9:
        return dodajCyfre(haslo, liczba+1, nowaLista)
    else: return nowaLista


# na potrzeby testów, możemy wywołać naszą funkcję, i zobaczyć rezultat

print (dodajCyfre ('analityk', 0, [])) 

Jeżeli się przyjrzymy, okazuje się że funkcja jest dość prosta:

  1. Pierwszy parametr, to hasło bazowa, drugi to liczba, którą chcemy dodać, trzeci, to lista, do której dodajmy poszczególne hasła.
  2. Funkcja dodaje do listy – hasło + liczba
  3. Funkcja, wywołuje sama siebie, zwiększając liczbę o 1
  4. Jeżeli wywołamy ją poprzez dodajCyfre (‚analityk’,0,[]), to wykona się 10 razy, po czym zwróci listę zawierającą – analityk0, analityk2, …. analityk9, czyli zwróci zmienną – nowaLista

Analogicznie, możemy napisać funkcję rekurencją, która, dla zadanego hasła, wygeneruje Analityk, aNalityk, anAlityk itd

Może ona wyglądać tak:

def zamienLitere (haslo, pozycja, nowa):
    if haslo[pozycja].islower():
        haslo2 = haslo[0:pozycja] + haslo[pozycja].upper() + haslo[pozycja+1:]
        nowa.append(haslo2)
    if pozycja < len(haslo)-2:
        return zamienLitere(haslo, pozycja+1, nowa)
    else: return nowa

Wygląda i działa ona bardzo podobnie jak wyżej opisana funkcja rekurencyjna, dodająca cyfrę. Co więcej, zwróćmy uwagę, że równie przyjmuje takie same parametry ( co będzie bardzo ważne za chwilę 🙂 ). Pierwszy to hasło, drugi, to liczba, tutaj nazwana pozycję, a trzeci to nowo tworzona lista, która zostanie zwrócona po zakończeniu naszej rekurencji.

Świetnie. Mamy zatem 2 funkcje – dodajCyfrę oraz zamieńLiterę. Zastanówmy się, co z nimi możemy zrobić.

  1. Jeżeli wywołamy jedną funkcję, to dostaniemy w rezultacie listę hasłem z dodaną cyfrą. Druga funkcja, również przyjmuje jako parametr wejściowy hasło, tak więc jak dostanie listę, to nie zadziała.
  2. Przydała by się jeszcze jedna funkcja, która przechodzi przez listę haseł, i (1) poszerza listę o hasła z dodaną cyfrą, dzięki funkcji dodajCyfrę (2) następnie przechodzi o rozszerzoną listę potencjalnych haseł, i znowu ją rozszerza, tym razem o hasła ze zmienioną literą na wielką

To wszystko brzmi na zakręcone, ale zobaczmy czy w rzeczywistości takie jest:

def przejrzyj(lista, pozycja, nowa, funkcja):
    nowa += funkcja(lista[pozycja], 0, [])
    if pozycja < len(lista)-1:
        return przejrzyj(lista, pozycja+1, nowa, funkcja)
    else: return nowa

lista = przejrzyj(lista, 0, [], dodajCyfre)
lista = przejrzyj(lista, 0, [], zamienLitere)

print(lista)

Co się dzieje:

  1. jako parametr, podajemy listę, która zawiera hasło lub hasła bazowe
  2. drugi i trzeci parametr to znane nam parametry, pozycja oraz nowa lista, które możemy traktować jako parametry ‚techniczne’, które umożliwiają nam przemieszczanie się po liście. Nic dla nas nowego
  3. Bardzo ciekawy jest, za to, czwarty parametr – ‚funkcja‚. Jest to coś o czym jeszcze nie rozmawialiśmy w kursach Python. Otóż, jak się okazuje, jako parametry, możemy przekazywać, nie tylko zmienne, ale również funkcje które chcemy wywołać. W ten sposób, możemy wykonać funkcję ‚przejrzyj’, raz z funkcją ‚dodajCyfre’, jako parametr, a raz z funkcją ‚zamienLiterę’. Jest to bardzo wygodne, gdyż mamy jedną funkcję, która przechodzi przez wszystkie hasła, i wywołuje dla każdego hasła, odpowiednią funkcję, przekazaną w parametrze. Jest to dość proste, gdyż obydwie funkcje, posiadają taki sam zestaw parametrów, tak więc z punktu widzenia funkcji ‚przejrzyj’, nie jest ważne, co robi funkcja, która została przekazana jako parametr.

Spójrzmy na nasze kody, jeszcze raz. Wszystkie 3 funkcje:

def dodajCyfre (haslo, liczba, nowa):
    nowa.append(haslo + str(liczba))
    if liczba < 9:
        return dodajCyfre(haslo, liczba+1, nowa) 
    else: return nowa

def zamienLitere (haslo, pozycja, nowa):
    if haslo[pozycja].islower():
        haslo2 = haslo[0:pozycja] + haslo[pozycja].upper() + haslo[pozycja+1:]
    nowa.append(haslo2)
    if pozycja < len(haslo)-2:
        return zamienLitere(haslo, pozycja+1, nowa)
    else: return nowa

def przejrzyj(lista, pozycja, nowa, funkcja):
    nowa += funkcja(lista[pozycja], 0, [])
    if pozycja < len(lista)-1:
        return przejrzyj(lista, pozycja+1, nowa, funkcja)
    else: return nowa


# przykładowe użycie ****

lista = ['analityk']
print("Nasza lista z hasła, przed wywołaniem funkcji przejrzyj \n")
print(lista)
print ("\n")

lista = przejrzyj(lista, 0, [], dodajCyfre)

print ("Lista po wywołaniu funkcji przejrzyj, z parametrem dadajCyfre \n")
print ( lista)
print ("\n")

lista = przejrzyj(lista, 0, [], zamienLitere)

print ("lista po wywołaniu funkcji przejrzyj, z parametrem zamienLitere \n")
print( lista )
print ("\n")

Wywołanie kodów, daje następujący wynik:

Kwesta, która nie została jeszcze zaadresowana, to zamienienie naszego programu, na skrypt, czyli możliwość jego wykonywania z parametrami. Aby zbytnio, nie komplikować. Pierwszy parametr to nasze hasło bazowe, drugi parametr to nazwa pliku, w którym chcemy zapisać wyniki.

Tym samym, początek programu, może wyglądać następująco

import sys

if len(sys.argv)<2 or len(sys.argv)>3:
    print (""" Program wymaga co najmniej 1 parametru:
                1 - hasło (wymagane)
                2 - nazwa pliku w którym mają zostać zapisane wyniki. Domyślenie hasla.txt""")
    sys.exit()
elif len(sys.argv) == 3:
    plik = sys.argv[2]
else:
    plik = "hasla.txt"

lista = [sys.argv[1]]
  1. Jeżeli nie podano żadnego parametru, lub podano więcej niż 2, program wydrukuje informację o sposobie jego wywołania
  2. Jeżeli jest tylko jeden argument, to nazwa pliku, w którym mają zostać zapisane hasła, jest domyślna – hasla.txt
  3. Hasło bazowe, jest zapisywane do zmiennej ‚lista’

Samo zapisanie do pliku, może odbyć się następująco:

plik = open("./"+plik, "w")<br>
plik.write('\n'.join(lista))<br>
plik.close()

A nasze finalne kody źródłowe, mogą wyglądać następująco:

import sys

if len(sys.argv)<2 or len(sys.argv)>3:
    print (""" Program wymaga co najmniej 1 parametru:
                1 - hasło (wymagane)
                2 - nazwa pliku w którym mają zostać zapisane wyniki. Domyślenie hasla.txt""")
    sys.exit()
elif len(sys.argv) == 3:
    plik = sys.argv[2]
else:
    plik = "hasla.txt"

lista = [sys.argv[1]]

def dodajCyfre (haslo, liczba, nowa):
    nowa.append(haslo + str(liczba))
    if liczba < 9:
        return dodajCyfre(haslo, liczba+1, nowa) 
    else: return nowa

def zamienLitere (haslo, pozycja, nowa):
    if haslo[pozycja].islower():
        haslo2 = haslo[0:pozycja] + haslo[pozycja].upper() + haslo[pozycja+1:]
    nowa.append(haslo2)
    if pozycja < len(haslo)-2:
        return zamienLitere(haslo, pozycja+1, nowa)
    else: return nowa

def przejrzyj(lista, pozycja, nowa, funkcja):
    nowa += funkcja(lista[pozycja], 0, [])
    if pozycja < len(lista)-1:
        return przejrzyj(lista, pozycja+1, nowa, funkcja)
    else: return nowa

lista = przejrzyj(lista, 0, [], dodajCyfre)
lista = przejrzyj(lista, 0, [], zamienLitere)

print(lista)

plik = open("./"+plik, "w")
plik.write('\n'.join(lista))
plik.close()

Podsumowując. Gratulacje! Napisaliśmy swój pierwszy program, z użyciem funkcji rekurencyjnych. Dodatkowo, przekazywaliśmy funkcję jako parametr.

Zdajemy sobie sprawę, że funkcje rekurencyjne, często potrafią sprawiać problemy. Wielu ludzi, nie spostrzega ich jako czegoś intuicyjnego, i potrzeba czasu, aby oswoić się z tą koncepcją.

Rekomendujemy powtórzenie ćwiczeń z lekcji o funkcjach rekurencyjnych w Python, oraz samodzielne uruchomienie mini projektu, na swojej maszynie.

W następnej lekcji, odejdziemy od funkcji rekurencyjnych, jednak zostaniemy blisko samych funkcji. Będziemy rozmawiać o mechanizmie, rozszerzającym ich funkcjonalności, zwanym – dekoratorami. Widzieliśmy ich zastosowanie, w lekcji na temat programowania obiektowego, oraz dziedziczenia, jednak nie tłumaczyliśmy czym są i w czym nam mogą pomóc. Tym właśnie, zajmiemy się w następnej lekcji. Zapraszamy!

Ćwiczenia

Ćwiczenie 1. Nasz agent, właśnie zdał sobie sprawę, że potrzebuje również wariacje hasła, zawierające znaj specjalny ‚!’, umieszczony w losowych miejscach hasla. Jak może to zaimplementować?

Przykładowo: !analityK7, analit!yk4

Python, NIE tylko dla początkujących

  1. Wstęp do kursu
  2. Moduły i pakiety
  3. Programowanie obiektowe – wstęp
  4. Programowanie obiektowe – dziedziczenie
  5. Programowanie obiektowe – hermetyzacja
  6. Mini projekt – Organizer
  7. Funkcje rekurencyjne w Python
  8. Pisanie skryptów w Python
  9. Mini projekt – tajny agent, generator haseł <– bieżąca lekcja
  10. *args oraz **kwargs
  11. Dekoratory w Python
  12. Mini projekt – tajny agent – kontakty
  13. Odwzorowanie list
  14. Podsumowanie oraz dalsze kroki