-
-
Notifications
You must be signed in to change notification settings - Fork 411
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 = attributeI'll be happy to implement this proposal (actually - both of them) if it would be considered useful.