MockIT logo on dark background.
Blog
Log In
MockIT logo on dark background.
MockIT logo.

Do you get the feeling that no one is reading your CV? Not getting invitations to recruitment interviews?

Test your technical skills and let MockIT help find you a job in IT!

Book an interview
Back

Python: Knowledge Essentials Everyone Should Have

Don't know where to start learning Python? Or maybe you want to find out if you've mastered the absolute basics of this language? In this article, I'll introduce you to the fundamentals of Python knowledge.

Mateusz Śliwiński

Python Developer - Ach Tech


May 7, 2024

Intro

Python is one of the most popular and versatile programming languages in the world. Its readable syntax and rich selection of libraries make it an excellent choice for both novice programmers and experienced developers.

In this article, I'll present the key concepts and functionalities of the Python language, and I'll try to review its basics. Before we dive into libraries and frameworks, I'd like to start with general coding principles.

Clean Code: SOLID, KISS, DRY

When writing code in Python (as well as in other programming languages), there are a set of principles that, when followed, help maintain readable, flexible, and easy-to-manage code. Among these principles, we distinguish SOLID, KISS, and DRY, which are fundamental to creating high-quality code.

SOLID

SOLID is a mnemonic describing five basic principles of object-oriented design:

  • S - Single Responsibility Principle: A class should have only one reason to change, meaning it should be responsible for a single specific functionality. This helps us avoid overly large and complex classes, making code management easier.

  • O - Open/Closed Principle: A class should be open for extension but closed for modification. Changes to the class should be made by adding new code, not by changing existing code.

  • L - Liskov Substitution Principle: Objects of a certain class should be replaceable by objects of their subclasses without changing the program's behavior. This helps maintain code consistency and flexibility.

  • I - Interface Segregation Principle: Interfaces should be specific to the client, avoiding the creation of general interfaces that require clients to implement unnecessary functions.

  • D - Dependency Inversion Principle: Modules should depend on abstractions, not on concrete implementations. This reduces dependencies between modules and facilitates code testing and development.

KISS

KISS, which stands for Keep It Simple, Stupid, is a principle encouraging the writing of simple, understandable code. Avoiding excessive complexity is crucial for maintaining code readability and maintainability. The more complex the code, the more difficult it is to understand and modify in the future. Using simple solutions, clear variable and function names, and avoiding excessive nesting of conditions and loops are key practices consistent with the KISS principle.

DRY

DRY, which stands for Don't Repeat Yourself, is a principle advocating for the avoidance of code repetition. Any repeated code fragments should be refactored into separate functions or classes, allowing for easier code management and maintenance. Repeated code is difficult to maintain because it requires changes in multiple places when modifications are necessary. By adhering to the DRY principle, code becomes more modular and flexible.

Syntax and Basic Data Types in Python

Python stands out for its readable and intuitive syntax, making it very user-friendly. Its dynamic nature allows for flexible manipulation of various data types without the need for prior type declaration.

Data Types in Python

Integers: These are whole numbers, such as 1, 100, -5, etc. Python allows for basic arithmetic operations on integers, such as addition +, subtraction -, multiplication *, division /, integer division //, and modulo operations %.

Floats: These include floating-point numbers, e.g. 3.14, 2.5, -0.5, etc. They are used to represent decimal numbers. Python supports mathematical operations on floating-point numbers such as addition, subtraction, multiplication, and division.

Strings: These are sequences of characters enclosed in quotation marks, e.g. “Hello, World!”. In Python, strings are immutable objects, meaning they cannot be modified after creation. Python offers many string operations, such as concatenation +, indexing [], slicing [start:stop:step], and many methods for text manipulation.

Lists: Lists are collections of elements that can be of different types. We create them using square brackets [], e.g. [1, 2, 3]. Lists in Python are mutable, meaning we can add, remove, and modify their elements.

Tuples: Tuples are similar to lists but are immutable collections of elements. We create them using parentheses (), e.g. (1, 2, 3). Tuples are more memory-efficient than lists because they occupy less space in memory, but they cannot be modified after creation.

Dictionaries: Dictionaries are collections of key-value pairs, where each key is unique. We create them using curly braces {}, e.g. {“key”: “value”, “name”: “Anna”}. Dictionaries allow fast access to values by key and are used to store data in the form of pairs.

Control Flow Statements, Loops, and Typing in Python

Python offers many control flow statements for controlling the flow of a program and loops for repeating code. These elements are crucial for building flexible and dynamic programs. Here are a few examples:

Control Flow Statements: if, elif, else are used to execute different code blocks depending on conditions being met.

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")

While Loop executes a block of code as long as the condition is true.

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

For Loop is used for iterating over sequences such as lists, tuples, dictionaries, etc.

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

Range() function generates a sequence of numbers that can be used in loops.

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

Enumerate() function is used for iterating over a sequence while returning indexes and elements simultaneously.

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

Break and Continue Statements allow controlling the behavior of loops. Break terminates the loop, while continue moves to the next iteration.

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 is a Dynamically Typed Language, meaning we don't have to declare a variable's type when creating it. However, starting from Python 3.6, type annotations were introduced, allowing for some indication of a variable's type. Nowadays, types in Python are often used, improving code readability and somewhat reducing susceptibility to errors.

Examples:

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)

Unit Testing in Python

Unit testing is a key element of the programming process, especially in the context of Test-Driven Development (TDD) and maintaining high-quality code. In Python, the unittest library or the pytest tool are often used for creating unit tests. With unit tests, we can verify the correctness of our functions, methods, and classes.

Unittest Library

The unittest library is part of Python's standard library and provides tools for creating and running unit tests. Here's an example of a simple unit test in 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()

Pytest tool

Pytest is a more modern testing tool in Python and offers a more transparent and convenient syntax than 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

With pytest, we don't need to inherit from a class or use special assertions, making test writing more clear and concise.

Concurrency

Concurrency in Python allows for the simultaneous execution of multiple parts of a program. This is useful especially in situations where we want to perform operations in parallel, such as data processing, handling network requests, or interacting with input-output devices. In Python, we can use threads with the threading module and processes with the multiprocessing module.

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")

It's worth noting that using threads (threading) in Python doesn't achieve full parallelism due to the Global Interpreter Lock (GIL), which restricts simultaneous execution of Python code in a single process. Therefore, if full parallelism is desired, consider using processes (multiprocessing). For those interested, I refer to another article describing this in more detail: link

Pandas Library

Pandas is a powerful library for data manipulation and analysis in Python. It offers efficient and easy-to-use data structures, such as DataFrame, which allows working with data similar to spreadsheets.

Data Structures in Pandas

DataFrame is the main data structure in Pandas, resembling a database table or spreadsheet. It consists of rows and columns, and each column can have a different data type. We can create a DataFrame from various data sources, such as CSV files, databases, other DataFrames, etc.

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 is a one-dimensional data structure in Pandas, representing a single column or row in a DataFrame. Each value in a Series is indexed, allowing for fast data operations.

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

Basic Data Manipulations using pandas

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 allows extensive manipulation, analysis, and basic as well as advanced plotting using other libraries like matplotlib and seaborn. Unfortunately, pandas and data visualization are too extensive topics for this modest article.

Requests Library

The requests library in Python is a tool that enables sending HTTP requests to servers and receiving responses. It is a very useful tool for communicating with external API services, retrieving data from websites, or making HTTP requests.

Sample GET request in the requests library

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')

The requests library also allows us to include appropriate parameters in the URL.

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

Sending POST requests:

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

Handling headers:

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

The requests library is very flexible and has many advanced features that can be useful in various cases. Especially when combined with concurrency and the requests library, we can create a truly powerful tool that fetches thousands of data from the provided API.

FastAPI Framework

FastAPI is a modern framework for building API applications in Python. It is based on the ASGI (Asynchronous Server Gateway Interface) standard, which allows handling asynchronous HTTP requests. This is very useful for high-performance web applications. FastAPI is fast, easy to learn, and supports many advanced features, such as automatically generating an OpenAPI interface (Swagger) based on code.

Key Features of FastAPI

Speed

By leveraging asynchronicity, FastAPI achieves high performance in handling multiple concurrent requests.

Simplicity and Elegance

FastAPI uses a declarative syntax based on data types (known from Pydantic), allowing for convenient definition of API endpoints.

Automatic Documentation

FastAPI automatically generates an OpenAPI interface (formerly known as Swagger), facilitating API management and testing.

Data Validation Handling

By using Pydantic, FastAPI automatically validates and converts input and output data.

Middleware Support

Capability to add middleware for additional logic, such as authentication, logging, etc.

Simple Example of Running FastAPI

To install FastApi in the terminal, type the command:

pip install fastapi uvicorn

Then create a file named 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}

Next, run the application using uvicorn

uvicorn main:app --reload

Now, open a browser and go to http://localhost:8000/docs. The API documentation will be automatically generated using Swagger.

FastAPI offers many more capabilities, such as handling different types of requests (POST, PUT, DELETE), file handling, creating dependencies, automatic API testing, and much more. It's a great tool for building modern and efficient web applications using Python.

Summary

In this article, I've described topics that form the fundamentals every Python programmer should know to effectively create applications, analyze data, communicate with servers, and test and maintain high-quality code. I encourage further exploration of the discussed topics. One article is definitely too little to cover all the functionalities and applications of the mentioned libraries, tools, or frameworks.


You may also be interested in

© 2024 MockIT