Unexpected behavior of python inline function

I ran into a problem with subtyping the str class due to the behavior of str.__call__ , which I apparently don't understand.

This is best illustrated by the simplified code below.

 class S(str): def __init__(self, s: str): assert isinstance(s, str) print(s) class C: def __init__(self, s: str): self.s = S(s) def __str__(self): return self.s c = C("a") # -> prints "a" c.__str__() # -> does not print "a" str(c) # -> asserts fails in debug mode, else prints "a" as well!? 

I always thought that the str(obj) function just calls the obj.__str__ and that it is. But for some reason, it calls the __init__ S function again. Can someone explain the behavior and how can I avoid the fact that S.__init__ is called as a result of C.__str__ when using the str() function?

+6
source share
1 answer

Strictly speaking, str not a function. This is a type. When you call str(c) , Python goes through the usual procedure to generate an instance of the type, calling str.__new__(str, c) to create an object (or reuse an existing object) , and then invoke the __init__ method to initialize it .

str.__new__(str, c) calls the C level function PyObject_Str , which calls _PyObject_Str , which calls your __str__ method. The result is an instance of S , so it is considered a string, and _PyObject_Str decides that it is good enough, and not try to force the object with type(obj) is str to the result. So str.__new__(str, c) returns cs .

Now we go to __init__ . Since the str argument was c , it is also passed to __init__ , so Python calls cs__init__(c) . __init__ calls print(c) , which you think will call str(c) and lead to infinite recursion. However, the PRINT_ITEM calls C-level PyFile_WriteObject to write the object and which calls PyObject_Str instead of str , so it skips __init__ and doesn’t solve endlessly. Instead, it calls c.__str__() and prints the resulting instance of S , since the instance of S is a string.

+7
source

All Articles