-2
class Foo:
    def __init__(self, *args, **kwargs):
        ...
    def some_method(self):
        ...
        return something

I have this sample class. What I want to do, is when I create an instance it should instantly return the result of a particular method.

I know I can do this by Foo().some_method() but is there anyway I can do this just creating a instance?

I tried doing this,

class Foo:
    def __init__(self, *args, **kwargs):
        ...
        return self.some_method()
    def some_method(self):
        ...
        return something

But it says TypeError: __init__() should return None, not 'object()'

kaya3
  • 47,440
  • 4
  • 68
  • 97
Anaam
  • 124
  • 6
  • 1
    Do you need a class if all you do with it is calling a single method? – bereal Aug 30 '21 at 10:44
  • What you want is a function then, not a class… – deceze Aug 30 '21 at 10:44
  • This is just an example, in my actual work there is a bunch of methods but one method's which using the other methods generates the required result. I was thinking to decrease the syntactical complexity as it's just one method that needs to be returned. – Anaam Aug 30 '21 at 10:47
  • 1
    How about simply writing a function around `Foo`? `def get_foo(): return Foo().some_method()` – deceze Aug 30 '21 at 11:13
  • 1
    @deceze I disagree this is a duplicate of the question you linked - that question is a "why" and this one is a "how", and the linked one doesn't have any answers saying "how". – kaya3 Aug 30 '21 at 12:57
  • 1
    Closest dupe candidate I can find is [this one](https://stackoverflow.com/questions/2491819/how-to-return-a-value-from-init-in-python) but I think this question is about a more specific version of that problem. – kaya3 Aug 30 '21 at 13:04

2 Answers2

2

The __init__ method doesn't actually return the new instance; when you construct an object by writing Foo() you are not calling __init__ directly, you are calling __new__, and the __new__ method initialises the instance by calling __init__. So you can change what Foo() returns by overriding __new__:

class Foo:
    def __new__(cls):
        obj = super().__new__(cls)
        obj.__init__()
        return obj.some_method()
    def __init__(self):
        self.x = 5
    def some_method(self):
        return self.x * 2

print(Foo()) # 10

Alternatively you can use a class decorator to replace Foo with a function which creates the instance, calls the method and returns the result:

from functools import wraps

def autocall(cls):
    @wraps(cls)
    def wrapped():
        obj = cls()
        return obj.some_method()
    return wrapped

@autocall
class Foo:
    def __init__(self):
        self.x = 5
    def some_method(self):
        return self.x * 2

print(Foo()) # 10

That said, it's almost always better to just write a helper function, so that the class Foo can still be used in the normal way for other purposes (e.g. testing):

class Foo:
    def __init__(self):
        self.x = 5
    def some_method(self):
        return self.x * 2

def foo_helper():
    obj = Foo()
    return obj.some_method()

print(foo_helper()) # 10
kaya3
  • 47,440
  • 4
  • 68
  • 97
1

Since you cannot return anything from constructor you can use something like factory pattern:

class Creator:

    def __init__(self, factoryClass):    
        self.factoryClass = factoryClass
    
    def makeMethod(self):
        return self.factoryClass

class FactoryInterface:
    def __init__():
        pass
    def factoryMethod():
        pass

class Factory1(FactoryInterface):
    def __init__():
        pass

    def factoryMethod():
        print("Factory1 method is called.")          

class Factory2(FactoryInterface):
    def __init__():
        pass

    def factoryMethod():
        print("Factory2 method is called.")                      

factoryCreator = Creator(Factory1)
newFactory = factoryCreator.makeMethod()
newFactory.factoryMethod()

factoryCreator = Creator(Factory2)
newFactory = factoryCreator.makeMethod()
newFactory.factoryMethod()

Prints out:

Factory1 method is called.
Factory2 method is called.

Berk Kırtay
  • 46
  • 1
  • 3