Feedback Request
- Is the manner in which I use
attrsadequate (i.e. designed well)? - Should I not expect to be able to use argument ordering to order the joining of groups, given my current setup (see
def label(self):below)? - Are there any general Pythonic suggestions for the code I have?
Context
I am working on creating labels for specimens in paleontological collections.
At the moment, I have a poetry package called paleo_utils. __init__.py contains code that takes Label, CollectionsLabel (subclassed Label), and SystematicsLabel (subclassed Label) from label.py. For the sake of this question, the code in Label and SystematicsLabel does not matter.
I would prefer to not have to use __init__ while also using attrs in the code below but at present I am not sure I am able to get {key: kwargs[key] for key in kwargs} using just __attrs_post_init__.
My wish is to be able to have these two cases work as follows:
import paleo_utils
label = paleo_utils.CollectionsLabel(
save_directory="../assets/saved_images/",
id_number="3244",
collection="AMNH",
collector="Dr. Montague",
location="North America",
formation="Navesink",
coordinates=(40.7128, -74.0060),
date_found="2024-01-01",
title_overrides={"collection": "Museum: "},
)
print(label.label())
# ID Number: 3244
# Museum: AMNH
# Collector: Dr. Montague
# Location: North America
# Formation: Navesink
# Coordinates: (40.7128, -74.006)
# Date Found: 2024-01-01
and
import paleo_utils
label = paleo_utils.CollectionsLabel(
save_directory="../assets/saved_images/",
date_found="2024-01-01",
id_number="3244",
formation="Navesink",
collection="AMNH",
collector="Dr. Montague",
location="North America",
coordinates=(40.7128, -74.0060),
title_overrides={
"date_found": "Date Collected: ",
"location": "Locality: "},
)
print(label.label())
# Date Collected: 2024-01-01
# ID Number: 3244
# Formation: Navesink
# Collection: AMNH
# Collector: Dr. Montague
# Locality: North America
# Coordinates: (40.7128, -74.006)
Code For CollectionsLabel
@attrs.define(kw_only=True)
class CollectionsLabel(Label):
collection: str | None = attrs.field(
default=None
)
id_number: str | None = attrs.field(
default=None
)
collector: str | None = attrs.field(
default=None
)
species: str | None = attrs.field(
default=None
)
species_author: str | None = attrs.field(
default=None
)
common_name: str | None = attrs.field(
default=None
)
location: str | None = attrs.field(
default=None
)
coordinates: tuple[float, float] | None = (
attrs.field(default=None)
)
coordinates_separate: bool = attrs.field(
default=False
)
date_found: str | None = attrs.field(
default=None
)
date_cataloged: str | None = attrs.field(
default=None
)
formation: str | None = attrs.field(
default=None
)
formation_author: str | None = attrs.field(
default=None
)
chrono_age: str | None = attrs.field(
default=None
)
chrono_age_author: str | None = attrs.field(
default=None
)
size: str | None = attrs.field(default=None)
link: str | None = attrs.field(default=None)
default_titles = {
"collection": "Collection: ",
"id_number": "ID Number: ",
"collector": "Collector: ",
"species": "Scientific Name: ",
"species_author": "Species Author: ",
"common_name": "Common Name: ",
"location": "Location: ",
"coordinates": "Coordinates: ",
"date_found": "Date Found: ",
"date_cataloged": "Date Cataloged: ",
"formation": "Formation: ",
"formation_author": "Formation Author: ",
"chrono_age": "Age: ",
"chrono_age_author": "Age Author: ",
"size": "Size: ",
"link": "Link: ",
}
title_overrides: dict[str, str] = attrs.field(
factory=dict
) # empty by default
_ordered_kwargs: dict = attrs.field(init=False)
def __init__(self, **kwargs):
self._ordered_kwargs = {key: kwargs[key] for key in kwargs}
def __attrs_post_init__(self):
# update title_overrides with any user-provided overrides
if self.title_overrides:
# merge user-provided titles, overriding defaults
for (
key,
value,
) in self.title_overrides.items():
if key in self.default_titles:
self.default_titles[key] = (
value
)
def _get_collections_attrs(self):
label_attrs = {
attr.name
for attr in Label.__attrs_attrs__
}
# collections_attrs = {
# attr.name: getattr(self, attr.name)
# for attr in self.__attrs_attrs__
# if attr.name not in label_attrs
# }
# print(self.__attrs_attrs__)
collections_attrs = {
key: value for key, value in self._ordered_kwargs.items() if key not in label_attrs
}
return collections_attrs
def label(self):
# empty list for parts of the final label
parts = []
# collections label exclusive attrs
collections_attrs = (
self._get_collections_attrs()
)
# iterative over collections attrs
for (
key,
value,
) in collections_attrs.items():
# for all non-None collections attrs, proceed
if (
value is not None
and not isinstance(value, dict)
):
# edit title with spaces and capitalized
title = self.default_titles.get(
key,
f"{key.replace('_', ' ').capitalize()}: ",
)
# add the group
parts.append(f"{title}{value}")
# consolidate to multiline label
return "\n".join(parts)
TypedDictwith a helper static class? \$\endgroup\$