flowchart LR A(Input A) --> F["Black box"] B(Input B) --> F F --> O(Output) style F fill:#000,color:#fff,stroke:#333,stroke-width:4px
flowchart LR A(Input A) --> F["Black box"] B(Input B) --> F F --> O(Output) style F fill:#000,color:#fff,stroke:#333,stroke-width:4px
flowchart LR A(height: 1.0) --> F[is_operable] B(period: 3.0) --> F F --> O(True)
A pure function returns the same output for the same input.
A non-pure function can return different outputs for the same input.
A function can have side effects (besides returning a value)
Pure functions without side effects are easier to reason about.
But sometimes side effects are necessary.
Functions that doesn’t modify the input arguments are easier to use.
Just be aware that copying large datasets can be slow.
Swapping out the internals of the black box…
Makes it easy to use a function with many arguments.
Python’s default arguments are evaluated once when the function is defined, not each time the function is called.
Since Python is a dynamic language, the type of the returned variable is allowed to vary.
General advice: Avoid multiple return types!
Important
Is this the result you expected?
A non-empty string or a non-zero value is considered “truthy” in Python!
Python is a dynamically typed language, the type of a variable is determined at runtime.
['hammer']
['hammer']
from datetime import date
class Interval:
def __init__(self, start:date, end:date) -> None:
self.start = start
self.end = end
@staticmethod
def from_string(date_string: str) -> Interval:
start_str, end_str = date_string.split("|")
start = date.fromisoformat(start_str)
end = date.fromisoformat(end_str)
return Interval(start, end)
dr = Interval.from_string("2020-01-01|2020-01-31")
dr
<__main__.Interval at 0x7f23f8d978c0>
from dataclasses import dataclass
@dataclass
class Interval:
start: date
end: date
@staticmethod
def from_string(date_string):
start_str, end_str = date_string.split("|")
start = date.fromisoformat(start_str)
end = date.fromisoformat(end_str)
return Interval(start, end)
dr = Interval.from_string("2020-01-01|2020-01-31")
dr
Interval(start=datetime.date(2020, 1, 1), end=datetime.date(2020, 1, 31))
Interval(start=datetime.date(2020, 1, 1), end=datetime.date(2020, 1, 31))
On a regular class, equality is based on the memory address of the object.
class Interval:
def __init__(self, start:date, end:date):
self.start = start
self.end = end
def __eq__(self, other):
return self.start == other.start and self.end == other.end
dr1 = Interval(start=date(2020, 1, 1), end=date(2020, 1, 31))
dr2 = Interval(start=date(2020, 1, 1), end=date(2020, 1, 31))
dr1 == dr2
True
For a dataclass, equality is based on the values of the fields.
True
Modules are files containing Python code (functions, classes, constants) that belong together.
$tree analytics/
analytics/
├── __init__.py
├── date.py
└── tools.py
The analytics package contains two modules:
tools
moduledate
module__init__.py
__init__.py
can be empty, and it indicates that the directory it contains is a Python package__init__.py
can also execute initialization code__init__.py
Example: mikeio/pfs/__init__.py
:
from ._pfsdocument import PfsDocument
from ._pfssection import PfsNonUniqueList, PfsSection
def read_pfs(filename:str, encoding:str="cp1252", unique_keywords:bool=False) -> PfsDocument:
"""Read a pfs file for further analysis/manipulation"""
return PfsDocument(filename, encoding=encoding, unique_keywords=unique_keywords)
By adhering to the naming conventions, your code will be easier to read for other Python developers.
lowercase_with_underscores
CamelCase
UPPERCASE_WITH_UNDERSCORES
Python will not prevent you from changing the value of a constant, but it is a convention to use all uppercase characters for constants.
Python package development