python内置str函数的意外行为

时间:2016-01-18 16:25:43

标签: 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!?

我一直认为str(obj)函数只是调用obj.__str__方法,就是这样。但由于某种原因,它还会再次调用__init__的{​​{1}}函数。 有人可以解释一下这种行为以及我如何避免在使用S函数时S.__init__调用C.__str__的结果吗?

1 个答案:

答案 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实例是一个字符串。