Mahi
Mahi

Reputation: 21951

Use __getattr__ instead of a getter and still have a setter?

Say my class already has __getattr__ and __setattr__ like so:

def __getattr__(self, attr):
    return _global_user_data[self.userid][attr]

def __setattr__(self, attr, value):
    _global_user_data[self.userid][attr] = value

I want to make it so that player's gold (for example) cannot drop below zero. Here's a setter for it:

def gold(self, value):
    if value < 0:
        raise ValueError
    self.__setattr__('gold', value)

However, I find it stupid to write a getter which does nothing but calls for self.__getattr__('gold'), is there a way to make it so I don't need do define yet an other method here?

It's not just one property (gold in this example), I have multiple properties and the getter seemS to be just some extra work.

Upvotes: 0

Views: 797

Answers (1)

jonrsharpe
jonrsharpe

Reputation: 122161

If I've correctly understood what you're after (this time!) you could do something like:

class User:

    def __init__(self, id_):
        self.user_id = id_

    def __getattr__(self, attr):
        return _global_user_data[self.user_id][attr]

    def set_x(self, val):
        if val < 0:
            raise ValueError
        _global_user_data[self.user_id]['x'] = val

    x = property(lambda self: self.__getattr__('x'), set_x)

You only need to define a specific setter for x, the getter just redirects to the existing __getattr__. Note that this creates the property directly, rather than using the decorator syntax, but otherwise does exactly the same thing. In use:

>>> _global_user_data = {0: {'x': None}}
>>> user = User(0)
>>> user.x
>>> user.x = -1
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    user.x = -1
  File "<pyshell#35>", line 8, in set_x
    raise ValueError
ValueError
>>> user.x = 1
>>> user.x
1

Upvotes: 3

Related Questions