Suraj Donthi
Suraj Donthi

Reputation: 329

Calling other class methods from __new__() in python

I have created a class foo as below:

class foo():

    def __new__(cls, a, b, c, add=True):
        return cls.sum(a, b, c) if add else cls.subtract(a, b, c)

    def sum(a, b, c):
        return a + b + c

    def subtract(a, b, c):
        return c - b - a

print(foo(1, 2, 3, True))

This program returns the required result as 6. However, I needed to get the clarity of a few concepts:

  1. Is using this methodology or design of OOP correct or is there a better way to do it? I want the class to return a value or any other object(not it's own class instance)
  2. Regardless of the structure above, if sum and subtract are instance methods, how can they be called without instantiating an object as in the above example i.e., print(...)?

I have observed many python APIs and frameworks returning an object or a value through class instantiation.

I am trying to understand the core concepts of OOPs in python please help.

Upvotes: 1

Views: 234

Answers (1)

Artyer
Artyer

Reputation: 40791

The way you have it now, sum and subtract are indeed instance methods.

>>> foo_obj = object.__new__(foo)  # This will actually create a foo object to demonstrate
>>> foo_obj.sum
<bound method foo.sum of <__main__.foo object at 0x0000000000000000>>
>>> type(foo_obj.sum)
<class 'method'>

But that's just because when you access them through an instance, Python dynamically creates a method (Basically just binds the first argument to the object, usually self)

But, you can access the wrapped function through the class:

>>> foo_obj.sum.__func__
<function foo.sum at 0x0000000000000001>
>>> foo.sum
<function foo.sum at 0x0000000000000001>
>>> foo_obj.sum.__func__ is foo.sum
True

So in your __new__ function, it won't bind the first argument, and they call the underlying function instead of making them an instance method.

To fix the warnings, you can make them classmethods or staticmethods. But it is generally bad practice to not return an object that is an instance of the class from the __new__. If you really wanted to use OOP, either subclass int or make a wrapper, so you can have:

>>> class Foo:
    __slots__ = 'value',
    def __init__(self, a, b, c, add=True):
        self.value = self.sum(a, b, c) if add else self.subtract(a, b, c)
    @staticmethod
    def sum(a, b, c):
        return a + b + c
    @staticmethod
    def subtract(a, b, c):
        return c - b - a
>>> foo = Foo(1, 2, 3, True)
>>> foo
<__main__.foo object at 0x0000000000000002>
>>> foo.value
6

or

>>> class Foo(int):
    __slots__ = ()
    def __new__(cls, a, b, c, add=True):
        value = cls.sum(a, b, c) if add else cls.subtract(a, b, c)
        return super().__new__(cls, value)
    @staticmethod
    def sum(a, b, c):
        return a + b + c
    @staticmethod
    def subtract(a, b, c):
        return c - b - a
>>> foo = Foo(1, 2, 3, True)
>>> foo
6
>>> type(foo)
<class '__main__.Foo'>

Upvotes: 2

Related Questions