1

I am trying to get property/setter methods described in the parent class to work when inherited into a child class. I am able to get it to work when I directly assign the call from the parent class as shown below. In my case though, I need to wrap the same call inside a method so that I can make some calculations before I call the parent class. I could get it to work by using fget method and passing an object to it, but I would like the former method to work.

I looked up the issue on StackOverflow and found some methods mentioned that involved using super(), which I tried, but it did not work.

Any help getting this inherited property method to work inside a child class is appreciated!

class A(object):
    """ Base Class """
    def __init__(self, n):
        self.n = n
    @staticmethod
    def slices():
        def get_slices(self):
            return self.n
        def set_slices(self, n):
            self.n = n
        return property(get_slices, set_slices)

class B(A):
    def __init__(self, n):
        self.n = n
    his_slices = A.slices() # <--- works well
    def her_slices(self): # <--- does not work
        return A.slices()

if __name__ == "__main__":
    b = B(100)
    # This works
    print("%d slices" % b.his_slices)
    # 100 slices

    # This fails
    print("%d slices" % b.her_slices)
    # TypeError: %d format: a number is required, not method

    # Can be made to work like this, but I want above to work
    ss = b.her_slices()
    print("%d slices" % ss.fget(b))
    # 100 slices
vik-s
  • 13
  • 3
  • Is the indentation under `A.slices` correct (I mean like in your code, wasn't changed when pasted here)? If yes, it's wrong. So is a static method that defines 2 instance? (based on the arg name) methods. – CristiFati Apr 21 '18 at 07:15
  • Thanks for your quick response! Yes, it is correct. It is an explicit way of defining property/setter functions. The call to this static method works, as seen from the his_slices method call. – vik-s Apr 21 '18 at 07:20
  • Your questions is unclear, at least to me. There is no property in parent A, so child B cannot inherit it. There is only the `slices` static method creating and returning a descriptor - that implements a property - at each call. That makes little sense (to me), because a descriptor must be a member of a class in order to have a working property in all instances. However this is not the case in your code. If you don't use the builtin rules of attribute access you need to call the getter yourself creating a code doing trivial things in a convoluted way. – VPfB Apr 21 '18 at 07:42
  • Your are totally right, but I am working on an open source project and I am trying to write a child class that inherits the parent class as I have described in my example. For better or worse, this is how the parent class is written. I'm finding that things are getting convoluted because of this. – vik-s Apr 21 '18 at 14:43

2 Answers2

0

I think you're looking for something more like this.

class A(object):
    """ Base Class """
    def __init__(self, n):
        self.n = n
        self.slices
    def get_slices(self):
        return self.n
    def set_slices(self, n):
        self.n = n
    slices = property(get_slices, set_slices)


class B(A):
    def __init__(self, n):
        self.n = n
    his_slices = A.slices # <--- works well
    def her_slices(self): # <--- does not work
        return self.slices

if __name__ == "__main__":
    b = B(100)
    # This works
    print("%d slices" % b.his_slices)
    # 100 slices

    # This fails
    print("%d slices" % b.her_slices())
    # TypeError: %d format: a number is required, not method

I'm not exactly sure what you're trying to do though.

Ryan Lim
  • 144
  • 6
  • Thank you. I should have clarified that I would like to avoid altering the parent class, and ideally find a work around in the child class. This is because the base class is already used by many child classes in the open source project I am working on, and I am trying to avoid changing it if possible. – vik-s Apr 21 '18 at 14:45
  • I spent some time with this answer again, and found that your way of defining the property slices in Class A is the much nicer way to do it, and also indicated by the response from @Subbdue. This has helped me understand python properties a bit better, so thanks! – vik-s Apr 23 '18 at 05:29
  • Great, sorry I couldn't have been more help. The response from @Subbdue seems a lot more informative in any case. – Ryan Lim Apr 24 '18 at 08:18
0

Treat this like any other inheritance. If you need to modify the behavior of a regular function within the child class, you would just redefine the function and either completely change its contents, or extend it with super and further modify it.

In case of setters/getters, you'd do the same thing ... redefine/extend them.

In your case things are a little different because, as one of the commenters points out, slices is actually a staticmethod that returns a property, and not a property attribute itself. So you really don't have an existing named property that you can inherit and extend.

Looking at what you're trying to do in __main__, what you really want to do is create a new property called his_slices in your child class and invoke A.slices appropriately within the getter and setter. Like so

class A(object):
    """ Base Class """
    def __init__(self, n):
        self.n = n

    @staticmethod
    def slices():
        def get_slices(self):
            print("Get", self.n)
            return self.n

        def set_slices(self, n):
            print("Set", n)
            self.n = n

        return property(get_slices, set_slices)

class B(A):
    def __init__(self, n):
        self.n = n

    @property
    def his_slices(self):
        return A.slices().fget(self)

    @his_slices.setter
    def his_slices(self, v):
        value = self.n + v
        print("modified n in setter", value)
        A.slices().fset(self, value)

if __name__ == "__main__":
    b = B(100)

    # setter
    b.his_slices = 101
    # getter
    print("%d slices" % b.his_slices)

If slices were truly a property in the base class, this is how you would inherit and modify the getter and setter. It is also described here

class A(object):
    """ Base Class """
    def __init__(self, n):
        self.n = n

    def get_slices(self):
        print("Get A", self.n)
        return self.n

    def set_slices(self, n):
        print("Set A", n)
        self.n = n

    slices = property(get_slices, set_slices)


class B(A):
    @property
    def slices(self):
        print("Get B", self.n)
        return A.slices.fget(self)

    @slices.setter
    def slices(self, v):
        value = self.n + v
        print(".. Modifing value before Set", value)
        A.slices.fset(self, value) 

if __name__ == "__main__":
    a = A(10)
    b = B(10)

    a.slices = 9
    b.slices = 11
    print("%d a-slices" % a.slices)
    print("%d b-slices" % b.slices)

Output from the above code:

Set A 9
Modifing value before Set 21
Set A 21
Get A 9
9 a-slices
Get B 21
Get A 21
21 b-slices

... and when in doubt, it's always useful to get back to basics and listen to what Raymond Hettinger has to say about properties

Subbdue
  • 825
  • 1
  • 7
  • 12
  • Very detailed reply, thank you. Using fset and fget as property and setter in the child class is the right way to fix my situation. Oh, also, is the Raymond Hettinger link supposed to point to the python docs for property? Just wondering... – vik-s Apr 23 '18 at 05:31
  • That's right, the link points to a HOWTO guide that Raymond wrote. Scroll to the top of that page. – Subbdue Apr 23 '18 at 05:42