Python
Summary
Main page for all things Python. Other pages cover specific topics, such as:
- Dependency management
- Python environments
- Code style
- Python collections
- Python Pweave
- Pandas
- Notes on Python grammar of graphics
- Monkey patching
- Abstract classes
Installation
Anaconda
An option to install Python is to use Anaconda1.
Download the appropriate installation file from https://www.anaconda.com/products/individual.
And then run the installer with, e.g., sh ./Anaconda3-2021.11-Linux-x86_64.sh
. The installation would typically be under $HOME/anaconda3
.
There is a page dedicated to configuring and using [Anaconda].
Language changes
In 2021, the Python steering council accepted the proposal to add a pattern-matching primitive to the language. The proposal consists of PEP634 along with PEP635 and PEP636.
- pages/backlog/Python 3.10 Cool New Features for You to Try
- Python 3.9 changes
- These include new dictionary operators, topological ordering, IPv6 scoped addresses, new math and string functions, and HTTP codes.
pip
No binary install
To install a package from source with pip
specify
pip install --no-binary $PACKAGE
Since requirements
files are passed as command-line options to pip
, you can also specify it as
some-package
--no-binary
another-package
Additionaly this will also work on setup.py
’s install_requires
. For instance:
setup(
install_requires=[
"some-package==0.0.1 --no-binary"
])
Modules
Problems
Missing __init__.py
In versions of Python before 3.3, an __init__.py
file was mandatory in order for the interpreter to treat a directory, whether on the filesystem or inside a zipfile, as a Python package directory. This file served as a marker, indicating the presence of importable modules or subpackages, even if it was completely empty and didn’t actually run any code during the package import process.
Starting with Python 3.3, this requirement was relaxed. The interpreter now recognizes any directory on sys.path
whose name matches the desired package name as a valid package, regardless of the presence of an __init__.py
file. The directory’s modules and subpackages are treated as part of the package namespace.
To illustrate this difference, let’s look at a simple project structure:
project/
example/
foo.py
Assume that foo.py
has the following content:
print("Hello from ", __name__)
Now, if we set project
as the current working directory and try to import example.foo
using Python 2.7, we’ll encounter an error:
$ python2 -c "import example.foo"
Traceback (most recent call last):
File "<string>", line 1, in <module>
ImportError: No module named example.foo
However, Python 3.3 and later versions can import the submodule seamlessly:
$ python3 -c "import example.foo"
Hello from example.foo
Relative import in Python 3
If a relative import is present inside a Python 3 file (e.g. file1
) inside a module (e.g. mymod
), say
from .foo import bar
We will encounter the error
ImportError: attempted relative import with no known parent package
A possible solution is to include the following in your module’s __init__.py
:
import os, sys
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
Operators
Ternary operator
Ternary operators help reduce the amount of very small if-else blocks. Python does not have a ternary operator like other languages. However, conditionals can be used to the same effect:
y = 7
x = 0 if (y == 1) else 1
print(x)
1
for … else
for-else
blocks allow to capture if a condition was met inside a for-loop
. For instance, consider the following for-loop
:
locations = ['a', 'b', 'c', 'd', 'f']
treasure = False
for location in locations:
if location == 'x':
treasure = True
break
if not treasure:
print("X marks the spot, but not found")
X marks the spot, but not found
We can simplify the above logic using a for-else loop:
for location in locations:
if location == 'x':
break
else:
print("X marks the spot, but not found")
X marks the spot, but not found
Boolean unravelling
and
unravelling the and
boolean operator.
The operation can be rewritten as the function u_and
:
def u_and(a, b):
result = a
if a:
result = b
return result
For instance:
a = True ; b = None
print(a and b, u_and(a, b))
a = True ; b = True
print(a and b, u_and(a, b))
a = False ; b = True
print(a and b, u_and(a, b))
None None
True True
False False
or
On the other hand, or
cand be unravelled as:
def u_or(a, b):
result = a
if not a:
result = b
return result
As an example:
a = True ; b = None
print(a or b, u_or(a, b))
a = True ; b = True
print(a or b, u_or(a, b))
a = False ; b = True
print(a or b, u_or(a, b))
True True
True True
True True
The many faces of print
Concatenating arguments
var1 = "Foo"
var2 = "Bar"
print("I am ", var1, " not ", var2)
I am Foo not Bar
It is also possible to use separators by using the sep
argument:
var1 = "Foo"
var2 = "Bar"
print("I am", var1, "not", var2, sep="!")
I am!Foo!not!Bar
String termination
The end
argument allows to specify the suffix of the whole string.
print("This is on radio", end=" (over)")
This is on radio (over)
Filesystem operations
Get home directory
For Python +3.5:
from pathlib import Path
home = str(Path.home())
print(home)
/Users/rui
List files recursively
For Python +3.5, use glob
:
import glob
# root_dir with trailing slash (i.e. /root/dir/)
root_dir = "./"
for filename in glob.iglob(root_dir + '**/*.md', recursive=True):
print(filename[:-3])
./Python
./Python code style
./Python Abstract classes
./New Features in Python 3.9
./Understanding Decorators in Python
Date operations
Offset-aware operations
Let’s say you have a date without timezone (offset naive), for instance:
from datetime import datetime, timezone
ts = datetime.now().replace(tzinfo=None)
print(ts)
2022-08-07 14:08:13.498348
And you want to calculate the $\delta$ with a datetime which has a time (offset aware). We’ll get an error.
try:
delta = datetime.now(timezone.utc) - ts
except TypeError as error:
print(error)
can't subtract offset-naive and offset-aware datetimes
The solution is to add a timezone to the offset naive date. For instance:
ts = datetime.now(timezone.utc)
delta = datetime.now(timezone.utc) - ts
delta
datetime.timedelta(microseconds=55)
Strings
Packaging
PyOxidizer
An alternative to package Python applications is PyOxidizer.
PyOxidizer is often used to generate binaries embedding a Python interpreter and a custom Python application. However, its configuration files support additional functionality, such as the ability to produce Windows