Home > Enterprise >  Is there "on_getattr" equivalent functionality for attrs package?
Is there "on_getattr" equivalent functionality for attrs package?

Time:10-03

I use attrs package for my entities and it works like a charm in most cases. An issue I faced is in defining getter for attributes (generally in pure python @property is the goto)

for setter attrs has a handy parameter on_setattr that works like:

@attr.s(kw_only=True)
class Customer:
    name = attr.ib(type=str, on_setattr=[prevent_empty_string, other_validators_or_logics, ])

and it relieves us from using @name.setter virtually in any scenario (at least in my experience)

but still, when we have some logic in times of returning the attributes, of course, we cannot use:

@property
def name(...

because it will override the original definition of our attrs' name. So I want to know if there is any way builtin in attrs for such a case or if not what are other solutions besides defining another attribute with a different name i.e.

@property
def get_name(self):
    return self.name # or whatever

Update

A solution would be using/overriding __getattribute__ of attr to add logic something like:

@attr.s(kw_only=True)
class Customer:
    name = attr.ib(type=str, on_setattr=[prevent_empty_string, other_validators_or_logics, ])

    def __getattribute__(self, name: str) -> Any:
        if name == "name":
            return self.__dict__["name"].upper()
        return super().__getattribute__(name)

but eventually, it gets polluted with a lot of if/switch blocks and more importantly will mess with builtin __repr__

CodePudding user response:

There is not…so far the assumption was that for your use-case you'd have a private attribute and define a property on top of it.

If you do something like:

@attr.define
class Customer:
    _name: str = attr.field(on_setattr=[…])

    @property
    def name(self) -> str:
        return self._name.upper()

    @name.setter
    def name(self, value: str) -> None:
        self._name = value

you should get all the upsides with less magic metaprogramming.

N.B. the __init__ argument of _name will still be name.

  • Related