contextlib
This module provides utilities for common tasks involving the with
statement.
contextlib.closing(thing)
Return a context manager that closes thing upon completion of the block. This is basically equivalent to:
Note
Most types managing resources support the context manager protocol, which closes thing on leaving the with statement. As such,
closing()
is most useful for third party types that don’t support context managers. This example is purely for illustration purposes, asurlopen()
would normally be used in a context manager.
contextlib.suppress(*exceptions)
Return a context manager that suppresses any of the specified exceptions if they occur in the body of a with statement and then resumes execution with the first statement following the end of the with statement.
This code is equivalent to:
Package Management
uv
An extremely fast Python package and project manager, written in Rust.
- 🚀 A single tool to replace
pip
,pip-tools
,pipx
,poetry
,pyenv
,twine
,virtualenv
, and more. - ⚡️ 10-100x faster than
pip
. - 🐍 Installs and manages Python versions.
- 🛠️ Runs and installs Python applications.
- ❇️ Runs scripts, with support for inline dependency metadata.
- 🗂️ Provides comprehensive project management, with a universal lockfile.
- 🔩 Includes a pip-compatible interface for a performance boost with a familiar CLI.
- 🏢 Supports Cargo-style workspaces for scalable projects.
- 💾 Disk-space efficient, with a global cache for dependency deduplication.
- ⏬ Installable without Rust or Python via
curl
orpip
. - 🖥️ Supports macOS, Linux, and Windows.
Installing uv
Using uv in Docker
Class and static methods
Note
🧠 Changed in version 3.9: Class methods can now wrap other descriptors such as
property()
.
Note
🦖 Changed in version 3.10: Class methods now inherit the method attributes (
__module__
,__name__
,__qualname__
,__doc__
and__annotations__
) and have a new__wrapped__
attribute.
Note
🔥 Changed in version 3.11: Class methods can no longer wrap other descriptors such as
property()
.
Generators
Note
Have you ever had to work with a dataset so large that it overwhelmed your machine’s memory? Or maybe you have a complex function that needs to maintain an internal state every time it’s called, but the function is too small to justify creating its own class. In these cases and more, generators and the Python yield statement are here to help.
Introduced with PEP 255, generator functions are a special kind of function that return a lazy iterator. These are objects that you can loop over like a list. However, unlike lists, lazy iterators do not store their contents in memory.
A generator expression yields a new generator object. Its syntax is the same as for comprehensions, except that it is enclosed in parentheses instead of brackets or curly braces.
Operator
The operator
module exports a set of efficient functions corresponding to the intrinsic operators of Python.
For example, operator.add(x, y)
is equivalent to the expression x+y
.
Bisect
This module provides support for maintaining a list in sorted order without having to sort the list after each insertion. For long lists of items with expensive comparison operations, this can be an improvement over linear searches or frequent resorting.
Locate the insertion point for x
in a
to maintain sorted order.
The parameters lo
and hi
may be used to specify a subset of the list which should be considered;
by default the entire list is used.
If x
is already present in a
, the insertion point will be before (to the left of) any existing entries.
The return value is suitable for use as the first parameter to list.insert()
assuming that a
is already sorted.
The returned insertion point ip
partitions the array a
into two slices such that
all(elem < x for elem in a[lo : ip])
is true for the left slice and all(elem >= x for elem in a[ip : hi])
is true for the right slice.
key
specifies a key function of one argument that is used to extract
a comparison key from each element in the array. To support searching complex records, the key function is not applied to the x
value.
If key
is None
, the elements are compared directly and no key function is called.
Similar to bisect_left()
,
but returns an insertion point which comes after (to the right of) any existing entries of x
in a
.
The returned insertion point ip
partitions the array a
into two slices such that
all(elem <= x for elem in a[lo : ip])
is true for the left slice and
all(elem > x for elem in a[ip : hi])
is true for the right slice.
Itertools
itertools — Functions creating iterators for efficient looping
This module implements a number of iterator building blocks inspired by constructs from APL, Haskell, and SML. Each has been recast in a form suitable for Python.
The module standardizes a core set of fast, memory efficient tools that are useful by themselves or in combination. Together, they form an “iterator algebra” making it possible to construct specialized tools succinctly and efficiently in pure Python.
Infinite iterators:
Iterator | Arguments | Results | Example |
---|---|---|---|
count | [start[, step]] | start , start+step , start+2*step , … | count(10) ⇾ 10 11 12 13 14 ... |
cycle | p | p0 , p1 , … plast , p0 , p1 , … | cycle('ABCD') ⇾ A B C D A B C D ... |
repeat | elem [,n] | elem , elem , elem ,… endlessly or up to n times | repeat(10, 3) ⇾ 10 10 10 |
Iterators terminating on the shortest input sequence:
Iterator | Arguments | Results | Example |
---|---|---|---|
accumulate | p [,func] | p0 , p0+p1 , p0+p1+p2 , … | accumulate([1,2,3,4,5]) → 1 3 6 10 15 |
batched | p, n | (p0, p1, ..., p_n-1) , … | batched('ABCDEFG', n=3) → ABC DEF G |
chain | p, q, ... | p0 , p1 , … plast , q0 , q1 , … | chain('ABC', 'DEF') → A B C D E F |
chain.from_iterable | iterable | p0 , p1 , … plast , q0 , q1 , … | chain.from_iterable(['ABC', 'DEF']) → A B C D E F |
compress | data, selectors | (d[0] if s[0]) , (d[1] if s[1]) , … | compress('ABCDEF', [1,0,1,0,1,1]) → A C E F |
dropwhile | pred, seq | seq[n] , seq[n+1] , starting when pred fails | dropwhile(lambda x: x<5, [1,4,6,4,1]) → 6 4 1 |
filterfalse | pred, seq | elements of seq where pred(elem) is false | filterfalse(lambda x: x%2, range(10)) → 0 2 4 6 8 |
groupby | iterable[, key] | sub-iterators grouped by value of key(v) | |
islice | seq, [start,] stop [, step] | elements from seq[start:stop:step] | islice('ABCDEFG', 2, None) → C D E F G |
pairwise | iterable | (p[0], p[1]) , (p[1], p[2]) , … | pairwise('ABCDEFG') → AB BC CD DE EF FG |
starmap | func, seq | func(*seq[0]) , func(*seq[1]) , … | starmap(pow, [(2,5), (3,2), (10,3)]) → 32 9 1000 |
takewhile | pred, seq | seq[0] , seq[1] , until pred fails | takewhile(lambda x: x<5, [1,4,6,4,1]) → 1 4 |
tee | it, n | it1 , it2 , … itn splits one iterator into n | |
zip_longest | p, q, ... | (p[0], q[0]) , (p[1], q[1]) , … | zip_longest('ABCD', 'xy', fillvalue='-') → Ax By C- D- |
Combinatoric iterators:
Iterator | Arguments | Results |
---|---|---|
product | p, q, ... [repeat=1] | Cartesian product, equivalent to a nested for-loop |
permutations | p[, r] | r-length tuples, all possible orderings, no repeated elements |
combinations | p, r | r-length tuples, in sorted order, no repeated elements |
combinations_with_replacement | p, r | r-length tuples, in sorted order, with repeated elements |
Batched
Batch data from the iterable into tuples of length n.
The last batch may be shorter than n. Loops over the input iterable and accumulates data into tuples up to size n. The input is consumed lazily, just enough to fill a batch. The result is yielded as soon as the batch is full or when the input iterable is exhausted:
Iterables and Iterators
Python’s iterators and iterables are two different but related tools that come in handy when you need to iterate over a data stream or container. Iterators power and control the iteration process, while iterables typically hold data that you want to iterate over one value at a time.
Formatted Strings
A formatted string literal or f-string is a string literal that is prefixed with
f
or F
. These strings may contain replacement fields,
which are expressions delimited by curly braces {}
.
While other string literals always have a constant value, formatted strings are really expressions evaluated at run time.
The parts of the string outside curly braces are treated literally, except that any doubled curly braces {{
or }}
are replaced with the corresponding single curly brace. A single opening curly bracket {
marks a replacement field,
which starts with a Python expression.
To display both the expression text and its value after evaluation, (useful in debugging), an equal sign =
may be added after the expression.
A conversion field, introduced by an exclamation point !
may follow.
A format specifier may also be appended, introduced by a colon :
.
A replacement field ends with a closing curly bracket }
.
If a conversion is specified, the result of evaluating the expression is converted before formatting.
Conversion !s
calls str()
on the result,
!r
calls repr()
,
and !a
calls ascii()
.
The result is then formatted using the format()
protocol.
The format specifier is passed to the __format__()
method of the expression or conversion result. An empty string is passed when the format specifier is omitted.
The formatted result is then included in the final value of the whole string.
:::note 🔧 Changed in version 3.12: Prior to Python 3.12, reuse of the same quoting type of the outer f-string inside a replacement field was not possible. :::
Backslashes are also allowed in replacement fields and are evaluated the same way as in any other context:
:::note 🔧 Changed in version 3.12: Prior to Python 3.12, backslashes were not permitted inside an f-string replacement field. :::
Configuration 🔧
I love the way we can configure Golang applications WITH TYPES, but this library is in Python, and seems nice to me for doing the same in Python.
HTTP Frameworks 🌐
Sometimes I don’t want to set up the giant Django so, I have these options. Sanic and FastAPI both are asynchronous and says they have good performance.
Sanic
Simple and lightweight: Intuitive API with smart defaults and no bloat allows you to get straight to work building your app. Unopinionated and flexible: Build the way you want to build without letting your tooling constrain you. Performant and scalable: Built from the ground up with speed and scalability as a main concern. It is ready to power web applications big and small. Production ready: Out of the box, it comes bundled with a web server ready to power your web applications. Trusted by millions: Sanic is one of the overall most popular frameworks on PyPI, and the top async enabled framework Community driven: The project is maintained and run by the community for the community.
- After installing, Sanic has all the tools you need for a scalable, production-grade server—out of the box!
- Running Sanic with TLS enabled is as simple as passing it the file paths…
- Up and running with web sockets in no time using the websockets package.
- Serving static files is of course intuitive and easy. Just name an endpoint and either a file or directory that should be served.
- Beginning or ending a route with functionality is as simple as adding a decorator.
- Raising errors will intuitively result in proper HTTP errors:
- Check in on your live, running applications (whether local or remote).
- In addition to the tools that Sanic comes with, the officially supported Sanic Extensions provides lots of extra goodies to make development easier.
- CORS protection
- Template rendering with Jinja
- Dependency injection into route handlers
- OpenAPI documentation with Redoc and/or Swagger
- Predefined, endpoint-specific response serializers
- Request query arguments and body input validation
- Auto create HEAD, OPTIONS, and TRACE endpoints
- Live health monitor
Sanic User Guide - The lightning-fast asynchronous Python web framework
Dependency injection is a method to add arguments to a route handler based upon the defined function signature. Specifically, it looks at the type annotations of the arguments in the handler. This can be useful in a number of cases like:
Sanic User Guide - Sanic Extensions - Dependency Injection
Flask
Welcome to Flask — Flask Documentation (3.0.x)
FastAPI
HTTP Client 🌐
Within the Python programming language, a diverse set of HTTP client libraries exists to facilitate communication with web servers. These libraries enable the programmatic construction and transmission of HTTP requests.
- The
urllib
module, a built-in library, offers a foundational approach to HTTP communication. - The
requests
library, known for its ease of use and rich feature set, is a widely adopted choice. - The
aiohttp
library caters to projects requiring asynchronous operations, ideal for non-blocking communication patterns. - https://github.com/encode/httpx
Django 🦖
The Django documentation which is the best source for learning and reading about it.
Django has built-in support for GIS data. You can find more about it here (for Django 4.1):
Writing REST API in a Django application using Django REST Framework is awesome, following links are the important concepts of the DRF (Django REST Framework):
Sometimes it is better in DRF to read its code because its documentation is not complete:
Semi-automatic swagger documentation for the REST APIs:
Database Optimization
Django’s database layer provides various ways to help developers get the most out of their databases.
As general programming practice, this goes without saying. Find out what queries you are doing and what they are
costing you. Use QuerySet.explain()
to understand how specific QuerySet
s are executed by your database.
To avoid performance problems, it is important to understand:
- That
QuerySet
s are lazy. - When they are evaluated.
- How the data is held in memory.
Use iterator()
,
When you have a lot of objects, the caching behavior of the QuerySet
can cause a large amount
of memory to be used. In this case, iterator()
may help.
Dataclasses
Using data-classes to define request and response in Django REST Framework. There are cases in which your request or response is not a model, in those cases you can define them as a dataclass using the following library.
Using the library instead of 😔:
you can write 😍:
Django Filters
Having reusable filters for models in Django REST Framework with Django-filter. These filters help you to write viewsets easier and give client developers vast choices in getting the data.
inspectdb
There are cases in which you already have the database and want to describe it using Django models:
Tests
In python, you need a library for testing:
pytest: helps you write better programs — pytest documentation
But in Django you don’t need anything and Django already has what you need.
Standard CLI 💾
Welcome to Click — Click Documentation (8.1.x)
Typer
Typer, build great CLIs. Easy to code. Based on Python type hints.
Create a typer.Typer()
app, and create two sub commands with their parameters
You can also display beautiful and more complex information using Rich.
Coverage and Testing
I prefer to write tests using pytest
and coverage
.
Console UIs 💅
Pandas 🐼
The best way for working with data, doing math and statistics over it, etc. is using Pandas:
API reference — pandas documentation
Using it for reading/writing CSV is a better way than any other console application because you can actually do the query things after the reading phase.
GIS
Typing
The function below takes and returns a string and is annotated as follows:
In the function greeting
, the argument name
is expected to be of type str
and the return type str
. Subtypes are accepted as arguments.
Type aliases
A type alias is defined using the type
statement,
which creates an instance of TypeAliasType
.
In this example, Vector
and list[float]
will be treated equivalently by static type checkers:
Type aliases are useful for simplifying complex type signatures.
Annotating callable objects
Functions - or other callable objects - can be annotated using
collections.abc.Callable
or
typing.Callable
.
Callable[[int], str]
signifies a function that takes a single parameter of type int
and returns a str
.
The subscription syntax must always be used with exactly two values: the argument list and the return type.
The argument list must be a list of types, a ParamSpec
,
Concatenate
, or an ellipsis.
The return type must be a single type.
If a literal ellipsis ...
is given as the argument list, it indicates that a callable with any arbitrary parameter list would be acceptable:
:::note
⚠️ Callable
cannot express complex signatures such as functions that take a variadic number of arguments,
overloaded functions, or functions that have keyword-only parameters.
However, these signatures can be expressed by defining a Protocol
class with a __call__()
method.
:::
Generics
Since type information about objects kept in containers cannot be statically inferred in a generic way, many container classes in the standard library support subscription to denote the expected types of container elements.
Generic functions and classes can be parameterized by using type parameter syntax:
:::note ⚠️ Changed in version 3.12: Syntactic support for generics is new in Python 3.12. :::
For most containers in Python, the typing system assumes that all elements in the container will be of the same type.
list
only accepts one type argument.
Mapping
only accepts two type arguments:
the first indicates the type of the keys, and the second indicates the type of the values.
Unlike most other Python containers, however, it is common in idiomatic Python code for tuples to have elements which are not all the same type.
For this reason, tuples are special-cased in Python’s typing system. tuple
accepts any number of type arguments:
To denote a tuple which could be of any length, and in which all elements are of the same type T
, use tuple[T, ...]
.
To denote an empty tuple, use tuple[()]
. Using plain tuple
as an annotation is equivalent to using tuple[Any, ...]
.
The type of class objects
A variable annotated with C
may accept a value of type C
.
In contrast, a variable annotated with type[C]
(or typing.Type[C]
)
may accept values that are classes themselves - specifically, it will accept the class object of C
.
Note that type[C]
is covariant:
:::note 🧠 Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified :::
The only legal parameters for type
are classes,
Any
,
type variables, and unions of any of these types.
Packages
We love types even in python 💘
https://github.com/typeddjango/djangorestframework-stubs
https://github.com/typeddjango/django-stubs
https://github.com/typeddjango/awesome-python-typing
Typing (numpy.typing) — NumPy v1.26 Manual
Images
In python, you can even manipulate images.
Pydantic
Welcome to Pydantic - Pydantic
One of the primary ways of defining schema in Pydantic is via models.
Models are simply classes which inherit from
pydantic.BaseModel
and define fields as annotated attributes.
You can think of models as similar to structs in languages like C, or as the requirements of a single endpoint in an API.
Models share many similarities with Python’s dataclasses
,
but have been designed with some subtle-yet-important differences that streamline
certain workflows related to validation, serialization, and JSON schema generation.
Untrusted data can be passed to a model and, after parsing and validation, Pydantic guarantees that the fields of the resultant model instance will conform to the field types defined on the model.
Beyond accessing model attributes directly via their field names
(e.g. model.foobar
), models can be converted, dumped, serialized,
and exported in a number of ways.
The Field
function is used to customize and add metadata to fields of models.
Examples
Asynchronous
Asynchronous I/O
Asyncio is used as a foundation for multiple Python asynchronous frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc.
Running an asyncio Program
Runners are built on top of an event loop with the aim to simplify async code usage for common wide-spread scenarios.
This function runs the passed coroutine, taking care of managing the asyncio event loop, finalizing asynchronous generators, and closing the executor.
:::note This function cannot be called when another asyncio event loop is running in the same thread. :::
If debug is True
, the event loop will be run in debug mode.
False
disables debug mode explicitly.
None
is used to respect the global
Debug Mode settings.
If loop_factory is not None
, it is used to create a new event loop;
otherwise asyncio.new_event_loop()
is used.
The loop is closed at the end. This function should be used as a main entry point for asyncio programs,
and should ideally only be called once.
It is recommended to use loop_factory to configure the event loop instead of policies.
Runner context manager
A context manager that simplifies multiple async function calls in the same context.
Sometimes several top-level async functions should be called in the same
event loop
and contextvars.Context
.
If debug
is True
, the event loop will be run in debug mode.
False
disables debug mode explicitly.
None
is used to respect the global Debug Mode settings.
loop_factory
could be used for overriding the loop creation.
It is the responsibility of the loop_factory
to set the created loop as the current one.
By default, asyncio.new_event_loop()
is used and set as current event loop with asyncio.set_event_loop()
if loop_factory
is None
.
Basically, asyncio.run()
example can be rewritten with the runner usage:
Run a coroutine coro
in the embedded loop.
Return the coroutine’s result or raise its
exception. An optional keyword-only context argument allows specifying a custom
contextvars.Context
for the coro
to run in. The runner’s default context is used if None
.
This function cannot be called when another asyncio event loop is running in the same thread.
Close the runner. Finalize asynchronous generators, shutdown default executor,
close the event loop and release embedded contextvars.Context
.
Return the event loop associated with the runner instance.
Coroutines
Coroutines declared with the async/await syntax is the preferred way of writing asyncio applications. To actually run a coroutine, asyncio provides the following mechanisms:
-
The
asyncio.run()
function to run the top-level entry point “main()” function. -
Awaiting on a coroutine. The following snippet of code will print “hello” after waiting for 1 second, and then print “world” after waiting for another 2 seconds:
-
The
asyncio.create_task()
function to run coroutines concurrently as asyncioTasks
.The
asyncio.TaskGroup
class provides a more modern alternative tocreate_task()
. Using this API, the last example becomes:
Awaitables
We say that an object is an awaitable object if it can be used in
an await
expression.
Many asyncio APIs are designed to accept awaitables.
There are three main types of awaitable objects:
- coroutines
- Tasks
- Futures
:::note
⚠️ A coroutine function: an async def
function.
A coroutine object: an object returned by calling a coroutine function.
:::
:::note 🔥 Tasks are used to schedule coroutines concurrently. :::
Running Tasks Concurrently
:::note
🚧 A new alternative to create and run tasks concurrently and wait for their completion is
asyncio.TaskGroup
`.
TaskGroup provides stronger safety guarantees than gather for scheduling a nesting of subtasks:
if a task (or a subtask, a task scheduled by a task) raises an exception,
TaskGroup will, while gather will not, cancel the remaining scheduled tasks.
:::
Task Groups
Task groups combine a task creation API with a convenient and reliable way to wait for all tasks in the group to finish.
Eager Task Factory
A task factory for eager task execution.
When using this factory (via loop.set_task_factory(asyncio.eager_task_factory)
),
coroutines begin execution synchronously during Task construction.
Tasks are only scheduled on the event loop if they block.
This can be a performance improvement as the overhead of
loop scheduling is avoided for coroutines that complete synchronously.
A common example where this is beneficial is coroutines which employ caching or memoization to avoid actual I/O when possible.
:::note 🐼 Immediate execution of the coroutine is a semantic change. If the coroutine returns or raises, the task is never scheduled to the event loop. If the coroutine execution blocks, the task is scheduled to the event loop. This change may introduce behavior changes to existing applications. For example, the application’s task execution order is likely to change. :::
AIOfiles
aiofiles is an Apache2 licensed library, written in Python, for handling local disk files in asyncio applications.
HTTPX
HTTPX is a fully featured HTTP client for Python 3, which provides sync and async APIs, and support for both HTTP/1.1 and HTTP/2.
AIOHTTP
Welcome to AIOHTTP — aiohttp documentation
Key features
- Supports both Client and HTTP Server.
- Supports both Server WebSockets and Client WebSockets out-of-the-box without the Callback Hell.
- Web-server has Middlewares, Signals and plug-able routing.
Now, we have a ClientSession
called session
and a ClientResponse
object called resp
. We can get all the information we need from the response. The mandatory parameter of
ClientSession.get()
coroutine is an HTTP URL (str
or
class:yarl.URL
instance).
:::note Don’t create a session per request. Most likely you need a session per application which performs all requests together. More complex cases may require a session per site, e.g. one for GitHub and other one for Facebook APIs. Anyway making a session for every request is a very bad idea. A session contains a connection pool inside. Connection reuse and keep-alive (both are on by default) may speed up total performance. :::
A session context manager usage is not mandatory but await session.close()
method should be called in this case.
Streaming Response Content
While methods read()
,
json()
and text()
are very convenient you should use them carefully. All these methods load the whole response in memory.
For example if you want to download several gigabyte sized files, these methods will load all the data in memory.
Instead, you can use the content
attribute.
It is an instance of the aiohttp.StreamReader
class.
The gzip
and deflate
transfer-encodings are automatically decoded for you:
In general, however, you should use a pattern like this to save what is being streamed to a file:
It is not possible to use read()
,
json()
and text()
after explicit reading from content
.
Copy-on-write and GC
The theory was that every time we did a collection, it would update the gc_refs
with ob_refcnt
for all tracked objects
— but unfortunately this write-operation, caused memory pages to be COW-ed. A next obvious solution was to move all the
head to another chunk of memory and store densely.