由于我显然不理解的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!?
我一直认为str(obj)
函数只是调用obj.__str__
方法,就是这样。但由于某种原因,它还会再次调用__init__
的{{1}}函数。
有人可以解释一下这种行为以及我如何避免在使用S
函数时S.__init__
调用C.__str__
的结果吗?
答案 0 :(得分:7)
严格地说,str
不是一个功能。这是一种类型。当您调用str(c)
时,Python会通过正常的过程来生成类型的实例,调用str.__new__(str, c)
来创建对象(或重用现有对象),然后调用{{ 1}}初始化结果的方法。
str.__new__(str, c)
调用C级函数PyObject_Str
,该函数调用_PyObject_Str
,调用__init__
方法。结果是__str__
的实例,因此它被视为一个字符串,而S
决定这是足够好的,而不是试图用结果中的_PyObject_Str
来强制对象。因此,type(obj) is str
会返回str.__new__(str, c)
。
现在我们到达c.s
。由于__init__
的参数为str
,因此也会传递给c
,因此Python会调用__init__
。 c.s.__init__(c)
调用__init__
,您可能认为这会调用print(c)
并导致无限递归。但是,PRINT_ITEM
操作码调用C级PyFile_WriteObject来编写对象,并调用str(c)
而不是PyObject_Str
,因此它会跳过str
和没有无限的递归。相反,它调用__init__
并打印生成的c.__str__()
实例,因为S
实例是一个字符串。