0

I have an intresting behaviour for the following code:

class MyClass:
    def __init__(self):
        self.abc = 10

    @property
    def age(self):
        return self.abc

    @age.getter
    def age(self):
        return self.abc + 10

    @age.setter
    def age(self, value):
        self.abc = value

obj = MyClass()
print(obj.age)
obj.age = 12
print(obj.age)
obj.age = 11
print(obj.age)

And I have the following result:

20
12
11

Can somebody explain this behaviour ?

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
Denis Kotov
  • 857
  • 2
  • 10
  • 29

1 Answers1

1

On old style classes (which yours is if you're executing on Python 2) the assignment obj.age = 11 will 'override' the descriptor. See New Class vs Classic Class:

New Style classes can use descriptors (including __slots__), and Old Style classes cannot.

You could either actually execute this on Python 3 and get it executing correctly, or, if you need a solution that behaves similarly in Python 2 and 3, inherit from object and make it into a New Style Class:

class MyClass(object):
    # body as is

obj = MyClass()
print(obj.age)   # 20
obj.age = 12
print(obj.age)   # 22
obj.age = 11
print(obj.age)   # 21
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • Thanks, seems like you are right. I have checked with python 3 and result as you discribed (something wrong with my preferences, i thought that i was usin python 3). But I have another question: In which case function: {code} property def age(self): return self.abc {/code} This function would be available ? Because it is shadowed be: {code} age.getter def age(self): return self.abc + 10 {/code} – Denis Kotov Nov 22 '16 at 21:18
  • @ДенисКотов If you use `age.getter` with `def age(self):..` you won't be able to use another function with the name `age`, this applies for both `Python 2` when you inherit from `object` and `Python 3` by default. Is that what you're asking? – Dimitris Fasarakis Hilliard Nov 22 '16 at 21:26
  • Yes, that i asked. But i curious, why in this case we need age.getter decorator if we can use default one ? – Denis Kotov Nov 22 '16 at 21:38
  • Ah, take a look at this great answer for a good explanation on that: http://stackoverflow.com/questions/1554546/when-and-how-to-use-the-builtin-function-property-in-python – Dimitris Fasarakis Hilliard Nov 22 '16 at 21:42
  • I mean that property def age(self): return self.abc is useless, because we have age.getter def age(self): return self.abc + 10. What is the reason ? We have lost method ... ( – Denis Kotov Nov 22 '16 at 21:47
  • Oh I see now. Yes, you *don't* need `@age.getter` here since you've used `@property` with `def age`. You can remove `@age.getter` and do `return self.abc + 10` in `@property def age(self):..` and it wll work similarly @ДенисКотов – Dimitris Fasarakis Hilliard Nov 22 '16 at 21:53
  • Using `@property` with a function after defines a `getter`. You then override this when you did `@age.getter`. You can chose either one of those and the functionality will stay the same. – Dimitris Fasarakis Hilliard Nov 22 '16 at 21:54
  • @ Jim Fasarakis-Hilliard But can i live both ? And how to call first one property def age(self): return self.abc, because if i do obj.age every time i call second ? – Denis Kotov Nov 22 '16 at 23:01
  • You can't have both, the second one overrides the first, you'll have to use one getter for the attribute @ДенисКотов – Dimitris Fasarakis Hilliard Nov 22 '16 at 23:02