0

I think it is easier for me to explain what I'm trying to achieve with a simple example. Consider the following code:

class Person(object):
    def __init__(self):
        self._age = None
        self._gender = None

    def age(self, value):
        self._age = value
        return self

    def gender(self, value):
        self._gender = value
        return self


p = Person().age(10).gender("male")

assert p.age == 10
assert p.gender == "male"

Obviously the assertion fails because the attributes refers to the methods rather than the variables. I have tried messing with the __getattribute__ method so that it resolves "age" to "_age" but I can't seem to find a way where both cases works and I'm not entirely sure that it is possible at all but Python has surprised me before.

Matt Immer
  • 327
  • 1
  • 13
  • If you really want to use data hiding, here's an [example](http://stackoverflow.com/questions/6304040/real-world-example-about-how-to-use-property-feature-in-python/42300481#42300481) of properties that should get you started. – Mike Apr 20 '17 at 09:18

1 Answers1

3

Short answer: Don't. You're going to run into problems, everyone who uses/reads your code is going to run into problems, and it's not worth the time you spend implementing it.


Long answer: If you absolutely must, you could create a class that sets the attribute's value when called, and otherwise mimics the value's behaviour. To do this, you'll have to override all the magic methods and even then things like print(type(p.age)) won't produce the expected output of <class 'int'>.

Here's a snippet to get you started (with only the __eq__ method implemented):

class CallableAttribute:
    def __init__(self, owner, name):
        self.owner= owner
        self.name= name

        setattr(owner, name, self)
        self(None)

    def __call__(self, value):
        setattr(self.owner, '_'+self.name, value)
        return self.owner

    @property
    def _value(self):
        return getattr(self.owner, '_'+self.name)

    def __eq__(self, other):
        return self._value==other

class Person(object):
    def __init__(self):
        CallableAttribute(self, 'age')
        CallableAttribute(self, 'gender')
Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
  • You're probably right and I should probably reconsider the design choices. A short follow-up question: If I add: assert p.age * 2 == 20, I get TypeError: unsupported operand type(s) for *: 'instance' and 'int' - Is there a way to circumvent this in an elegant manner? – Matt Immer Apr 20 '17 at 10:21
  • @MattImmer Yes, you simply override the [`__mul__`](https://docs.python.org/3/reference/datamodel.html#object.__mul__) function. – Aran-Fey Apr 20 '17 at 10:27
  • Thanks! I'll note that it is possible but bad practice to do so and might consider an alternative design. – Matt Immer Apr 20 '17 at 10:37