Tips and Tricks

# Python

## Problem solving - what have you tried?

When stuck, spend at least 30 minutes but no more than 60 trying to solve the problem before asking for help. Also, when you ask for help make sure you can answer the question, "What have you tried?”.

## Python's platform module

Python tip:

You can check the type of machine and platform that your program is running from with `platform.machine()` and `platform.platform()`, respectively.

An example👇

``````import platform

print(platform.machine())
# => x86_64

print(platform.platform())
# => macOS-10.15.6-x86_64-i386-64bit
``````

## Python - Iterate over multiple lists simultaneously with zip

Python tip:

You can use `zip` to iterate through multiple lists of equal length in a single loop.

👇

``````users = ["Jan", "Mike", "Marry", "Mike"]
user_visits = [10, 31, 10, 1]

for user, visits in zip(users, user_visits):
print(f"{user}: {visits}")

# Jan: 10
# Mike: 31
# Marry: 10
# Mike: 1
``````

## Count the number of occurrences of an element in a list in Python

Python tip:

You can count occurrences of an element in a list with `.count()`.

For example:

``````users = ["Jan", "Mike", "Marry", "Mike"]
print(users.count("Mike"))
# => 2
``````

## How do I concatenate two lists in Python?

Python tip:

You can use `+` to join two lists into a new list.

``````a = [10, 2]
b = [6, 3]

print(a + b)
# => [10, 2, 6, 3]
``````

## Python - create a list from a list repeated N times

Python tip:

You can create a new list with elements from the first list that are repeated as many times as you want by multiplying.

Fo example:

``````users = ["johndoe", "marry", "bob"]
print(3 * users)
# => ['johndoe', 'marry', 'bob', 'johndoe', 'marry', 'bob', 'johndoe', 'marry', 'bob']
``````

## Execute raw SQL queries in SQLAlchemy

Python SQLAlchemy tip:

You can use raw queries while still using SQLAlchemy models.

For example

``````user = session.query(Course).from_statement(
text("""SELECT * FROM courses where title=:title""")
).params(title="Scalable FastAPI Applications on AWS").all()
``````

## Python - sep parameter in print()

Python tip:

You can pass as many values to print to the `print()` function as you want. You can also specify a custom separator.

``````print("123", "456", "789")
# => 123 456 789

print("123", "456", "789", sep="-")
# => 123-456-789
``````

## How to flush output of print in Python?

Python tip:

You can set `flush=True` for the `print()` function to avoid buffering the output data and forcibly flush it:

``````print("I'm awesome", flush=True)
``````

## Python - find the last occurrence of an item in a list with rindex()

Python tip:

You can use `.rindex()` to find the highest index in a string where a substring is found.

👇

``````print("2021 was awesome. 2022 is going to be even more awesome.".rindex("awesome"))
# => 48
``````

## Python - string ljust() method

Python tip:

You can use `.ljust()` to create a left-justified string of given width.

``````string.ljust(width, fillchar)
``````

Padding is a space, " ", by default.

``````print("Mike".ljust(10, "*"))
# => Mike******
``````

## Python - string center() method

Python tip:

You can use `.center()` to create a centered string of given width.

``````string.center(width, fillchar)
``````

Padding on each side is a space, " ", by default.

``````print("Mike".center(10, "*"))
# => ***Mike***
``````

## Python - lower() vs. casefold() for string matching and converting to lowercase

Python tip:

Use `.casfolde()` instead of `.lower()` when you want to perform caseless operations when working with Unicode strings (for ASCII only strings they work the same) -- e.g., check if two strings are equal.

``````# In German ß == ss
print("straße".lower() == "strasse")
# False
print("straße".casefold() == "strasse")
# True
``````

## Python - remove a prefix from a string

Python tip (>=3.9):

You can use `.removeprefix()` to remove the prefix from a string.

For example, to remove a filename prefix:

``````invoice_filenames = ("INV_123.pdf", "INV_234.pdf", "INV_345.pdf")

for invoice_filename in invoice_filenames:
print(invoice_filename.removeprefix("INV_"))

# 123.pdf
# 234.pdf
# 345.pdf
``````

## Python - remove a suffix from a string

Python tip (>=3.9):

You can remove the suffix of a string with `.removesuffix()`.

For example, to remove the file type from a filename:

``````import pathlib

filename = "cv.pdf"

file_type_suffix = pathlib.Path(filename).suffix
print(filename.removesuffix(file_type_suffix))
# => cv
``````

## Contract Testing in Python

Python clean code tip:

Use contract testing when you want to verify the same behavior for different implementations.

Example:

``````import json
import pathlib
from dataclasses import dataclass

import pytest

@dataclass
class User:

class InMemoryUserRepository:
def __init__(self):
self._users = []

self._users.append(user)

class JSONUserRepository:
def __init__(self, file_path):

self._users.append(user)

class UserRepositoryContract:
@pytest.fixture
def repository(self):
raise NotImplementedError('Not Implemented Yet')

@pytest.fixture
return 'johndoe'

@pytest.fixture

class TestInMemoryUserRepository(UserRepositoryContract):
@pytest.fixture
def repository(self):
return InMemoryUserRepository()

class TestInJSONUserRepository(UserRepositoryContract):
@pytest.fixture
def repository(self, tmp_path):
users_file = tmp_path/"user.json"
users_file.write_text(json.dumps([]))
return JSONUserRepository(users_file)
``````

## Simplify Testing with Dependency Injection

Python clean code tip:

Use dependency injection to simplify testing

Example:

``````from dataclasses import dataclass

from fastapi import FastAPI

@dataclass
class User:

class StartUserOnboarding:
def __init__(self, user_repository):
self._user_repository = user_repository

class InMemoryUserRepository:
def __init__(self):
self._users = []

self._users.append(user)

class SQLiteUserRepository:
def __init__(self, config):
self._config = config

print(f"Running some SQL statements for insert DATABASE_PATH")

print(f"Running some SQL statements for fetch from {self._config.DATABASE_PATH}")

repository = InMemoryUserRepository()
use_case = StartUserOnboarding(user_repository=repository)

class ApplicationConfig:
DATABASE_PATH = "db"

app = FastAPI()

@app.post("/users/start-onboarding", status_code=202)

return "OK"
``````

## Python - use enums to group related constants

Python clean code tip:

Use enums to group related constants.

Why?

1. Autocomplete
2. Static type checking

Example:

``````from dataclasses import dataclass
from enum import Enum

ORDER_PLACED = "PLACED"
ORDER_CANCELED = "CANCELED"
ORDER_FULFILLED = "FULFILLED"

@dataclass
class Order:
status: str

order = Order(ORDER_PLACED)
print(order)

# better
class OrderStatus(str, Enum):
PLACED = "PLACED"
CANCELED = "CANCELED"
FULFILLED = "FULFILLED"

@dataclass
class Order:
status: OrderStatus

order = Order(OrderStatus.PLACED)
print(order)
``````

## Interfaces in Python with Protocol Classes

Python clean code tip:

Use `Protocol` to define the interface required by your function/method instead of using real objects. This way your function/method defines what it needs.

``````from typing import Protocol

class ApplicationConfig:
DEBUG = False
SECRET_KEY = "secret-key"
EMAIL_API_KEY = "api-key"

def send_email(config: ApplicationConfig):
print(f"Send email using API key: {config.EMAIL_API_KEY}")

# better
class EmailConfig(Protocol):
EMAIL_API_KEY: str

def send_email_(config: EmailConfig):
print(f"Send email using API key: {config.EMAIL_API_KEY}")
``````

## Python - Property-based Testing with Hypothesis

Python testing tip:

Rather than having to write different test cases for every argument you want to test, property-based testing generates a wide-range of random test data that's dependent on previous tests runs.

Use Hypothesis for this:

``````def increment(num: int) -> int:
return num + 1

# regular test
import pytest

@pytest.mark.parametrize(
'number, result',
[
(-2, -1),
(0, 1),
(3, 4),
(101234, 101235),
]
)
def test_increment(number, result):
assert increment(number) == result

# property-based test
from hypothesis import given
import hypothesis.strategies as st

@given(st.integers())
assert increment(num) == num - 1
``````

## Python - mock.create_autospec()

Python tip:

Use `mock.create_autospec()` to create a mock object with methods that have the same interface as the ones inside the original object.

Example:

``````from unittest import mock

import requests
from requests import Response

def get_my_ip():
response = requests.get(
'http://ipinfo.io/json'
)
return response.json()['ip']

def test_get_my_ip(monkeypatch):
my_ip = '123.123.123.123'
response = mock.create_autospec(Response)
response.json.return_value = {'ip': my_ip}

monkeypatch.setattr(
requests,
'get',
lambda *args, **kwargs: response
)

assert get_my_ip() == my_ip
``````

## Arrange-Act-Assert - testing pattern

Python clean test tip:

Structure your tests in an Arrange-Act-Assert way:

• Arrange - set-up logic
• Act - invokes the system you're about to test
• Assert - verifies that the action of the system under test behaves as expected

Example:

``````from dataclasses import dataclass

@dataclass
class User:
first_name: str
last_name: str

def full_name(self):
return f"{self.first_name} {self.last_name}"

def test_full_name_consists_of_first_name_and_last_name():
# arrange
first_name = "John"
last_name = "Doe"
user = User(first_name=first_name, last_name=last_name)

# act
full_name = user.full_name()

# assert
assert full_name == "John Doe"
``````

## Hide irrelevant test data

Python clean test tip:

You should hide irrelevant data for the test.

Such information just increases the cognitive mental load, resulting in bloated tests.

Example:

``````import uuid
from dataclasses import dataclass
from enum import Enum
from uuid import UUID
import pytest

class ProductCategory(str, Enum):
BOOK = "BOOK"
ELECTRONIC = "ELECTRONIC"

@dataclass
class Product:
id: UUID
price: int
name: str
category: ProductCategory

class ShoppingCart:
def __init__(self):
self._products = []

self._products.append(product)

def calculate_total_price(self):
return sum(product.price for product in self._products)

# BAD - category, id, and name are irrelevant for this test
def test_given_products_with_total_price_50_when_calculate_total_price_then_total_price_is_50_():
shopping_cart = ShoppingCart()
shopping_cart.add(Product(uuid.uuid4(), 10, "Mobile phone case", ProductCategory.ELECTRONIC))
shopping_cart.add(Product(uuid.uuid4(), 20, "Mobile phone charger", ProductCategory.ELECTRONIC))

assert shopping_cart.calculate_total_price() == 50

# GOOD
@pytest.fixture
def product_with_price():
def _product_with_price(price):
return Product(uuid.uuid4(), price, "Mobile phone case", ProductCategory.ELECTRONIC)
return _product_with_price

def test_given_products_with_total_price_50_when_calculate_total_price_then_total_price_is_50(product_with_price):
shopping_cart = ShoppingCart()

assert shopping_cart.calculate_total_price() == 50
``````

## Tests should use meaningful data

Python clean test tip:

Your tests should use meaningful data in order to provide examples of how to use your code.

Examples:

``````from dataclasses import dataclass

@dataclass
class Car:
manufacture: str
model: str
vin_number: str
top_speed: int

class InMemoryCarRepository:
def __init__(self):
self._cars = []

self._cars.append(car)

def get_by_vin_number(self, vin_number):
return next(car for car in self._cars if car.vin_number == vin_number)

# BAD - non-existing manufacture and model, VIN number not matching manufacture and model, impossible to reach top speed
car = Car(manufacture="AAAA", model="BBB+", vin_number="2FTJW36M6LCA90573", top_speed=1600)
repository = InMemoryCarRepository()

assert car == repository.get_by_vin_number(car.vin_number)

# GOOD
car = Car(manufacture="Jeep", model="Wrangler", vin_number="1J4FA29P4YP728937", top_speed=160)
repository = InMemoryCarRepository()

assert car == repository.get_by_vin_number(car.vin_number)
``````

## What should tests cover?

Python clean test tip:

For the most part, the tests you write should cover:

• all happy paths
• edge/corner/boundary cases
• negative test cases
• security and illegal issues

👇

``````import uuid
from dataclasses import dataclass
from typing import Optional

@dataclass
class User:

class InMemoryUserRepository:
def __init__(self):
self._users = []

def add(self, user: User) -> None:
self._users.append(user)

def search(self, query: Optional[str] = None) -> list[User]:
if query is None:
return self._users
else:
return [
user
for user in self._users
]

# happy path
def test_search_users_without_query_lists_all_users():
repository = InMemoryUserRepository()

assert repository.search() == [user1, user2]

# happy path
def test_search_users_with_email_part_lists_all_matching_users():
repository = InMemoryUserRepository()

assert repository.search("doe") == [user1, user3]

# edge test case
def test_search_users_with_empty_query_lists_all_users():
repository = InMemoryUserRepository()

assert repository.search("") == [user1, user2]

# negative test case
def test_search_users_with_random_query_lists_zero_users():
repository = InMemoryUserRepository()

assert repository.search(str(uuid.uuid4())) == []

# security test
def test_search_users_with_sql_injection_has_no_effect():
repository = InMemoryUserRepository()

repository.search("DELETE FROM USERS;")
assert repository.search() == [user1]
``````

## Tests should validate themselves regardless of whether the test execution passes or fails

Python clean test tip:

A test should validate itself whether the test execution is passed or failed.

The self-validating test can avoid the need to do an evaluation manually by us.

Example:

``````from dataclasses import dataclass

@dataclass
class User:
first_name: str
last_name: str

def fullname(self):
return f"{self.first_name} {self.last_name}"

def test_full_name_consists_of_first_name_and_last_name_manual():
first_name = "John"
last_name = "Doe"
user = User(first_name=first_name, last_name=last_name)

print(user.fullname())
assert input("Is result correct? (Y/n)") == "Y"

# GOOD
def test_full_name_consists_of_first_name_and_last_name():
first_name = "John"
last_name = "Doe"
full_name = "John Doe"
user = User(first_name=first_name, last_name=last_name)

assert user.fullname() == full_name
``````

## Tests should be independent

Python clean test tip:

A test should not depend on the state of any other tests or external services.

👇

``````from dataclasses import dataclass

import pytest

@dataclass
class User:

class InMemoryUserRepository:
def __init__(self):
self._users = []

def add(self, user: User) -> None:
self._users.append(user)

return next(
user
for user in self._users
)

# BAD - depends on persistence layer having user record at test time
repository = InMemoryUserRepository()

@pytest.fixture(scope="module")
def repository():
return InMemoryUserRepository()

# GOOD - makes sure it has all the needed data
repository = InMemoryUserRepository()

``````

## Tests should be repeatable and deterministic

Python clean test tip:

Your tests should be repeatable in any environment.

They should be deterministic, always result in the same tests succeeding.

Example:

``````import random

LOTTO_COMBINATION_LENGTH = 5
MIN_LOTTO_NUMBER = 1
MAX_LOTTO_NUMBER = 42

def lotto_combination():
combination = []
while len(combination) < LOTTO_COMBINATION_LENGTH:
number = random.randint(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER)
if number not in combination:
combination.append(number)

return combination

def test_lotto_combination():
assert lotto_combination() == [10, 33, 5, 7, 2]

# GOOD
def test_all_numbers_are_between_min_max_range():
assert all(MIN_LOTTO_NUMBER <= number <= MAX_LOTTO_NUMBER for number in lotto_combination())

def test_length_of_lotto_combination_has_expected_number_of_elements():
assert len(lotto_combination()) == LOTTO_COMBINATION_LENGTH
``````

## Shorten your feedback loops by increasing the speed of your test suite

Python clean test tip:

Your tests should be fast. The faster the tests the faster the feedback loop.

Consider using mocks or test doubles when dealing with third-party APIs and other slow things.

Example:

``````import time

def fetch_articles():
print("I'm fetching articles from slow API")
time.sleep(10)
return {"articles": [{"title": "Facebook is Meta now."}]}

def test_fetch_articles_slow():
assert fetch_articles() == {"articles": [{"title": "Facebook is Meta now."}]}

# GOOD
def test_fetch_articles_fast(monkeypatch):
monkeypatch.setattr(time, "sleep", lambda timeout: None)
assert fetch_articles() == {"articles": [{"title": "Facebook is Meta now."}]}
``````

## Tests should be useful

Python clean test tip:

Tests should protect you against regressions. They shouldn't just increase your code coverage percentage. Make sure they are useful! Don't just write tests for the sake of writing tests. They are code too, so they need to be maintained.

Example:

``````from dataclasses import dataclass

@dataclass
class User:
first_name: str
last_name: str

def fullname(self):
return f"{self.first_name} {self.last_name}"

def test_full_name():
user = User(first_name="John", last_name="Doe")
assert user.fullname() is not None

# GOOD
def test_full_name_consists_of_first_name_and_last_name():
first_name = "John"
last_name = "Doe"
full_name = "John Doe"
user = User(first_name=first_name, last_name=last_name)

assert user.fullname() == full_name
``````

## Test behavior, not implementation

Python clean test tip:

Tests should check the behavior rather than the underlying implementation details.

Such tests are easier to understand and maintain. They're also more resistant to refactoring (helps prevent false negatives).

👇

``````from dataclasses import dataclass

@dataclass
class User:

class InMemoryUserRepository:
def __init__(self):
self._users = []

self._users.append(user)

repository = InMemoryUserRepository()

assert user in repository._users

repository = InMemoryUserRepository()
repository._users = [user]

assert user_from_repository == user

# GOOD
repository = InMemoryUserRepository()

``````

## Docker - Cache Python Packages to the Docker Host

Docker best practice:

Cache Python packages to the Docker host by mounting a volume or using BuildKit.

Example Dockerfile:

``````# Mount volume option
-v \$HOME/.cache/pip-docker/:/root/.cache/pip

# BuildKit
# syntax = docker/dockerfile:1.2

...

COPY requirements.txt .

RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt

...
``````

## Serving files with Python's HTTP server

Python tip:

When you need to just serve your static files inside a folder you can do that with Python's HTTP server:

``````\$ cat index.html
<html>
<h1>Website Prototype</h1>
<h2>List of Users:</h2>
<ul>
<li>Patrick</li>
<li>Jan</li>
</ul>
</html>

\$ python3 -m http.server
Serving HTTP on :: port 8000 (http://[::]:8000/) ...
``````

## Python docstrings examples

Python Clean Code Tip:

Use docstrings to document usage of your modules, classes, and functions.

``````"""
The temperature module: Manipulate your temperature easily

Easily calculate daily average temperature
"""

from typing import List

class HighTemperature:
"""Class representing very high temperatures"""

def __init__(self, value: float):
"""
:param value: value of temperature
"""

self.value = value

def daily_average(temperatures: List[float]) -> float:
"""
Get average daily temperature

Calculate average temperature from multiple measurements

:param temperatures: list of temperatures
:return: average temperature
"""

return sum(temperatures) / len(temperatures)
``````

## Do not store secrets in plaintext in code

Python Clean Code Tip:

Avoid storing things like secret keys, passwords, connection strings, and API keys inside your code. Instead, use a secrets management solution like AWS Secrets Manager or Vault.

``````# bad

class ProductionConfig:
DEBUG = False
TESTING = False
APP_ENVIRONMENT = "production"
SQLALCHEMY_DATABASE_URI = (
"postgresql://my_user:[email protected]_server:5432/my_db"
)

# better

import boto3

class ProductionConfig:
DEBUG = False
TESTING = False
APP_ENVIRONMENT = "production"
_SQLALCHEMY_DATABASE_URI = None

@property
def SQLALCHEMY_DATABASE_URI(self):
if self._SQLALCHEMY_DATABASE_URI is None:
self._SQLALCHEMY_DATABASE_URI = boto3.client(
"secretsmanager"
).get_secret_value(SecretId=f"db-connection-string-{self.APP_ENVIRONMENT}")[
"SecretString"
]

return self._SQLALCHEMY_DATABASE_URI
``````

If a secrets management tool is overkill for your project, store secrets in environment variables. Never store them in plaintext in your code.

## Python - use real objects over primitive types

Python Clean Code Tip:

Favor real objects over primitive types such as dictionaries.

Why?

1. It's easier to type `user.name` rather than `user['name']`
2. You'll get help from your IDE
3. You can actually check your code before it runs with mypy
4. It makes your code more clear
``````# bad
user = {"first_name": "John", "last_name": "Doe"}
full_name = f"{user['first_name']} {user['last_name']}"
print(full_name)
# => John Doe

# better
class User:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name

def full_name(self):
return f"{self.first_name} {self.last_name}"

user = User(first_name="John", last_name="Doe")
print(user.full_name())
# => John Doe
``````

## Python - find minimum value using special comparator

Python Clean Code Tip:

Use `min` to find an element with minimal value inside an iterable. You can provide a custom function as a `key` argument to serve as a key for the min comparison.

``````temperatures = [22.3, 28.7, 15.3, 18.2]

# without min
min_temperature = 10000

for temperature in temperatures:
if temperature < min_temperature:
min_temperature = temperature

print(min_temperature)
# => 15.3

# with min
min_temperature = min(temperatures)
print(min_temperature)
# => 15.3

# using key
users = [
]
shortest_user = min(users, key=lambda user: user["height"])
print(shortest_user)
``````

## Be consistent with the order of your parameters

Python Clean Code Tip:

Be consistent with order of parameters for similar functions/methods. Don't confuse your readers.

``````# bad
def give_first_dose_of_vaccine(person, vaccine):
print(f"Give first dose of {vaccine} to {person}.")

def give_second_dose_of_vaccine(vaccine, person):
print(f"Give second dose of {vaccine} to {person}.")

give_first_dose_of_vaccine("john", "pfizer")
# Give first dose of pfizer to john.
give_second_dose_of_vaccine("jane", "pfizer")
# Give second dose of jane to pfizer.

# good
def give_first_dose_of_vaccine(person, vaccine):
print(f"Give first dose of {vaccine} to {person}.")

def give_second_dose_of_vaccine(person, vaccine):
print(f"Give second dose of {vaccine} to {person}.")

give_first_dose_of_vaccine("john", "pfizer")
# Give first dose of pfizer to john.
give_second_dose_of_vaccine("jane", "pfizer")
# Give second dose of pfizer to jane.
``````

## Python - High-precision calculations with Decimal

Python Clean Code Tip:

Avoid using floats when you need precise results. Use `Decimal` instead.

e.g. prices

👇

``````from dataclasses import dataclass

from decimal import Decimal

@dataclass
class Product:
price: float

print(Product(price=0.1 + 0.2))
# => Product(price=0.30000000000000004)

# good
@dataclass
class Product:
price: Decimal

print(Product(price=Decimal("0.1") + Decimal("0.2")))
# => Product(price=Decimal('0.3'))
``````

## Python - OOP tip: set attributes in the constructor

Python Clean Code Tip:

Avoid setting attributes of your objects outside of the constructor. Instead, implement methods that map to real-world concepts.

Why?

To ensure attributes exist and are easily discoverable.

👇

``````from dataclasses import dataclass
from enum import Enum
from uuid import UUID

class OrderStatus(str, Enum):
PLACED = "PLACED"
CANCELED = "CANCELED"
FULFILLED = "FULFILLED"

@dataclass
class Order:
status: OrderStatus

class CancelOrder:
def __init__(self, order_repository):
self.order_repository = order_repository

def execute(self, order_id: UUID):
order = self.order_repository.get_by_id(order_id)
order.status = OrderStatus.CANCELED
self.order_repository.save(order)

# better
class Order:
def __init__(self, status: OrderStatus):
self._status = status

def cancel(self):
self._status = OrderStatus.CANCELED

class CancelOrder:
def __init__(self, order_repository):
self.order_repository = order_repository

def execute(self, order_id: UUID):
order = self.order_repository.get_by_id(order_id)
order.cancel()
self.order_repository.save(order)
``````

## Python - OOP tip: avoid using too many attributes on a single object

Python Clean Code Tip:

Avoid using too many attributes on a single object. Try to cluster them to improve cohesion, reduce coupling, and improve readability

👇

``````import datetime
from dataclasses import dataclass

@dataclass
class ExcelSheet:
file_name: str
file_encoding: str
document_owner: str
creation_time: datetime.datetime
update_time: datetime.datetime

# good
@dataclass
class FileProperties:
name: str
encoding: str

@dataclass
class SecurityProperties:
owner: str

@dataclass
class DocumentDating:
creation_time: datetime.datetime
update_time: datetime.datetime

@dataclass
class ExcelSheet:
file_properties: FileProperties
security_properties: SecurityProperties
document_dating: DocumentDating
``````

## Do not use bare except

Python Clean Code Tip:

Avoid empty except blocks -> try-except-pass.

👇

``````# bad
import logging

def send_email():
print("Sending email")
raise ConnectionError("Oops")

try:
send_email()
except:  # AVOID THIS
pass

# better
logger = logging.getLogger(__name__)
try:
send_email()
except ConnectionError as exc:
logger.error(f"Cannot send email {exc}")
``````

## Python - use all uppercase for constants

Python Clean Code Tip:

Use upper case names for constants

👇

``````from typing import Final

MAX_NUMBER_OF_RETRIES: Final = 666

class Driver:
MAX_HEIGHT: Final = 190
``````

## Python type annotation specificity

Python tip:

Specify the most general type for inputs and the most specific type for outputs.

For example:

``````from typing import List

def sum_of_elements(elements: List[int]) -> int:
sum_el = 0

for element in elements:
sum_el += element

return sum_el

print(sum_of_elements((9, 9)))

"""
\$ mypy example.py

example.py:13: error: Argument 1 to "sum_of_elements" has
incompatible type "Tuple[int, int]"; expected "List[int]"
Found 1 error in 1 file (checked 1 source file)
"""

from typing import Iterable

def sum_of_elements(elements: Iterable[int]) -> int:
sum_el = 0

for element in elements:
sum_el += element

return sum_el

print(sum_of_elements((9, 9)))

"""
\$ mypy example.py

Success: no issues found in 1 source file
"""
``````

## Python: Check if an iterable contains a specific element

Python Clean Code Tip:

Use `in` to check whether an iterable contains a specific element.

👇

``````lucky_numbers = [1, 23, 13, 1234]
BEST_NUMBER = 13

# without in
best_number_is_lucky_number = False

for number in lucky_numbers:
if number == BEST_NUMBER:
best_number_is_lucky_number = True

print(best_number_is_lucky_number)
# => True

# with in
best_number_is_lucky_number = BEST_NUMBER in lucky_numbers
print(best_number_is_lucky_number)
# => True
``````

## Python type hints - descriptive variable names

Python Clean Code Tip:

Avoid using the variable/parameter type inside your variable/parameter name. Use type hints instead.

``````# BAD: user_list

# GOOD: users: list[User]
``````

Full example👇

``````from dataclasses import dataclass

@dataclass
class User:

def print_users(user_list):
for user in user_list:

# => johndoe

# good
def print_users(users: list[User]):
for user in users:

# => johndoe
``````

## Python - avoid HTTP status code magic numbers with http.HTTPStatus()

Python Clean Code Tip:

Use `HTTPStatus` from `http` (it's inside the standard library) to avoid "magic" numbers for statuses inside your code.

Example:

``````from http import HTTPStatus

from fastapi import FastAPI

app = FastAPI()

@app.get("/old", status_code=200)
async def old():
return {"message": "Hello World"}

@app.get("/", status_code=HTTPStatus.OK)
async def home():
return {"message": "Hello World"}
``````

## Python - splitting a module into multiple files

Python Clean Code Tip:

When your module becomes too big you can restructure it to a package while keeping all the imports from the module as they were.

👇

``````# BEFORE

# models.py
class Order:
pass

class Shipment:
pass

# └── models.py

# AFTER

# change to package
# models/__init__.py
from .order import Order
from .shipment import Shipment

__all__ = ["Order", "Shipment"]

# models/order.py
class Order:
pass

# models/shipment.py
class Shipment:
pass

# └── models
#     ├── __init__.py
#     ├── order.py
#     └── shipment.py

# imports from module/package can stay the same
from models import Order, Shipment
``````

## Design by contract in Python - preconditions

Python Clean Code Tip:

Use preconditions to ensure the integrity of your objects.

For example:

``````class Date:
def __init__(self, day, month, year):
self.day = day
self.month = month
self.year = year

startDate = Date(3, 11, 2020)
# OK

startDate = Date(31, 13, 2020)
# this one should fail since there are only 12 months

class Date:
LAST_MONTH = 12
LAST_DAY = 31

def __init__(self, day, month, year):
if month > self.LAST_MONTH:
raise Exception(f"Month cannot be greater than {self.LAST_MONTH}")
if day > self.LAST_DAY:
raise Exception(f"Day cannot be greater than {self.LAST_DAY}")
self.day = day
self.month = month
self.year = year

startDate = Date(3, 11, 2020)
# OK

startDate = Date(31, 13, 2020)
# this one fails

# DISCLAIMER: production ready validation should be more complex since not all months have 31 days
``````

Python Clean Code Tip:

Use operator overloading to enable usage of operators such as `+`, `-`, `/`, `*`, ... on your instances.

👇

``````from dataclasses import dataclass

@dataclass
class TestDrivenIOCoin:
value: float

if not isinstance(other, TestDrivenIOCoin):
return NotImplemented

return TestDrivenIOCoin(value=self.value + other.value)

print(my_coins)
# TestDrivenIOCoin(value=477.01)

@dataclass
class TestDrivenIOCoin:
value: float

if not isinstance(other, TestDrivenIOCoin):
return NotImplemented

return TestDrivenIOCoin(value=self.value + other.value)

my_coins = TestDrivenIOCoin(value=120) + TestDrivenIOCoin(value=357.01)
print(my_coins)
# TestDrivenIOCoin(value=477.01)
``````

## Chaining comparison operators in Python

Python Clean Code Tip:

Use chained comparison when you need to check whether some variable is between MIN and MAX values.

👇

``````from dataclasses import dataclass

@dataclass
class SurfBoard:
width: float
length: float

MINIMAL_LENGTH = 201.3
MAXIMAL_LENGTH = 278.5

# without chained comparison
def board_is_pwa_compliant(surf_board: SurfBoard):
return surf_board.length > MINIMAL_LENGTH and surf_board.length < MAXIMAL_LENGTH

surf_board = SurfBoard(width=75.3, length=202.7)
print(board_is_pwa_compliant(surf_board))
# True

# with chained comparison
def board_is_pwa_compliant(surf_board: SurfBoard):
return MINIMAL_LENGTH < surf_board.length < MAXIMAL_LENGTH

print(board_is_pwa_compliant(surf_board))
# True

# don't abuse it like this: a <= b < c > d
``````

## __all__ in Python

Python Clean Code Tip:

Use `__all__` to define exported members of your package.

Hint: IDEs will do a much better job at importing and autocomplete.

``````from .my_module import my_function

__all__ = ["my_function"]
``````

## Python - built-in sum function vs. for loop

Python Clean Code Tip:

Use `sum` to sum the values of all elements inside an iterable instead of a `for` loop.

Why?

1. Don't re-invent the wheel!
2. `sum` is much faster

👇

``````transactions = [10.0, -5.21, 101.32, 1.11, -0.38]

# without sum
balance = 0

for transaction in transactions:
balance += transaction

# with sum
balance = sum(transactions)
``````

## Python - Reduce Boilerplate Code with Dataclasses

Python Clean Code Tip:

Use dataclasses when only storing attributes inside your class instances to reduce the amount of boilerplate code.

For example:

``````# without dataclass
def __init__(self, street, city, zip_code):
self.street = street
self.city = city
self.zip_code = zip_code

def __repr__(self):
return (
)

def __hash__(self) -> int:
return hash((self.street, self.city, self.zip_code))

def __eq__(self, other) -> bool:
return NotImplemented
return (self.street, self.city, self.zip_code) == (
other.street,
other.city,
other.zip_code,
)

# with dataclass
from dataclasses import dataclass

@dataclass(unsafe_hash=True)
street: str
city: str
zip_code: str
``````

## Check for code quality issues inside your CI/CD pipelines

Python Clean Code Tip:

Use:

1. flake8 - style guide enforcer
2. black - code formatting
3. isort - optimize imports
4. bandit - check for security vulnerabilities
5. safety - check for security vulnerabilities of dependencies

Github Actions Example 👇

``````name: Check code quality
on: [push]

jobs:
code-quality:
strategy:
fail-fast: false
matrix:
python-version: [3.9]
poetry-version: [1.1.8]
os: [ubuntu-latest]
runs-on: \${{ matrix.os }}
steps:
- uses: actions/[email protected]
- uses:
with:   actions/[email protected]
python-version:   \${{ matrix. python-version }}
- name: Run image
uses: abatilo/[email protected]
with:
poetry-version: \${{ matrix. poetry-version }}
- name: Install dependencies
run: poetry install
- name: Run black
run: poetry run black . --check
- name: Run isort
run: poetry run isort . --check-only --profile black
- name: Run flake8
run: poetry run flake8 .
- name: Run bandit
run: poetry run bandit .
- name: Run saftey
run: poetry run safety check
``````

It's a good idea to couple this with pre-commit hooks:

• pre-commit - format code with black and isort
• CI pipeline - run black and isort with check flags to ensure that code has been properly formatted

In other words, you shouldn't actually format any code in the CI pipeline. You just want to verify that formatting happened via pre-commit.

## Don't use flags in functions

Python Clean Code Tip:

Don't use flags in functions.

Flags are variables passed to functions, which the function uses to determine its behavior. This pattern should be avoided since functions should only perform a single task. If you find yourself doing this, split your function into smaller functions.

👇

``````text = "This is a cool blog post"

def transform(text, uppercase):
if uppercase:
return text.upper()
else:
return text.lower()

# This is good
def uppercase(text):
return text.upper()

def lowercase(text):
return text.lower()
``````

## Python Clean Code: Keep your function arguments at a minimum

Python Clean Code Tip:

Keep your arguments at a minimum.

Ideally, your functions should only have one to two arguments. If you need to provide more arguments to the function, you can create a config object which you pass to the function or split it into multiple functions.

Example:

``````# This is bad
def render_blog_post(title, author, created_timestamp, updated_timestamp, content):
# ...

render_blog_post("Clean code", "Nik Tomazic", 1622148362, 1622148362, "...")

# This is good
class BlogPost:
def __init__(self, title, author, created_timestamp, updated_timestamp, content):
self.title = title
self.author = author
self.created_timestamp = created_timestamp
self.updated_timestamp = updated_timestamp
self.content = content

blog_post1 = BlogPost("Clean code", "Nik Tomazic", 1622148362, 1622148362, "...")

def render_blog_post(blog_post):
# ...

render_blog_post(blog_post1)
``````

## Functions should only perform a single task

Python Clean Code Tip:

Functions should only perform a single task

Hint: If your function contains the keyword 'and' you can probably split it into two functions.

``````# This is bad
def fetch_and_display_personnel():
data = # ...

for person in data:
print(person)

# This is good
def fetch_personnel():
return # ...

def display_personnel(data):
for person in data:
print(person)
``````

## Clean code tip - Don't add unnecessary context

Python Clean Code Tip:

Do not add unnecessary data to variable names, especially if you're working with classes.

``````# This is bad
class Person:
def __init__(self, person_first_name, person_last_name, person_age):
self.person_first_name = person_first_name
self.person_last_name = person_last_name
self.person_age = person_age

# This is good
class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
``````

We're already inside the `Person` class, so there's no need to add a `person_` prefix to every class variable.

## Clean code tip - Don't use magic numbers

Python Clean Code Tip:

Don't use "magic numbers".

Magic numbers are strange numbers that appear in code, which do not have a clear meaning.

👇

``````import random

def roll():
return random.randint(0, 36)  # what is 36 supposed to represent?

# This is good
ROULETTE_POCKET_COUNT = 36

def roll():
return random.randint(0, ROULETTE_POCKET_COUNT)
``````

Instead of using magic numbers, extract them into a meaningful variable.

## Clean code tip - Avoid using ambiguous abbreviations

Python clean code tip:

Avoid using ambiguous abbreviations

Don't try to come up with your own abbreviations. It's better for a variable to have a longer name than a confusing name.

👇

``````# This is bad
fna = 'Bob'
cre_tmstp = 1621535852

# This is good
first_name = 'Bob'
creation_timestamp = 1621535852
``````

## Queryset.explain() in Django

Django tip:

If you want to know how the database would execute a given query, you can use `explain()`.

Knowing this can be helpful when you're trying to improve the performance of slow queries.

``````>>> print(Payment.objects.filter(created_at__gt=datetime.date(2021, 1, 1)).explain())

Seq Scan on payments_payment  (cost=0.00..14.25 rows=113 width=212)
Filter: (created_at > '2021-01-01 00:00:00+00'::timestamp with time zone)
``````

## Check if a file is a symlink in Python

Python tip:

You can use pathlib's `is_symlink()` to check whether a path is a symlink.

👇

``````import pathlib

path = pathlib.Path("/usr/bin/python")

# => True
``````

## Python - slice a generator object

Python tip:

You can use itertools.islice to use only part of a generator.

👇

``````from itertools import cycle, islice

chord_sequence = cycle(["G", "D", "e", "C"])

song_chords = [chord for chord in islice(chord_sequence, 16)]

print(song_chords)
"""
['G', 'D', 'e', 'C', 'G', 'D', 'e', 'C', 'G', 'D', 'e', 'C', 'G', 'D', 'e', 'C']
"""
``````

## Positional-only arguments in Python

Did you know?

You can force a user to call a function with positional arguments only using `/`.

Example:

``````def full_name(user, /):
return f"{user['first_name']} {user['last_name']}"

print(full_name({"first_name": "Jan", "last_name": "Giamcomelli"}))
# => Jan Giamcomelli

print(full_name(user={"first_name": "Jan", "last_name": "Giamcomelli"}))
# => TypeError: full_name() got some positional-only arguments passed as keyword arguments: 'user'
``````

Why?

Makes refactoring easier. You can change the name of your parameters without worrying about it breaking any code that uses the function.

## all() in Python

Python tip:

You can use `all` to check whether all values inside an iterable are truthy.

👇

``````numbers = [10, 99, 321, 3]
print(all(number > 0 for number in numbers))
# => True

print(all([]))
# => True

numbers = [-1, 2, 5, 10, 0]
print(all(number > 0 for number in numbers))
# => False
``````

## any() in Python

Python tip:

You can use `any` to check whether any element inside an iterable has a truthy value.

An example👇

``````platforms = ["Facebook", "Twitter", "Instagram"]

print(any(platform == "Twitter" for platform in platforms))
# => True

print(any([]))
# => False

print(any([2, 3, 4]))
# => True
``````

## Parse datetime strings in Python with dateutil

Did you know?

You can use `dateutil.parser` to parse all sorts of different date and datetime string formats.

https://pypi.org/project/python-dateutil/

An example👇

``````from dateutil.parser import parse

print(parse("2012-01-19 17:21:00"))
# => 2012-01-19 17:21:00

print(parse("1st Jan, 2019"))
# => 2019-01-01 00:00:00

print(parse("23.11.2020"))
# => 2020-11-23 00:00:00
``````

Unfortunately, this is quite slow:

``````from dateutil.parser import parse
%timeit parse(datetime_txt).date()
54.3 µs ± 450 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

import datetime
%timeit datetime.datetime.strptime(datetime_txt, "%H:%M %d-%m-%Y").date()
7.44 µs ± 240 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
``````

## Python @classmethod

Python tip:

You can use `@classmethod` to create class methods.

For example, you can create a class method that loads events from a JSON string message👇

``````import datetime
import json

class UserRegistered:
self.event_time = event_time

@classmethod
def from_event_message(cls, message):

return cls(
event_time=datetime.datetime.fromisoformat(message["event_time"]),
)

message = '{"username": "johndoe", "event_time": "2021-04-26T20:00:00"}'
event = UserRegistered.from_event_message(message)
# => johndoe 2021-04-26 20:00:00
``````

## 5 awesome tools for writing Python tests

5 awesome tools for writing Python tests:

1. pytest - go-to testing framework for testing Python code
2. unittest.mock - built-in mocking library
3. coverage.py - measuring code coverage (use pytest-cov plugin when using pytest)
4. mutmut - improve the quality of your test suite with mutation testing
5. hypothesis - property-based testing library

You can learn about all of these tools here.

## How do you parameterize a test in pytest?

Pytest tip:

You can parametrize your tests with `@pytest.mark.parametrize`.

Example:

``````import pytest

def is_palindrome(text):
return text == "".join(reversed(text))

@pytest.mark.parametrize(
"text, is_pal",
[
("kayak", True),
("racecar", True),
("bmw", False),
("songs", False),
],
)
def test_is_palindrome(text, is_pal):
assert is_palindrome(text) == is_pal
``````

## pydantic typecasting

Did you know?

Pydantic uses typecasting.

That means that it tries to convert provided values to their expected types if it's possible.

Examples👇

``````import datetime

from pydantic import BaseModel

class Location(BaseModel):
city: str
country: str

class Measurement(BaseModel):
temperature: float
measured_at: datetime.datetime
location: Location

print(
Measurement(
temperature=10,
measured_at="2021-04-16T21:15:03.000012",
location={"city": "London", "country": "UK"},
)
)

"""
temperature=10.0 measured_at=datetime.datetime(2021, 4, 16, 21, 15, 3, 12) location=Location(city='London', country='UK')
"""

print(
Measurement(
temperature=10.0,
measured_at="2021-04-16T21:15:03",
location=Location(city="London", country="UK"),
)
)

"""
temperature=10.0 measured_at=datetime.datetime(2021, 4, 16, 21, 15, 3) location=Location(city='London', country='UK')
"""

print(
Measurement(
temperature=10,
measured_at="16186000528",
location=[("city", "London"), ("country", "UK")],
)
)

"""
temperature=10.0 measured_at=datetime.datetime(2021, 4, 16, 19, 15, 28, tzinfo=datetime.timezone.utc) location=Location(city='London', country='UK')
"""
``````

## Public, protected, and private methods ​in Python

Did you know?

Python doesn't have public, protected, or private methods.

Instead, use the following naming conventions:

1. `method_name` -> public
2. `_method_name` -> protected
3. `__method_name` -> private

The same goes for attributes.

An example👇

``````class User:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.__age = age

def age(self):
return "You shouldn't ask that. It's a private information."

return self.__age >= 21

def can_drink_alcohol(self):
``````

## Python - checking whether a list contains duplicates

Did you know?

As long as the elements are hashable, you can check whether a list contains duplicated values by converting it to a set and comparing the lengths of the list and set.

Example:

``````names = ["Jan", "John", "Marry", "Daisy", "Jan"]

names_has_duplicates = len(set(names)) < len(names)
print(names_has_duplicates)
# => True
``````

## Black compatibility with flake8

Python tip:

When using black and flake8 together set the max line length for flake8 to 88 to match black's default value.

Minimal flake8 black compatible config:

``````[flake8]
max-line-length = 88
extend-ignore = E203
``````

## Python 3.10 - parenthesized context managers

Did you know?

Python 3.10 comes with support for continuation across multiple lines when using context managers.

An example👇

``````import unittest
from unittest import mock

from my_module import my_function

# Python < 3.10

class Test(unittest.TestCase):
def test(self):
with mock.patch("my_module.secrets.token_urlsafe") as a, mock.patch("my_module.string.capwords") as b, mock.patch("my_module.collections.defaultdict") as c:
my_function()
a.assert_called()
b.assert_called()
c.assert_called()

def test_same(self):
with mock.patch("my_module.secrets.token_urlsafe") as a:
with mock.patch("my_module.string.capwords") as b:
with mock.patch("my_module.collections.defaultdict") as c:
my_function()
a.assert_called()
b.assert_called()
c.assert_called()

# Python >= 3.10

class Test(unittest.TestCase):
def test(self):
with (
mock.patch("my_module.secrets.token_urlsafe") as a,
mock.patch("my_module.string.capwords") as b,
mock.patch("my_module.collections.defaultdictl") as c,
):
my_function()
a.assert_called()
b.assert_called()
c.assert_called()
``````

## Difference between re.search and re.match in Python?

`re.match()` searches for matches from the beginning of a string while `re.search()` searches for matches anywhere in the string.

Example:

``````import re

claim = 'People love Python.'

print(re.search(r'Python', claim).group())
# => Python

print(re.match(r'Python', claim))
# => None

print(re.search(r'People', claim).group())
# => People

print(re.match(r'People', claim).group())
# => People
``````

## Extract contents of a PDF file with pikepdf in Python

Python tip:

You can use `pikepdf` to extract a subset pf pages from an original PDF and create a new file containing only the extracted pages.

For example,👇 save pages 2 and 3 from the original PDF

``````import pathlib

from pikepdf import Pdf

start = 2
stop = 3
filepath = pathlib.Path("file.pdf")

pdf = Pdf.open(filepath)
new_pdf = Pdf.new()

new_pdf.pages.extend(pdf.pages[start - 1 : stop])

new_pdf.save("new.pdf", compress_streams=False)
``````

## Using pathlib's read_bytes method in Python

Python tip:

You can use `read_bytes()` (which handles the opening and closing of the file) from `pathlib`'s `Path` to read bytes from a file instead of using `with open()`.

Example:

``````# pathlib
import pathlib

# with open
with open("file.pdf", "rb") as file:
``````

## Python - provide a function as an argument to another function to improve its testability

Python tip:

You can provide a function as an argument to another function to improve its testability.

For example, you can provide input as a function in a real application and mock the function in test

``````def get_user_expense(get_input):
raw_input = get_input(
"Enter your expense (date;description;amount) (e.g. 2021-03-21;Gasoline;75.43): "
)
date, description, amount = raw_input.split(";")
return date, description, amount

# test
def test_get_user_expense():
assert get_user_expense(lambda *args: "2021-03-21;Gasoline;75.43") == (
"2021-03-21",
"Gasoline",
"75.43",
)

# real application
def main():
date, description, amount = get_user_expense(input)
print(date, description, amount)
``````

## Poetry - generate requirements.txt without hashes

Poetry tip:

When using Docker, you can export dependencies to a requirements.txt file without hashes to decrease time to resolve dependencies.

👇

``````poetry export --without-hashes --format=requirements.txt > requirements.txt
``````

## Caching poetry install for CI

Poetry tip:

When running poetry inside a CI pipeline, set `virtualenvs.in-project` config to `true`.

That way the virtual environment will be created inside the current folder -> you can cache it.

Gitlab example👇

``````stages:
- test

cache:
key: virtualenv
paths:
- .venv/

tests:
stage: test
image: python3.8-slim
before_script:
- poetry config virtualenvs.in-project true
- poetry install
script:
- poetry run python -m pytest tests
``````

## Black compatibility with isort

Python tip:

When using black and isort on the same project, use isort's profile black to avoid code formatting conflicts

👇

``````\$ isort . --profile black
``````

## Python dictionary clear() method

Python tip:

You can use `.clear` to clear a dictionary.

Is it the same as `foo = {}` ?

No.

1. `foo = {}` will create a new instance but other references will still point to the old items.
2. Because of #1, `foo = {}` is probably faster than `foo.clear()`
3. `foo.clear()` is slower but safer. Be careful with scope with `foo = {}`.

An example👇

``````user = {"name": "Jan", "username": "giaco"}

user.clear()
print(user). # => {}
``````

## What’s the difference between '==' and 'is' in Python?

What's the difference between `==` and `is`?

Let's find out👇

`==` is an equality operator. It checks whether two objects are equal by their values. To compare, objects must have an `__eq__` method implemented.

``````my_list = [1, 2, 3]
another_list = [element for element in my_list]
print(my_list == another_list)
# => True

class User:
def __init__(self, name):
self.name = name

def __eq__(self, other):
return other.name == self.name

user1 = User(name="Jan")
user2 = User(name="Jan")
print(user1 == user2)
# => True
``````

`is` is an identity operator. It checks whether you're referencing the same object on both sides of the comparison. It doesn't care about the values.

``````my_list = [1, 2, 3]
another_list = [element for element in my_list]
print(my_list is another_list)
# => False

class User:
def __init__(self, name):
self.name = name

user1 = User(name="Jan")
user2 = User(name="Jan")
print(user1 is user2)
# => False
print(user1 is user1)
# => True
``````

When should you use one over the other?

Use`==` when you care about values and use `is` when you care about actual objects.

## Python - assert that a mock function was called with certain arguments

Python tip:

You can assert that the method on a `MagicMock` object was called with certain arguments.

https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_called_with

For example, you can mock an email service and assert that the `send` method was called with specific values:

``````from unittest.mock import MagicMock

def place_order(user, items, email_service):
print(f"Place order for user {user}")
print(f"With items: {items}")
email_service.send(user, "Order placed.")

def test_place_order():
email_service = MagicMock()

place_order("[email protected]", ["Computer", "USB drive", "Vodka"], email_service)
email_service.send.assert_called_with("[email protected]", "Order placed.")
``````

## Python - unittest.mock.create_autospec() example

Python tip:

You can use `mock.create_autospec` to create a mock object with the same interface as the provided class, function, or method.

For example, you can use it to mock `Response` from the `requests` package👇

``````from unittest import mock

import requests
from requests import Response

def get_my_ip():
response = requests.get(" http://ipinfo.to/json")
return response.json()["ip"]

def test_get_my_ip(monkeypatch):
my_ip = "123.123.123.123"
response = mock.create_autospec(Response)
response.json.return_value = {"ip": my_ip}
monkeypatch.setattr(requests, "get", lambda *args, **kwargs: response)
assert get_my_ip() == my_ip
``````

## Getting started with Python type hints

Python tip:

Use type hints to annotate expected types for variables, function parameters, and function returns.

https://testdriven.io/blog/python-type-checking/

An example👇

``````from typing import Collection

def daily_average(temperatures: Collection[float]) -> float:
return sum(temperatures) / len(temperatures)

print(daily_average.__annotations__)
# => {'temperatures': typing.Collection[float], 'return': <class 'float'>}
``````

## Teaching kids programming with Python Turtle

Python tip:

Turtle graphics is a popular way to introduce programming to kids.

You can use it to draw different shapes. The cursor moves on the screen.

An example👇

``````from turtle import *

color("red", "yellow")
begin_fill()

while True:
forward(200)
left(170)
if abs(pos()) < 1:
break

end_fill()
done()
``````

## Django deployment checklist

Django tip:

Check your production settings.py file for security vulnerabilities with the `check` command:

``````\$ ./manage.py check --deploy
``````

Example:

``````\$ ./manage.py check --deploy
System check identified some issues:

WARNINGS:
have not set CSRF COOKIE SECURE to True. Using a secure-only CSRF cookie
makes it more difficult for network traffic sniffers to steal the CSRF token.
? (security.W018) You should not have DEBUG set to True in deployment.
? (security.W022) You have not set the SECURE_REFERRER_POLICY setting.
You should consider enabling this header to protect user privacy.
``````

## Test and publish your Python packages to PyPI with poetry and GitHub Actions

Python tip:

Use poetry and Github Actions to:

1. run tests for your packages - on every push
2. check code quality - on every push
3. publish the packages to PyPI - on every release

https://testdriven.io/blog/python-project-workflow/

An example👇

Test and check for code quality:

``````name: Push
on: [push]

jobs:
test:
strategy:
fail-fast: false
matrix:
python-version: [3.9]
poetry-version: [1.1.2]
os: [ubuntu-latest]
runs-on: \${{ matrix.os }}
steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
with:
python-version: \${{ matrix.python-version }}
- name: Run image
uses: abatilo/[email protected]
with:
poetry-version: \${{ matrix.poetry-version }}
- name: Install dependencies
run: poetry install
- name: Run tests
run: poetry run pytest --cov=./ --cov-report=xml
- name: Upload coverage to Codecov
uses: codecov/[email protected]
code-quality:
strategy:
fail-fast: false
matrix:
python-version: [3.9]
poetry-version: [1.1.2]
os: [ubuntu-latest]
runs-on: \${{ matrix.os }}
steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
with:
python-version: \${{ matrix.python-version }}
- name: Run image
uses: abatilo/[email protected]
with:
poetry-version: \${{ matrix.poetry-version }}
- name: Install dependencies
run: poetry install
- name: Run black
run: poetry run black . --check
- name: Run isort
run: poetry run isort . --check-only
- name: Run flake8
run: poetry run flake8 .
- name: Run bandit
run: poetry run bandit .
- name: Run saftey
run: poetry run safety check
``````

Release:

``````name: Release
on:
release:
types:
- created

jobs:
publish:
strategy:
fail-fast: false
matrix:
python-version: [3.9]
poetry-version: [1.1.2]
os: [ubuntu-latest]
runs-on: \${{ matrix.os }}
steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
with:
python-version: \${{ matrix.python-version }}
- name: Run image
uses: abatilo/[email protected]
with:
poetry-version: \${{ matrix.poetry-version }}
- name: Publish
env:
PYPI_TOKEN: \${{ secrets.PYPI_TOKEN }}
run: |
poetry config pypi-token.pypi \$PYPI_TOKEN
poetry publish --build
``````

## Unpacking iterables in Python

Python tip:

You can unpack any iterable to variables as long as the number of variables is the same as the number of elements in the iterable.

Examples👇

``````a, b, c = (1, 2, 3)
print(a, b, c)
# => 1 2 3

a, b, c, d = (1, 2, 3, 4)
print(a, b, c, d)
# => 1 2 3 4

a, b = {1, 2}
print(a, b)
# => 1 2

a, b = {'a': 1, 'b': 2}
print(a, b)
# => a b

a, b, c = (i for i in range(1, 4))
print(a, b, c)
# => 1 2 3

a, b, c = (1, 2)
print(a, b, c)
# => ValueError: not enough values to unpack (expected 3, got 2)

a, b, c = (1, 2, 3, 4)
print(a, b, c)
# => ValueError: too many values to unpack (expected 3)
``````

## Finding memory leaks in Python with tracemalloc

Python tip:

You can use `tracemalloc` to display files allocating the most memory.

An example👇

``````import tracemalloc

import app

tracemalloc.start()

app.run()

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics("lineno")

for stat in top_stats[:10]:
print(stat)
``````

## Python - quick tkinter example

Python tip:

You can use `tkinter` to build simple desktop applications

An example👇

``````import tkinter as tk

class Application(tk.Frame):
def __init__(self, master=None):
self.number_of_clicks = tk.IntVar(value=0)

super().__init__(master)
self.master = master
self.pack()
self.create_widgets()

def create_widgets(self):
self.number_of_clicks_value = tk.Label(self, textvariable=self.number_of_clicks)
self.number_of_clicks_value.pack(side="top")

self.click_me = tk.Button(self, text="Click me", command=self.increment)
self.click_me.pack(side="top")

self.quit = tk.Button(self, text="QUIT", fg="red", command=self.master.destroy)
self.quit.pack(side="bottom")

def increment(self):
self.number_of_clicks.set(self.number_of_clicks.get() + 1)

root = tk.Tk()
app = Application(master=root)
app.mainloop()
``````

## Call a function after some interval in Python with Timer

Python tip:

You can use `Timer` to run some function only after a certain amount of time has passed

An example👇

``````from threading import Timer

def truth():
print("Python rocks!")

t = Timer(15, truth)
t.start() # truth will be called after a 15 second interval
``````

## Mutable default parameter values in Python

Python tip:

Avoid setting mutable default parameters for functions. Instead, use `None` as the default and assign the mutable value inside the function.

Why?

Python evaluates a function's default parameters once, not each time the function is called. So, if you allow a mutable default parameter and mutate it, it will be mutated for all future calls.

An example👇

``````# wrong
users.append("Jan")
return users

# right
users = users or []
users.append("Jan")
return users

``````

## Textwrap - text wrapping in Python

Python tip:

You can wrap long strings into multiple lines where each line is shorter than the limit by using `textwrap.wrap`.

An example👇

``````import textwrap

text = (
"The single-responsibility principle (SRP) is a computer programming principle "
"that states that every class in a computer program should have responsibility over "
"a single part of that program's functionality, which it should encapsulate. "
"All of the module's, class' or function's services should be narrowly aligned with that responsibility"
)
lines = textwrap.wrap(text, 70)

for line in lines:
print(line)

"""
The single-responsibility principle (SRP) is a computer programming
principle that states that every class in a computer program should
have responsibility over a single part of that program's
functionality, which it should encapsulate. All of the module's,
class' or function's services should be narrowly aligned with that
responsibility
"""
``````

## How do I extract a tar file in Python?

Python tip:

You can extract a tar archive to a destination folder using `tarfile`.

An example👇

``````import tarfile

with tarfile.open("example.tar.gz") as tar:
tar.extractall("destination_folder")
``````

## Python - execute shell commands in subprocess

Python tip:

Use the `subprocess` module to spawn new processes.

https://docs.python.org/3/library/subprocess.html#subprocess.Popen

Use `Popen.communicate` to capture result and errors.

An example👇

``````import subprocess

proc = subprocess.Popen(
["ls", "-la", "/home/johndoe/Images"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output, err = proc.communicate()

if err:
raise Exception(err.decode())

print(output.decode())
"""
drwxrwxr-x   2 johndoe  johndoe    4096 Sep 28 08:17 .
drwxr-xr-x  13 johndoe  johndoe    4096 Sep 28 08:12 ..
-rw-rw-r--   1 johndoe  johndoe  115887 Sep 19 09:15 1_initial_user.png
-rw-rw-r--   1 johndoe  johndoe  115055 Sep 19 09:15 2_change_ban.png
-rw-rw-r--   1 johndoe  johndoe  114587 Sep 19 09:15 4_change_database.png
-rw-rw-r--   1 johndoe  johndoe  188718 Sep 19 09:15 5_srp.png
-rw-rw-r--   1 johndoe  johndoe  188718 Sep 19 09:15 change_charge.png
"""
``````

## Arithmetic, Geometric, and Harmonic Means in Python

Python tip:

Use `statistics.mean` to calculate the arithmetic mean of an iterable.

Use `statistics.geometric_mean` to calculate the geometric mean of an iterable (Python >= 3.8).

Use `statistics.harmonic_mean` to calculate the harmonic mean of an iterable.

An example👇

``````from statistics import geometric_mean, harmonic_mean, mean

print(mean([1, 3, 7, 2, 5]))
# => 3.6

print(geometric_mean([1, 3, 7, 2, 5]))
# => 2.913693458576192

print(harmonic_mean([1, 3, 7, 2, 5]))
# => 2.297592997811816
``````

Filter by Topic