Unexpected behavior of python builtin str function

2024/9/8 10:42:20

I am running into an issue with subtyping the str class because of the str.__call__ behavior I apparently do not 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.sc = 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 the str(obj) function simply calls the obj.__str__ method, and that's it. But for some reason it also calls the __init__ function of S again. Can someone explain the behavior and how I can avoid that S.__init__ is called on the result of C.__str__ when using the str() function?

Answer

Strictly speaking, str isn't a function. It's a type. When you call str(c), Python goes through the normal procedure for generating an instance of a type, calling str.__new__(str, c) to create the object (or reuse an existing object), and then calling the __init__ method of the result 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 counts as a string, and _PyObject_Str decides this is good enough rather than trying to coerce an object with type(obj) is str out of the result. Thus, str.__new__(str, c) returns c.s.

Now we get to __init__. Since the argument to str was c, this also gets passed to __init__, so Python calls c.s.__init__(c). __init__ calls print(c), which you might think would call str(c) and lead to infinite recursion. However, the PRINT_ITEM opcode calls the C-level PyFile_WriteObject to write the object, and that calls PyObject_Str instead of str, so it skips the __init__ and doesn't recurse infinitely. Instead, it calls c.__str__() and prints the resulting S instance, as the S instance is a string.

https://en.xdnf.cn/q/72683.html

Related Q&A

How to set cookies with GAE/Python for 1 month?

I need to implement the following:User input user id and pass We validate that on another server If they are correct, cookies with these details should be saved for one month Each time user uses my sit…

connection refused with Celery

I have a Django project on an Ubuntu EC2 node, which I have been using to set up an asynchronous using Celery. I am following How to list the queued items in celery? along with the docs, to experiment…

Routing all packets through my program?

I want to build an application that routes all network traffic (not just HTTP) through my application. Basically, what I want is all the traffic to be given to my application (they should never reach t…

python struct.pack(): pack multiple datas in a list or a tuple

Say i have a list or a tuple containing numbers of type long long,x = [12974658, 638364, 53637, 63738363]If want to struct.pack them individually, i have to use struct.pack(<Q, 12974658)or if i want…

Can I pass a list of colors for points to matplotlibs Axes.plot()?

Ive got a lot of points to plot and am noticing that plotting them individually in matplotlib takes much longer (more than 100 times longer, according to cProfile) than plotting them all at once. Howev…

Tidy data from multilevel Excel file via pandas

I want to produce tidy data from an Excel file which looks like this, with three levels of "merged" headers:Pandas reads the file just fine, with multilevel headers:# df = pandas.read_excel(t…

ttk tkinter multiple frames/windows

The following application I have created is used to demonstrate multiple windows in tkinter. The main problem is that none of the Entry controls, neither in the bmi-calculator or the converter, accept …

input() vs sys.stdin.read()

import sys s1 = input() s2 = sys.stdin.read(1)#type "s" for examples1 == "s" #False s2 == "s" #TrueWhy? How can I make input() to work properly? I tried to encode/decode…

Renormalize weight matrix using TensorFlow

Id like to add a max norm constraint to several of the weight matrices in my TensorFlow graph, ala Torchs renorm method.If the L2 norm of any neurons weight matrix exceeds max_norm, Id like to scale it…

Numpy: find the euclidean distance between two 3-D arrays

Given, two 3-D arrays of dimensions (2,2,2):A = [[[ 0, 0],[92, 92]],[[ 0, 92],[ 0, 92]]]B = [[[ 0, 0],[92, 0]],[[ 0, 92],[92, 92]]]How do you find the Euclidean distance for each vector in A and B e…