Logo MockIT na ciemnym tle.
Umów rozmowę
Blog
Zaloguj się
Logo MockIT na ciemnym tle.
Powrót

Python: Wiedza w pigułce, którą każdy powinien mieć

Nie wiesz od czego zacząć naukę Pythona? A może chciałbyś się dowiedzieć czy opanowałeś absolutne podstawy tego języka? W tym artykule przybliżę Ci fundamenty wiedzy z Pythona.

Mateusz Śliwiński

Python Developer - Ach Tech


7 maja 2024

Intro

Python to jeden z najpopularniejszych i najbardziej wszechstronnych języków programowania na świecie. Jego czytelna składnia i bogaty wybór bibliotek sprawiają, że jest to doskonały wybór zarówno dla początkujących programistów, jak i doświadczonych developerów.

W tym artykule przedstawię kluczowe koncepcje i funkcjonalności języka Python oraz postaram się przypomnieć jego podstawy. Zanim przejdziemy do bibliotek i frameworków chciałbym zacząć od ogólnych zasad pisania kodu.

Clean Code: SOLID, KISS, DRY

Podczas pisania kodu w Pythonie (a także w innych językach programowania), istnieje szereg zasad, których przestrzeganie pomaga utrzymać kod czytelnym, elastycznym i łatwym do zarządzania. Wśród tych zasad wyróżniamy SOLID, KISS i DRY, które są fundamentalne dla tworzenia wysokiej jakości kodu.

SOLID

SOLID to mnemonik określający pięć podstawowych zasad projektowania obiektowego:

  • S - Single Responsibility Principle (Zasada pojedynczej odpowiedzialności): Klasa powinna mieć tylko jedną odpowiedzialność, czyli być odpowiedzialna za jedną konkretną funkcjonalność. Dzięki temu unikamy zbyt dużych i złożonych klas, co ułatwia zarządzanie kodem.

  • O - Open/Closed Principle (Zasada otwarte/zamknięte): Klasa powinna być otwarta na rozszerzenie, ale zamknięta na modyfikację. Zmiany w klasie powinny być dokonywane poprzez dodawanie nowego kodu, a nie zmienianie istniejącego.

  • L - Liskov Substitution Principle (Zasada podstawienia Liskov): Obiekty danej klasy powinny być zastępowalne przez obiekty ich podklas bez zmiany działania programu. Dzięki temu zachowujemy spójność i elastyczność kodu.

  • I - Interface Segregation Principle (Zasada segregacji interfejsów): Interfejsy powinny być konkretne dla danego klienta, unikamy tworzenia ogólnych interfejsów, które wymagają od klientów implementowania niepotrzebnych funkcji.

  • D - Dependency Inversion Principle (Zasada odwrócenia zależności): Moduły powinny być zależne od abstrakcji, a nie od konkretnych implementacji. Dzięki temu zmniejszamy zależności między modułami i ułatwiamy testowanie oraz rozwijanie kodu.

KISS

KISS, czyli Keep It Simple, Stupid, to zasada zachęcająca do pisania prostego, zrozumiałego kodu. Unikanie nadmiernego skomplikowania jest kluczowe dla utrzymania czytelności i łatwości utrzymania kodu. Im bardziej skomplikowany kod, tym trudniejsze jest jego zrozumienie i modyfikacja w przyszłości. Stosowanie prostych rozwiązań, klarownych nazw zmiennych i funkcji oraz unikanie nadmiernego zagnieżdżania warunków i pętli są kluczowymi praktykami zgodnymi z zasadą KISS.

DRY

DRY, czyli Don’t Repeat Yourself, to zasada mówiąca o unikaniu powtarzania się kodu. Wszelkie powtarzające się fragmenty kodu powinny być zrefaktoryzowane do oddzielnych funkcji lub klas, co pozwala na łatwiejsze zarządzanie i utrzymanie kodu. Powtarzający się kod jest trudny w utrzymaniu, ponieważ wymaga zmian w wielu miejscach w przypadku konieczności modyfikacji. Dzięki zastosowaniu zasady DRY, kod staje się bardziej modułowy i elastyczny.

Składnia i podstawowe typy danych w Pythonie

Python wyróżnia się czytelną i intuicyjną składnią, co czyni go bardzo przyjaznym dla użytkownika. Jego dynamiczna natura pozwala na elastyczne operowanie na różnych typach danych, bez konieczności wcześniejszego deklarowania typu zmiennej.

Typy danych w Pythonie

Integers (liczby całkowite): Są to liczby całkowite, takie jak 1, 100, -5 itp. Python pozwala na wykonywanie podstawowych operacji arytmetycznych na liczbach całkowitych, takich jak dodawanie +, odejmowanie -, mnożenie *, dzielenie /, dzielenie całkowite // oraz operacje modulo %.

Floats (liczby zmiennoprzecinkowe): Obejmują liczby zmiennoprzecinkowe, np. 3.14, 2.5, -0.5 itp. Są używane do reprezentowania liczb dziesiętnych. Python obsługuje operacje matematyczne na liczbach zmiennoprzecinkowych takie jak dodawanie, odejmowanie, mnożenie i dzielenie.

Strings (łańcuchy znaków): Są to sekwencje znaków umieszczone w cudzysłowiu, np. “Hello, World!”. W Pythonie łańcuchy znaków są niezmiennymi obiektami, co oznacza, że po utworzeniu nie można ich modyfikować. Python oferuje wiele operacji na łańcuchach, takich jak konkatenacja +, indeksowanie [], wycinanie [start:stop:step] oraz wiele metod do manipulacji tekstem.

Lists (listy): Listy są kolekcjami elementów, które mogą być różnych typów. Tworzymy je za pomocą nawiasów kwadratowych [], np. [1, 2, 3]. Listy w Pythonie są mutowalne, co oznacza, że można dodawać, usuwać i modyfikować ich elementy.

Tuples (krotki): Krotki są podobne do list, ale są niezmiennymi kolekcjami elementów. Tworzymy je za pomocą nawiasów okrągłych (), np. (1, 2, 3). Krotki są bardziej wydajne od list, gdyż zajmują mniej miejsca w pamięci, ale nie można ich modyfikować po utworzeniu.

Dictionaries (słowniki): Słowniki to kolekcje par klucz-wartość, gdzie każdy klucz jest unikalny. Tworzymy je za pomocą nawiasów klamrowych {}, np. {“klucz”: “wartość”, “imie”: “Anna”}. Słowniki umożliwiają szybkie dostęp do wartości poprzez klucz i są używane do przechowywania danych w formie par.

Instrukcje sterujące, pętle oraz typowanie w Pythonie

Python oferuje wiele instrukcji sterujących do kontroli przepływu programu oraz pętle do powtarzania kodu. Te elementy są kluczowe dla budowania elastycznych i dynamicznych programów. Oto kilka przykładów:

Instrukcje sterujące if, elif, else służą do wykonania różnych bloków kodu w zależności od spełnienia warunków.

age = 25 if age < 18: print("You are underage") elif age >= 18 and age < 65: print("You are an adult") else: print("You are senior")

Pętla while wykonuje blok kodu, dopóki warunek jest spełniony.

x = 0 while x < 5: print(x) x += 1

Pętla for służy do iteracji po sekwencjach, takich jak listy, krotki, słowniki itp.

fruits = ["apple", "banana", "orange"] for fruit in fruits: print(fruit)

Funkcja range() generuje sekwencję liczb, które można wykorzystać w pętlach.

for i in range(5): print(i)

Funkcja enumerate() służy do iteracji po sekwencji jednocześnie zwracając indeksy i elementy.

colors = ["red", "green", "blue"] for index, color in enumerate(colors): print(f"Color {index}: {color}")

Instrukcje break i continue pozwalają kontrolować działanie pętli. Break przerywa pętlę, a continue przechodzi do następnej iteracji.

for i in range(10): if i == 5: break # break loop when value of "i" reaches 5 if i % 2 == 0: continue # skip iteration if value of "i" is even print(i)

Python jest językiem dynamicznie typowanym, co oznacza, że nie musimy deklarować typu zmiennej podczas jej tworzenia. Jednak od wersji Pythona 3.6 wprowadzono możliwość dodawania adnotacji typów, co pozwala na pewne wskazanie typu danej zmiennej. W dzisiejszych czasach, często korzysta się z typów w Pythonie. Poprawia to czytelność kodu i nieco ogranicza podatność na błędy.

Przykłady:

name: str = "Alice" age: int = 30 is_adult: bool = True def greet(name: str) -> str: return f"Hello, {name}!" def void(a,b) -> None: print(a,b)

Testy jednostkowe w Pythonie

Testy jednostkowe są kluczowym elementem procesu programowania, zwłaszcza w kontekście Test-Driven Development (TDD) i utrzymywania wysokiej jakości kodu. W Pythonie, do tworzenia testów jednostkowych, często używana jest biblioteka unittest lub narzędzie pytest. Dzięki testom jednostkowym możemy sprawdzać poprawność działania naszych funkcji, metod i klas.

Biblioteka unittest

Biblioteka unittest jest częścią biblioteki standardowej Pythona i zapewnia narzędzia do tworzenia i uruchamiania testów jednostkowych. Oto przykład prostego testu jednostkowego w unittest:

import unittest def add(a, b): return a + b class TestMathOperations(unittest.TestCase): def test_addition(self): self.assertEqual(add(2, 3), 5) self.assertEqual(add(-1, 1), 0) self.assertEqual(add(0, 0), 0) if __name__ == '__main__': unittest.main()

Narzędzie pytest

Pytest jest bardziej nowoczesnym narzędziem do testowania w Pythonie i oferuje bardziej przejrzystą i wygodną składnię niż unittest.

def add(a, b): return a + b def test_addition(): assert add(2, 3) == 5 assert add(-1, 1) == 0 assert add(0, 0) == 0

W przypadku pytest nie musimy dziedziczyć z klasy czy używać specjalnych asercji, co sprawia, że pisanie testów jest bardziej przejrzyste i kompaktowe.

Wielowątkowość

Wielowątkowość w Pythonie pozwala na równoległe wykonywanie kilku części programu jednocześnie. Jest to przydatne zwłaszcza w sytuacjach, gdy chcemy wykonywać operacje równolegle, takie jak przetwarzanie danych, obsługa żądań sieciowych czy interakcje z urządzeniami wejścia-wyjścia. W Pythonie możemy korzystać z wątków za pomocą modułu threading oraz procesów za pomocą modułu multiprocessing.

import threading import time def worker(num): print(f"Worker {num} started") time.sleep(2) # Mock of certain instructions print(f"Worker {num} finished") # Thread creation threads = [] for i in range(3): t = threading.Thread(target=worker, args=(i,)) threads.append(t) t.start() # Waiting for threads to finish for t in threads: t.join() print("All threads finished")

Warto zauważyć, że korzystając z wątków (threading) w Pythonie, nie uzyskujemy pełnej równoległości ze względu na Global Interpreter Lock (GIL), który ogranicza jednoczesne wykonywanie kodu Pythona w jednym procesie. Dlatego, jeśli zależy nam na pełnej równoległości, warto rozważyć użycie procesów (multiprocessing). Dla zainteresowanych odsyłam do innego artykułu w języku angielskim opisujący to dokładniej: link

Biblioteka Pandas

Pandas to potężna biblioteka do manipulacji i analizy danych w języku Python. Oferuje wydajne i łatwe w użyciu struktury danych, takie jak DataFrame, które umożliwiają pracę z danymi w sposób podobny do arkuszy kalkulacyjnych

Struktury danych w Pandas

DataFrame jest główną strukturą danych w Pandas, przypominającą tabelę bazy danych lub arkusz kalkulacyjny. Składa się z wierszy i kolumn, a każda kolumna może mieć inny typ danych. Możemy tworzyć DataFrame z różnych źródeł danych, takich jak pliki CSV, bazy danych, inne DataFrame itp.

import pandas as pd data = {'Name': ['Alice', 'Bob', 'Charlie'], 'Age': [25, 30, 35], 'City': ['New York', 'Los Angeles', 'Chicago']} df = pd.DataFrame(data) print(df)

Series jest jednowymiarową strukturą danych w Pandas, reprezentującą pojedynczą kolumnę lub wiersz w DataFrame. Każda wartość w Series jest indeksowana, co umożliwia szybkie operacje na danych

import pandas as pd numbers = pd.Series([10, 20, 30, 40, 50]) print(numbers)

Podstawowe manipulacje na danych za pomocą pandasa

import pandas as pd df = pd.read_csv('file_name.csv') # reading data from CSV file df.head() # displays first data rows df.info() # displays basic information about data df.describe() # displays basic statistics for numeric values filtered_df = df[df['example_column'] > warunek] # filtering of rows meeting condition column_data = df['example_column'] # selecting data from one column subset = df[['example_column1', 'example_column2']] # selecting multiple columns df['new_example_column'] = values # adding new column df.dropna() # removing rows with missing values df.drop(['example_column1', 'example_column2'], axis=1) # removing specific columns grouped_df = df.groupby('example_column').mean() # grouping data and counting mean value merged_df = pd.merge(df1, df2, on='key') # connecting data frames df['example_column'] = df['example_column'].astype('new_type') # changing data in the column sorted_df = df.sort_values(by='example_column', ascending=False) # sorting data

Pandas pozwala na obszerną manipulację analizę oraz na tworzenie wykresów podstawowych jak i zaawansowanych za pomocą innych bibliotek jak matplotlib oraz seaborn. Niestety pandas i wizualizacja danych jest zbyt obszernym tematem jak na ten skromny artykuł.

Biblioteka Requests

Biblioteka requests w Pythonie jest narzędziem, które umożliwia wysyłanie żądań HTTP do serwerów i odbieranie odpowiedzi. Jest to bardzo przydatne narzędzie do komunikacji z zewnętrznymi serwisami API, pobierania danych ze stron internetowych czy wykonania żądań HTTP.

Przykładowy get w bibliotece requests

import requests # Sending GET request response = requests.get('https://api.example.com/data') # Checking response status code if response.status_code == 200: # Taking response data data = response.json() # Converting JSON response to Dictionary print(data) else: print('Error occured while reading data')

Biblioteka requests również pozwala nam wrzucać odpowiednie parametry w link.

params = {'key': 'value'} response = requests.get('https://api.example.com/data', params=params)

Wysyłanie żądań POST:

params = {'key': 'value'} response = requests.get('https://api.example.com/data', params=params)

Obsługa nagłówków:

headers = {'Authorization': 'Bearer token'} response = requests.get('https://api.example.com/data', headers=headers)

Biblioteka requests jest bardzo elastyczna i posiada wiele zaawansowanych funkcji, które mogą być użyteczne w różnych przypadkach. Szczególnie jak połączy się wielowątkowość wraz z biblioteką requests jesteśmy w stanie stworzyć naprawdę potężne narzędzie które będzie zgarniało tysiące danych z udostępnionego API.

Framework FastAPI

FastAPI to nowoczesny framework do tworzenia aplikacji API w języku Python. Jest on oparty na standardzie ASGI (Asynchronous Server Gateway Interface), co umożliwia obsługę asynchronicznych żądań HTTP. Jest to bardzo przydatne w przypadku wysokowydajnych aplikacji internetowych. FastAPI jest szybki, łatwy do nauki i obsługuje wiele zaawansowanych funkcji, takich jak generowanie interfejsu OpenAPI (Swagger) automatycznie na podstawie kodu.

Główne cechy FastAPI

Szybkość

Dzięki wykorzystaniu asynchroniczności, FastAPI osiąga wysoką wydajność w obsłudze wielu równoczesnych żądań.

Prostota i elegancja

FastAPI korzysta z deklaratywnej składni opartej na typach danych (znanej z Pydantic), co pozwala na wygodne definiowanie endpointów API.

Automatyczna dokumentacja

FastAPI generuje automatycznie interfejs OpenAPI (dawniej znany jako Swagger), co ułatwia zarządzanie i testowanie API.

Obsługa walidacji danych

Wykorzystując Pydantic, FastAPI automatycznie waliduje i konwertuje dane wejściowe i wyjściowe.

Obsługa middleware

Możliwość dodawania middleware dla dodatkowej logiki, np. autoryzacji, logowania itp.

Prosty Przykład jak uruchomić FastAPI

Aby zainstalować FastApi w terminalu wpisujemy polecenie:

pip install fastapi uvicorn

Następnie tworzymy plik main.py

from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"Hello": "World"} @app.get("/add") def add_numbers(x: int, y: int): return {"result": x + y}

Następnie uruchamiamy aplikację za pomocą uvicorn

uvicorn main:app --reload

Teraz uruchamiamy przeglądarkę i wchodzimy pod adres http://localhost:8000/docs. Dokumentacja API zostanie automatycznie wygenerowana za pomocą Swagger.

FastAPI oferuje znacznie więcej możliwości, takich jak obsługa różnych typów żądań (POST, PUT, DELETE), obsługa plików, tworzenie zależności, testowanie automatyczne API i wiele innych. Jest to świetne narzędzie do budowy nowoczesnych i wydajnych aplikacji webowych z wykorzystaniem Pythona.

Podsumowanie

W tym artykule opisałem tematy, które stanowią fundamenty, które każdy programista Pythona powinien znać, aby efektywnie tworzyć aplikacje, analizować dane, komunikować się z serwerami oraz testować i utrzymywać kod na wysokim poziomie jakości. Zachęcam do dalszego zgłębiania opisanych zagadnień. Jeden artykuł to zdecydowanie zbyt mało by omówić wszystkie funkcjonalności i zastosowania wspomnianych bibliotek, narzędzi czy frameworków.