starfield
is a Python package that allows you to create attrs
classes with a single field that can be initialized using variadic positional arguments (i.e. the star *
). This makes it easier to initialise list-like structures with attrs
without having to explicitly pass a list to the initializer.
To install starfield
, run the following command in your terminal:
pip install starfield
The following example shows how to use starfield
to create a class that behaves like a list with some extra fields:
from attrs import define, field
from starfield import starfield
@define(field_transformer=starfield)
class ShoppingList:
items: list = field(init="*")
store: str = field()
grocery_list = ShoppingList("Milk", "Bread", "Eggs", store="Supermarket")
Without starfield
, you would have to explicitly pass a list to the initializer:
from attrs import define, field
@define
class ShoppingList:
items: list = field()
store: str = field()
grocery_list = ShoppingList(["Milk", "Bread", "Eggs"], store="Supermarket")
To illustrate the power of starfield
, let's look at a more complex example involving nested fields. Suppose we want to create a data structure to represent a simple grammatical expression:
"I" ("love"|"hate") ("cats"|"dogs")
We can define a class to represent this expression with attrs
and starfield
:
from attrs import define, field
from starfield import starfield
@define(field_transformer=starfield)
class And:
children: list = field(init="*")
@define(field_transformer=starfield)
class Or:
children: list = field(init="*")
expr = And("I", Or("love", "hate"), Or("cats", "dogs"))
Without starfield
, you would have to explicitly pass a list to the initializer:
from attrs import define, field
@define
class And:
children: list = field()
@define
class Or:
children: list = field()
expr = And(["I", Or(["love", "hate"]), Or(["cats", "dogs"])])
Nested fields can quickly become unwieldy when initializing objects with attrs
. attrs
's documentation explains why it's usually better to use a classmethod
than to modify the initializer. But this can make initialization even more verbose.
starfield
provides an alternative to using a classmethod
by allowing you to define a single field that can be initialized using variadic positional arguments (i.e. the star *
).
-
starfield
will make all non-star fields keyword-only. -
You can still set the star field using a keyword argument (e.g.
expr = And("I", items=[Or("love", "hate"), Or("cats", "dogs")])
). -
To make the string representation of the class more readable,
starfield
adds a__rich_repr__
method to the class. However, this only works if you're using rich to stringify your objects. To add a__repr__
method as well, you can passrepr=True
tostarfield
.
-
starfield
only works with classes that useattrs
. -
You can only have one star field per class.
-
The behaviour of
starfield
's__repr__
method may be inconsistent with theattrs
-generated__repr__
methods (just in case that matters to you). -
starfield
is not yetmypy
-compatible.- In particular,
mypy
will complain about the following:[arg-type]
: static type checkers won't recognize variadic positional arguments as valid arguments to the initializer.[call-overload]
and[misc]
onfield(init="*")
:attrs
expectsinit
to be abool
.
- As a workaround, you can suppress these errors using:
# type: ignore[arg-type]
upon initialization with variadic positional arguments, and# type: ignore[call-overload, misc]
afterfield(init="*")
in the class definition.
- In particular,
-
This feature has been requested and discussed here. The use of
init="*"
is also proposed. -
pydantic
's root types serve a similar purpose. Notably, however, a class with a root type cannot have any other fields.
Please let me know if I've missed any.
starfield
requires Python 3.9 or later and attrs >= 22.2.0
.