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.
Meta.__call__()
-->Foo.__new__()
-->Foo.__init__()
? Do the arrows mean "calls"? – Throttle