更深入地理解Python对象机制

时间:2016-12-02 10:44:53

标签: python python-3.x object reference

我想更好地理解Python 3.x数据模型。但我没有找到Python对象行为的完整而准确的解释。

我正在寻找引用,如果我在下面展示的每个案例都可以链接到Python API参考或PEP或任何其他有价值的东西,那就太棒了。再次感谢您的明智建议......

假设我们有一些复杂的Python结构用于测试目的:

d1 = {
    'id': 5432
   ,'name': 'jlandercy'
   ,'pets': {
        'andy': {
            'type': 'cat'
           ,'age': 3.5
        }
       ,'ray': {
            'type': 'dog'
           ,'age': 6.5
        }
    }
   ,'type': str
   ,'complex': (5432, 6.5, 'cat', str)
   ,'list': ['milk', 'chocolate', 'butter']
}

1)不可变的原子对象是单身

无论我创建一个新整数的方式如何:

n1 = 5432
n2 = int(5432)
n3 = copy.copy(n1)
n4 = copy.deepcopy(n1)

不会创建此数字的新副本,而是指向与d1['id']相同的对象。更简洁

 d1['id'] is n1
 ...

它们都具有相同的id,我无法创建值为5432的新int实例,因此它是 singleton

2)不可变和可变对象可能是单身......

之前的观察也适用于str,它们是不可变可迭代。以下所有变量:

s1 = 'jlandercy'
s2 = str('jlandercy')
s3 = copy.copy(s1)
s4 = copy.deepcopy(s1)

指向最初创建的副本d1['name']。字符串也是单例

......但不完全......

元组也是 immutable iterable ,但它们的行为不像字符串。众所周知,神奇的空元组是 singleton

() is ()

但是其他元组不是。

t1 = (5432, 6.5, 'cat', str)

...相反,他们同样哈希

他们没有相同的id

id(d1['complex']) != id(t1)

但是这两个结构中的所有项都是原子的,因此它们指向相同的实例。重要的是,两个结构hash的方式相同:

hash(d1['complex']) == hash(t1)

因此它们可以用作字典键。对于嵌套元组,这甚至是正确的:

t2 = (1, (2, 3))
t3 = (1, (2, 3))

它们具有相同的hash

3)通过双重解除引用传递字典作为它的浅层副本

让我们定义以下功能:

def f1(**kwargs):
    kwargs['id'] = 1111
    kwargs['pets']['andy'] = None

将通过双重解除引用(**运算符)接收我们的试用词典第一学位成员将被复制,但最深的将通过引用传递。

这个简单程序的输出说明了它:

print(d1)
f1(**d1)
print(d1)

它返回:

{'complex': (5432, 6.5, 'cat', <class 'str'>),
 'id': 5432,
 'list': ['milk', 'chocolate', 'butter'],
 'name': 'jlandercy',
 'pets': {'andy': {'age': 3.5, 'type': 'cat'},
          'ray': {'age': 6.5, 'type': 'dog'}},
 'type': <class 'str'>}

{'complex': (5432, 6.5, 'cat', <class 'str'>),
 'id': 5432,
 'list': ['milk', 'chocolate', 'butter'],
 'name': 'jlandercy',
 'pets': {'andy': None, 'ray': {'age': 6.5, 'type': 'dog'}},
 'type': <class 'str'>}

字典d1已由函数f1修改,但并非完全修改。成员id'被保留,因为我们处理了一个副本,但成员pets也是一个字典而浅层副本没有复制它,然后它已被修改。

此行为类似于copy.copy对象的dict行为。我们需要copy.deepcopy来获得对象的递归和完整副本。

我的要求是:

  • 我的观察是否正确解释了?

    1. 不可变的原子对象是单身

    2. 不可变和可迭代的对象可能是单例,但并不完全相反,它们是平等的

    3. 通过双重解除引用传递字典作为它的浅层副本

  • 这些行为是否记录在某处?
  • 对于每种情况都说明正确的属性和的行为。

1 个答案:

答案 0 :(得分:1)

  

不可变的原子对象是单身

不,有些是,有些不是,这是CPython实现的细节。

  • 缓存(-6, 256]范围内的整数,当对这些整数发出新请求时,将返回已存在的对象。该范围之外的数字受到常量折叠的影响,其中解释器在编译期间重新使用常量作为轻微优化。有关创建新PyLong个对象的部分中的This is documented

    另外,请参阅以下内容以讨论这些内容:

  • 在编译到字节码期间,字符串文字会像int一样进行实习。但是,管理它的规则并不像int中那样简单:只考虑由特定字符组成的特定大小的字符串。我不知道文档中的任何部分指定了这一点,您可以通过阅读here来查看行为。

  • 浮动,例如,可以考虑&#34;原子&#34; (即使在Python中,这个术语并没有你想到的意思),也没有单身人士:

    i = 1.0
    j = 1.0
    i is j # False
    

    他们当然仍然需要不断折叠。正如您所看到的那样:'is' operator behaves unexpectedly with floats

  

不可变和可迭代的对象可能是单例,但并不完全相反,它们是同等的

空的immutables集合是signletons;这又是一个实现细节,无法在Python参考中找到,但只有在查看源代码时才会发现。

请参阅此处了解实施情况:Why does '() is ()' return True when '[] is []' and '{} is {}' return False?

  

通过双重解除引用传递字典作为它的浅层副本。

是。虽然该术语不是双重解除引用,但它正在解包。

  

这些行为是否记录在某处?

那些被视为实施细节的内容无需以您查找max函数文档的方式进行记录。如果做出决定,这些是可能容易改变的具体事情。