关于this question的后续问题:我在python3.5和python3.6上运行了下面的代码,结果截然不同:
class Container:
KEYS = ('a', 'b', 'c')
def __init__(self, a=None, b=None, c=None):
self.a = a
self.b = b
self.c = c
def keys(self):
return Container.KEYS
def __getitem__(self, key):
if key not in Container.KEYS:
raise KeyError(key)
return getattr(self, key)
def __str__(self):
# python 3.6
# return f'{self.__class__.__name__}(a={self.a}, b={self.b}, c={self.c})'
# python 3.5
return ('{self.__class__.__name__}(a={self.a}, b={self.b}, '
'c={self.c})').format(self=self)
data0 = Container(a=1, b=2, c=3)
print(data0)
data3 = Container(**data0, b=7)
print(data3)
如前一个问题所述,这引起了
TypeError: type object got multiple values for keyword argument 'b'
在Python3.6上。但在python 3.5上,我得到了一个例外:
KeyError: 0
此外,如果我不提出KeyError
,而只是在__getitem__
中打印出key
和{
这将打印出int
序列0, 1, 2, 3, 4, ...
。(python 3.5)
所以我的问题是:
两个版本之间有什么不同之处呢?
这些整数是从哪里来的?
更新:正如λuser的注释中所提到的:实现__iter__
将改变Python3.5上的行为,以匹配Python3.6所做的:
def __iter__(self):
return iter(Container.KEYS)
这实际上是在解包自定义映射对象和创建调用方参数期间多个内部操作之间的复杂冲突。因此,如果您想彻底了解其根本原因,我建议您查看源代码。但是,这里有一些提示和起点,您可以查看更多的细节。在
在内部,当您在调用方级别解包时,字节码^{} 会从堆栈中弹出count映射,将它们合并到一个字典中并推送结果。另一方面,带有参数
oparg
is defined as following的操作码的堆栈效果:说到这里,让我们看看一个例子(在Python-3.5中)的字节码,看看这一点在实际中的作用:
^{pr2}$如您所见,在偏移量15处,我们有负责解包的
BUILD_MAP_UNPACK_WITH_CALL
字节码。在现在,它将0作为
key
参数返回给__getitem__
方法,会发生什么情况?在每当解包期间解释器遇到异常,在本例中是
KeyError
,它将停止继续推/弹出流,而不是返回变量的实际值,而是返回堆栈效果,这就是为什么键最初是0的原因,如果每次获得递增结果时都不处理异常(由于堆栈大小)。在现在,如果在Python-3.6中执行相同的反汇编,将得到以下结果:
在创建局部变量(} ,它负责创建元组并使用堆栈中的count项。在
LOAD_FAST
)之前,LOAD_GLOBAL
之后有一个^{在我看来,这就是为什么你没有得到一个键错误,而是得到
TypeError
。因为在创建参数元组的过程中,它遇到了一个重复的名称,因此,正确地返回TypeError
。在相关问题 更多 >
编程相关推荐