python-attrs / python-attrs/attrs
[RFC] A shortcut to define a classvar with the same name as an attribute
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 5.8k
- Forks
- 480
- Avg merge
- 2h 15m
- Merged PRs (30d)
- 2
Description
Hello!
Thanks again for the great library! :)
After answering in a couple of issues regarding default values on class-level and debugging another mess with self-using factory which was fixed by migration to @cached_property, I thought that it may be good to have a simple way to set a class-level attribute with the same name as an attr.ib on attrs class.
The issue can be illustrated by a following example:
import attr
@attr.dataclass
class BaseClass:
value: int
magic: str
@attr.s
class FixedMagicClass(BaseClass):
magic = attr.ib(init=False, default='alohomora')
@attr.s
class _ReprMagicClass(BaseClass):
# A proxy class to exclude 'magic' from init
magic = attr.ib(init=False)
@attr.s
class ReprMagicClass(_ReprMagicClass):
@cached_property
def magic(self):
return repr(self.value)
I'd like to add classvar parameter in attr.ib and decorator syntactic sugar like this:
@attr.s
class FixedMagicClass(BaseClass):
magic = attr.ib(init=False, classvar='alohomora') # Almost no difference, but it can be accessed on the class object
@attr.s
class ReprMagicClass(BaseClass):
@attr.ib(init=False).classvar
@cached_property # Property is a descriptor and is set on class object itself
def magic(self):
return repr(self.value)
Alternative approach may be implementing it via metadata + field_transformer, but syntax becomes clumsy. From my perspective this seems more like a core feature than like an extension.
Though, it may be a non-desired feature as it wouldn't work with slotted classes - they use their own slot descriptors - and therefore may complicate migration from dict-classes to slot-classes.
Other possible related feature, which may blend in perfectly with property and class-level constants, is a way to make an attr.ib skippable during __init__ - e.g. if an attribute is init=True and has default=attr.UNSET the generated __init__ code will be like following:
def __init__(self, required_attribute, attribute=attr.UNSET):
self.required_attribute = required_attribute # This attribute had `attr.NOTHING` default in `attr.ib`
if attribute is not attr.UNSET: # If it wasn't set, we'll take the classvar instead
self.attribute = attribute
I'll be happy to implement this proposal (actually - both of them) if it would be considered useful.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the examples in the issue and review the existing attr.ib and decorator behavior; no source files or tests are named. The first milestone is maintainer agreement on whether the classvar proposal, skippable-init proposal, or neither should be pursued, after which the chosen scope can be implemented and validated.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100