哪些类型的 Python 对象是用引用初始化的,哪些不是?
Which types of Python objects are initialized with a reference, and which are not?
我在 Windows 的 Python 3.7 中玩 sys.getrefcount
。我尝试了以下方法:
>>> import sys
>>> x = "this is an arbitrary string"
>>> sys.getrefcount(x)
2
我理解其中一个引用是x
,另一个是sys.getrefcount
内部使用的参数。无论 x
初始化为哪种类型,这似乎都有效。但是,当我在通过之前不分配时,我注意到一些奇怪的行为:
>>> import sys
>>> sys.getrefcount("arbitrary string")
2
>>> sys.getrefcount(1122334455)
2
>>> sys.getrefcount(1122334455+1)
2
>>> sys.getrefcount(frozenset())
2
>>> sys.getrefcount(set())
1
>>> sys.getrefcount(object())
1
>>> sys.getrefcount([])
1
>>> sys.getrefcount(lambda x: x)
1
>>> sys.getrefcount(range(1122334455))
1
>>> sys.getrefcount(dict())
1
>>> sys.getrefcount(())
8341
>>> sys.getrefcount(tuple())
8340
>>> sys.getrefcount(list("arbitrary string"))
1
>>> sys.getrefcount(tuple("arbitrary string"))
1
>>> sys.getrefcount(("a", "r", "b", "i", "t", "r", "a", "r", "y", " ", "s", "t", "r", "i", "n", "g"))
2
这是怎么回事?似乎不可变类型有两个引用而可变类型只有一个?为什么看起来有些对象在传递之前就已赋值,而另一些对象只有一个引用作为参数?
这和str
/int
/tuple
拘禁有关系吗?
编辑:一个更直接的问题:为什么选择像 frozenset()
这样的不可变类型在构造时有引用,而像 set()
这样的可变类型没有?我单独理解为什么您可能会选择保留这个全局范围的参考或不全面,但为什么会出现差异?
一个有趣的问题所以这是一个有趣的问题 read。
你应该尝试 getrefcount(2)
,对我来说它返回了 93,这意味着 CPython 为同一个内存地址保留了 93 个引用,保持第二个,所以它不必分配再次这样做,因为它是不可变的,所以这样做完全没问题。
现在让我们尝试两种不同的方法:
# first
getrefcount(set()) # returns 1
# second
s = set()
getrefcount(s) # returns 2
因为它是可变类型,所以当您创建可变类型 (set()
) 时,它的行为是不同的,它将在内存中分配它并且只有一个对它的引用,在这一行结束后立即被删除.但是在第二个我们定义变量并分配它,在计算引用时我们有 s
使用的一个和函数 getrefcount
.
使用的一个
而在 Python tuples are immutable 中,这就是它 returns 一个巨大数字的原因,CPython 保留了大量对空元组的引用。
随着我了解的更多,回答我自己的问题。
区别与 python 字节码对象的格式有关。 sys.getrefcount("arbitrary string")
的字节码如下:
>>> dis.dis('sys.getrefcount("arbitrary string")')
1 0 LOAD_NAME 0 (sys)
2 LOAD_METHOD 1 (getrefcount)
4 LOAD_CONST 0 ('arbitrary string')
6 CALL_METHOD 1
8 RETURN_VALUE
在这里,LOAD_CONST 操作码不会从头开始构建新字符串,它只是从代码对象的常量元组中加载一个字符串。该元组是持有额外参考的内容:
>>> f = lambda: sys.getrefcount("arbitrary string")
>>> f.__code__.co_consts
(None, 'arbitrary_string')
考虑到这一点,一些例子是有道理的:
>>> import sys
# the string is stored in co_consts
>>> sys.getrefcount("arbitrary string")
2
# the integer is store in co_consts
>>> sys.getrefcount(1122334455)
2
# this addition is constant-folded and the result is stored in co_consts
>>> sys.getrefcount(1122334455+1)
2
# Tuples of constants are folded into one big constant:
>>> sys.getrefcount(("a", "r", "b", "i", "t", "r", "a", "r", "y", " ", "s", "t", "r", "i", "n", "g"))
2
同时,以下对象不能存储在 co_consts 中,因为它们每次都必须重建,因为它们要么是可变的,要么依赖于必须查看的某个函数的函数调用向上:
>>> sys.getrefcount(set()) # construct a new set at call time
1
>>> sys.getrefcount(object()) # construct a new object at call time
1
>>> sys.getrefcount([]) # construct a new list at call time
1
>>> sys.getrefcount(lambda x: x) # construct a new function at call time
1
# construct a new range object at call time.
# We could do something dumb like range = abs,
# so this can't be constant-folded.
>>> sys.getrefcount(range(1122334455))
1
>>> sys.getrefcount(dict()) # construct a new dict at call time
1
>>> sys.getrefcount(list("arbitrary string")) # construct a new list at call time
1
# Construct a new tuple at call time.
# We could do something dumb like tuple=list
# so this can't be constant-folded.
>>> sys.getrefcount(tuple("arbitrary string"))
1
最后,对象的第三类是那些使用某种缓存或保留的对象,在这种情况下,当构造一个新对象时,Python 可以以某种方式欺骗并为您提供一个已经存在的对象.
# There is only one empty frozenset that gets re-used.
>>> sys.getrefcount(frozenset())
2
# There is only one empty tuple that gets re-used
# whenever someone requests an empty tuple
>>> sys.getrefcount(())
8341
# The same thing, but that tuple does get stored
# in co_consts because the name `tuple` could be rebound.
>>> sys.getrefcount(tuple())
8340
要验证关于 frozensets 的断言,请注意
一个空的 frozenset 的引用计数增加
当建造新的时:
>>> sys.getrefcount(frozenset())
2
>>> x, y, z = frozenset(), frozenset(), frozenset()
>>> sys.getrefcount(frozenset())
5
我在 Windows 的 Python 3.7 中玩 sys.getrefcount
。我尝试了以下方法:
>>> import sys
>>> x = "this is an arbitrary string"
>>> sys.getrefcount(x)
2
我理解其中一个引用是x
,另一个是sys.getrefcount
内部使用的参数。无论 x
初始化为哪种类型,这似乎都有效。但是,当我在通过之前不分配时,我注意到一些奇怪的行为:
>>> import sys
>>> sys.getrefcount("arbitrary string")
2
>>> sys.getrefcount(1122334455)
2
>>> sys.getrefcount(1122334455+1)
2
>>> sys.getrefcount(frozenset())
2
>>> sys.getrefcount(set())
1
>>> sys.getrefcount(object())
1
>>> sys.getrefcount([])
1
>>> sys.getrefcount(lambda x: x)
1
>>> sys.getrefcount(range(1122334455))
1
>>> sys.getrefcount(dict())
1
>>> sys.getrefcount(())
8341
>>> sys.getrefcount(tuple())
8340
>>> sys.getrefcount(list("arbitrary string"))
1
>>> sys.getrefcount(tuple("arbitrary string"))
1
>>> sys.getrefcount(("a", "r", "b", "i", "t", "r", "a", "r", "y", " ", "s", "t", "r", "i", "n", "g"))
2
这是怎么回事?似乎不可变类型有两个引用而可变类型只有一个?为什么看起来有些对象在传递之前就已赋值,而另一些对象只有一个引用作为参数?
这和str
/int
/tuple
拘禁有关系吗?
编辑:一个更直接的问题:为什么选择像 frozenset()
这样的不可变类型在构造时有引用,而像 set()
这样的可变类型没有?我单独理解为什么您可能会选择保留这个全局范围的参考或不全面,但为什么会出现差异?
一个有趣的问题所以这是一个有趣的问题 read。
你应该尝试 getrefcount(2)
,对我来说它返回了 93,这意味着 CPython 为同一个内存地址保留了 93 个引用,保持第二个,所以它不必分配再次这样做,因为它是不可变的,所以这样做完全没问题。
现在让我们尝试两种不同的方法:
# first
getrefcount(set()) # returns 1
# second
s = set()
getrefcount(s) # returns 2
因为它是可变类型,所以当您创建可变类型 (set()
) 时,它的行为是不同的,它将在内存中分配它并且只有一个对它的引用,在这一行结束后立即被删除.但是在第二个我们定义变量并分配它,在计算引用时我们有 s
使用的一个和函数 getrefcount
.
而在 Python tuples are immutable 中,这就是它 returns 一个巨大数字的原因,CPython 保留了大量对空元组的引用。
随着我了解的更多,回答我自己的问题。
区别与 python 字节码对象的格式有关。 sys.getrefcount("arbitrary string")
的字节码如下:
>>> dis.dis('sys.getrefcount("arbitrary string")')
1 0 LOAD_NAME 0 (sys)
2 LOAD_METHOD 1 (getrefcount)
4 LOAD_CONST 0 ('arbitrary string')
6 CALL_METHOD 1
8 RETURN_VALUE
在这里,LOAD_CONST 操作码不会从头开始构建新字符串,它只是从代码对象的常量元组中加载一个字符串。该元组是持有额外参考的内容:
>>> f = lambda: sys.getrefcount("arbitrary string")
>>> f.__code__.co_consts
(None, 'arbitrary_string')
考虑到这一点,一些例子是有道理的:
>>> import sys
# the string is stored in co_consts
>>> sys.getrefcount("arbitrary string")
2
# the integer is store in co_consts
>>> sys.getrefcount(1122334455)
2
# this addition is constant-folded and the result is stored in co_consts
>>> sys.getrefcount(1122334455+1)
2
# Tuples of constants are folded into one big constant:
>>> sys.getrefcount(("a", "r", "b", "i", "t", "r", "a", "r", "y", " ", "s", "t", "r", "i", "n", "g"))
2
同时,以下对象不能存储在 co_consts 中,因为它们每次都必须重建,因为它们要么是可变的,要么依赖于必须查看的某个函数的函数调用向上:
>>> sys.getrefcount(set()) # construct a new set at call time
1
>>> sys.getrefcount(object()) # construct a new object at call time
1
>>> sys.getrefcount([]) # construct a new list at call time
1
>>> sys.getrefcount(lambda x: x) # construct a new function at call time
1
# construct a new range object at call time.
# We could do something dumb like range = abs,
# so this can't be constant-folded.
>>> sys.getrefcount(range(1122334455))
1
>>> sys.getrefcount(dict()) # construct a new dict at call time
1
>>> sys.getrefcount(list("arbitrary string")) # construct a new list at call time
1
# Construct a new tuple at call time.
# We could do something dumb like tuple=list
# so this can't be constant-folded.
>>> sys.getrefcount(tuple("arbitrary string"))
1
最后,对象的第三类是那些使用某种缓存或保留的对象,在这种情况下,当构造一个新对象时,Python 可以以某种方式欺骗并为您提供一个已经存在的对象.
# There is only one empty frozenset that gets re-used.
>>> sys.getrefcount(frozenset())
2
# There is only one empty tuple that gets re-used
# whenever someone requests an empty tuple
>>> sys.getrefcount(())
8341
# The same thing, but that tuple does get stored
# in co_consts because the name `tuple` could be rebound.
>>> sys.getrefcount(tuple())
8340
要验证关于 frozensets 的断言,请注意 一个空的 frozenset 的引用计数增加 当建造新的时:
>>> sys.getrefcount(frozenset())
2
>>> x, y, z = frozenset(), frozenset(), frozenset()
>>> sys.getrefcount(frozenset())
5