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.