python内置str函数的意外行为

pfp*_*ers 6 python

由于str.__call__我显然不理解的行为,我遇到了对str类进行子类型化的问题.

下面的简化代码可以很好地说明这一点.

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!?
Run Code Online (Sandbox Code Playgroud)

我一直认为str(obj)函数只是调用obj.__str__方法,就是这样.但由于某种原因,它也再次称之为__init__功能S.有人可以解释这种行为以及如何避免在使用该函数时S.__init__调用的结果吗?C.__str__str()

use*_*ica 7

严格来说,str不是一个功能.这是一种类型.当您调用时str(c),Python会通过正常的过程来生成类型的实例,调用str.__new__(str, c)以创建对象(或重用现有对象),然后调用__init__结果的方法来初始化它.

str.__new__(str, c)调用C级函数PyObject_Str,该函数调用_PyObject_Str调用__str__方法.结果是一个实例S,因此它被视为一个字符串,并_PyObject_Str确定这是足够好的,而不是试图强迫一个对象type(obj) is str超出结果.因此,str.__new__(str, c)回报c.s.

现在我们来了__init__.由于参数为stris c,因此也会传递给__init__Python,因此Python调用c.s.__init__(c).__init__调用print(c),你可能认为会调用str(c)并导致无限递归.但是,PRINT_ITEM操作码调用C级PyFile_WriteObject来编写对象,并且调用PyObject_Str而不是str,因此它会跳过__init__并且不会无限递归.相反,它调用c.__str__()并打印生成的S实例,因为S实例是一个字符串.