20

Why can't I make a class' __call__ method static using the @staticmethod decorator?

class Foo(object): @staticmethod def bar(): return 'bar' @staticmethod def __call__(): return '__call__' print Foo.bar() print Foo() 

outputs

bar <__main__.Foo object at 0x7fabf93c89d0> 

but I would expect it to output

bar __call__ 

2 Answers 2

22

You need to override __call__ on the metaclass. The special methods defined in a class are for its instances, to change a class's special methods you need to change them in its class, i.e metaclass. (When you call Foo() usually the order is: Meta.__call__() --> Foo.__new__() --> Foo.__init__(), only if they return normally)

class Meta(type): @staticmethod def __call__(): return '__call__' class Foo(object): __metaclass__ = Meta @staticmethod def bar(): return 'bar' print Foo() #__call__ 

As you're trying to modify class instantiation, another way will be to override __new__ on the class itself and return __call__ from it(when __new__ returns something other than an instance the __init__ method is never called):

class Foo(object): def __new__(*args): #ignore the args return '__call__' @staticmethod def bar(): return 'bar' print Foo() #__call__ 
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! That clears it up for me. What do you mean by Meta.__call__() --> Foo.__new__() --> Foo.__init__()? Do the arrows mean "calls"?
@fragapanagos That's the order followed by Python when a class is instantiated. See: ideone.com/4gxeiN
Could the meta object be skipped by having the desired type inherit from type?
2

I figured out this problem after having searched for a while but not finding an answer. Then, as I was about to submit my Q&A post to share, I found this post in the list of similarly asked questions. Since I did the work and it may help others find this post in the future, I'm adding it here as an answer.


QUESTION

Is it possible to make __call__ a class method or static method which can be used to execute the same instance of functionality multiple times? For example:

class MyClass: ... _phrase: str = None @classmethod def __call__(cls, phrase: str=None) -> str: ... if phrase is not None: ...# Implement a custom initialization and update cls._phrase = phrase elif (cls._phrase, phrase) == (None, None): ...# Implement a default initialization cls._phrase = "Hello, world!" # Do any necessary work here return cls._phrase print(MyClass()) # Should print "Hello, world!" but prints address # <__main__.MyClass object at 0x000002069BC5BF70> print(MyClass("Goodbye, y'all!")) # Should print "Goodbye, y'all!" but raises error # TypeError: MyClass() takes no arguments 

ANSWER

The short answer is to use __new__ in place of __init__ and __call__. __new__ seems to have a couple important qualities which make it the best way to implement a static callable class. First, when __new__ is implemented, any __init__ or __call__ implementations are ignored, making them mutually exclusive. Second, __new__ is automatically a class method which is why implementing it must result in __init__ and __call__ being totally ignored. Here's how to implement it for the functionality I was needing:

class MyClass: ... _phrase: str = None def __new__(cls, phrase: str=None) -> str: ... if phrase is not None: ...# Implement a custom initialization and update cls._phrase = phrase elif (cls._phrase, phrase) == (None, None): ...# Implement a default initialization cls._phrase = "Hello, world!" # Do any necessary work here return cls._phrase print(MyClass()) # Prints "Hello, world!" print(MyClass("Goodbye, y'all!")) # Prints "Goodbye, y'all!" print(MyClass()) # Prints "Goodbye, y'all!" 

Essentially, one __new__ class method can be used to do what __init__ and __call__ do for class instances.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.