Embark on your Python journey: mastering classes, abstraction, packages, and Poetry for aspiring developers.
Introduction
In this guide, aspiring developers will explore the foundational elements of Python that are crucial for building robust applications. From understanding classes and abstraction to organizing code with packages and modules, you’ll gain practical insights into structuring your projects effectively.
Package and dependency management
Python package management is the process of managing the dependencies and packages used by Python projects. This includes installing, upgrading, and removing packages, as well as keeping track of their versions, requirements and dealing with python versions. Popular tools: Pyenv, Poetry, UV, Conda.
A — Manage Python version with Pyenv
Pyenv lets you manage multiple Python versions on your machine.
Installation
curl https://pyenv.run | bash
List and install versions
pyenv install --list
pyenv install 3.13.0
pyenv versions
pyenv global 3.13.0
python -V
B — Manage Python projects dependency and packaging with Poetry
Installation
curl -sSL https://install.python-poetry.org | python3 -
Basic usage
poetry new poetry-demo
poetry-demo
├── README.md
├── poetry_demo
│ └── __init__.py
├── pyproject.toml
└── tests
└── __init__.py
pyproject.toml:
[tool.poetry]
name = "poetry-demo"
version = "0.1.0"
description = ""
authors = ["James Kokou GAGLO <[email protected]>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Dependencies and virtual environments
poetry add playwright
poetry env use python3.12
poetry env list
poetry env info
Poe — task management
pip install poethepoet
[tool.poe.tasks]
py-version = "python --version"
poetry poe py-version
C — Python module and package
A module is a single file containing Python code. A package is a directory containing one or more modules and a special __init__.py file.
poetry-demo
├── poetry_demo
│ ├── __init__.py
│ ├── main.py
│ ├── module1.py
│ ├── subpackage1
│ │ ├── __init__.py
│ │ └── module2.py
│ └── subpackage2
│ ├── __init__.py
│ └── module3.py
# main.py
import module1
from subpackage1 import module2
from subpackage2 import module3
print(module1.func1())
print(module2.func2())
print(module3.func3())
D — Python and OOP concepts
Inheritance
class Animal:
def speak(self):
return "I make a sound."
class Dog(Animal):
def speak(self):
return "Woof!"
dog = Dog()
print(dog.speak()) # "Woof!"
Use super() to access parent class methods.
Polymorphism
class Cat:
def speak(self):
return "Meow!"
class Dog:
def speak(self):
return "Woof!"
animals = [Cat(), Dog()]
for animal in animals:
print(animal.speak())
Abstraction
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
Class and static methods
class MyClass:
@classmethod
def from_string(cls, arg):
return cls(arg)
@staticmethod
def is_valid(arg):
return arg > 0
E — Pydantic: data validation
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
email: str = None
user_data = {"name": "Alice", "age": 30}
try:
user = User(**user_data)
print(user.name, user.age, user.email)
except ValueError as e:
print(e)
Custom validator:
from pydantic import BaseModel, validator
class User(BaseModel):
name: str
age: int
@validator('age')
def check_age(cls, value):
if value < 0:
raise ValueError("Age must be positive")
return value
Serialization:
user_dict = user.dict()
user_json = user.json()
F — Pydantic Settings: load settings and configs
from pydantic import BaseSettings, ValidationError
class MyAppSettings(BaseSettings):
database_url: str
debug_mode: bool = False
max_connections: int = 10
class Config:
env_file = '.env'
env_prefix = 'MYAPP_'
import os
os.environ['MYAPP_DATABASE_URL'] = 'sqlite:///mydb.sqlite'
os.environ['MYAPP_DEBUG_MODE'] = 'True'
settings = MyAppSettings()
print(settings.json(indent=2))
G — Click: command line interfaces
import click
@click.command()
@click.option('--name', default='World', help='The person to greet.')
def hello(name):
"""Simple program that greets NAME."""
click.echo(f'Hello {name}!')
if __name__ == '__main__':
hello()
python script.py --name James
python script.py
H — SQLAlchemy: SQL ORM
import datetime, uuid
from loguru import logger
from sqlalchemy import create_engine, func, MetaData
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import DeclarativeBase, mapped_column, Mapped, registry, Session
from typing import Type, TypeVar, Generic
_database = create_engine(settings.db_url, echo=settings.db_debug)
mapper_registry = registry()
my_metadata = MetaData()
T = TypeVar('T', bound='PsqlBaseModel')
class PsqlBaseModel(DeclarativeBase, Generic[T]):
metadata = my_metadata
id = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
created_at: Mapped[datetime.datetime] = mapped_column(server_default=func.now())
updated_at: Mapped[datetime.datetime] = mapped_column(server_default=func.now(), server_onupdate=func.now())
@staticmethod
def bulk_insert(objects):
with Session(_database) as session:
session.bulk_save_objects(objects)
session.commit()
@classmethod
def get_by_hash(cls: Type[T], hash: str):
with Session(_database) as session:
return session.query(cls).filter(cls.hash == hash).first()
Create all tables:
PsqlBaseModel.metadata.create_all(_database)
Product model with PostgreSQL trigger:
class Product(PsqlBaseModel):
__tablename__ = "products"
name: Mapped[str] = mapped_column(nullable=True)
hash: Mapped[str] = mapped_column(index=True)
price: Mapped[int] = mapped_column(index=True, type_=DECIMAL(10,2), nullable=True, default=0)
url: Mapped[str]
website: Mapped[str]
image: Mapped[str] = mapped_column(nullable=True)
website_updated_at: Mapped[datetime.datetime]
crawler_updated_at: Mapped[datetime.datetime]
I — Alembic: SQLAlchemy migrations
In progress.
J — FastAPI: web framework for building APIs
In progress.