深入理解Python对象机制 [英] Deeper understanding of Python object mechanisms

查看:56
本文介绍了深入理解Python对象机制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

d1 = {'id':5432,'name': 'jlandercy','宠物':{'安迪':{'类型':'猫','年龄': 3.5},'射线': {'类型':'狗','年龄': 6.5}},'类型':str,'complex': (5432, 6.5, 'cat', str),'list': ['牛奶', '巧克力', '黄油']}

1) 不可变原子对象是单例

无论我创建一个新整数的方式是什么:

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

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

 d1['id'] 是 n1...

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

2) 不可变和可迭代对象可能是单例...

先前的观察也适用于 str,它们是 immutableiterable.以下所有变量:

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

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

...但不完全是...

元组也是 immutableiterable,但它们的行为不像字符串.众所周知,神奇的空元组是一个单例:

() 是 ()

但其他元组不是.

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'] = 1111kwargs['pets']['andy'] = 无

将通过双重解引用(** 运算符)接收我们的试用字典的一级成员将被复制,但最深将通过引用传递.

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

print(d1)f1(**d1)打印(d1)

它返回:

{'complex': (5432, 6.5, 'cat', ),'id':5432,'list': ['牛奶', '巧克力', '黄油'],'name': 'jlandercy','pets': {'andy': {'age': 3.5, 'type': 'cat'},'ray': {'age': 6.5, 'type': 'dog'}},'类型':}{'complex': (5432, 6.5, 'cat', <class 'str'>),'id':5432,'list': ['牛奶', '巧克力', '黄油'],'name': 'jlandercy','pets':{'andy':无,'ray':{'age':6.5,'type':'dog'}},'类型':}

字典d1 已经被函数f1 修改过,但还没有完全修改.成员id'被保留了,因为我们做了一个副本,但是成员pets也是一个字典,浅拷贝没有复制它,然后它被修改了/p>

此行为类似于 dict 对象的 copy.copy 行为.我们需要 copy.deepcopy 来递归完整地复制对象.

我的要求是:

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

    1. 不可变原子对象是单例

    2. Immutable 和 Iterable 对象可能是单例对象,但不完全是它们的哈希值相等

    3. 通过双重解引用传递字典作为它的浅拷贝

  • 这些行为是否在某处有详细记录?
  • 对于每种情况都说明正确的属性 &行为.

解决方案

不可变原子对象是单例

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

  • (-6, 256] 范围内的整数会被缓存,当对这些整数提出新请求时,将返回已经存在的对象.超出该范围的数字会进行常量折叠解释器在编译期间重新使用常量作为轻微优化. 在创建新的 PyLong 对象的部分对此进行了记录.

    此外,有关这些的讨论,请参阅以下内容:

  • 字符串字面量在编译成字节码的过程中会像整数一样被实习.然而,管理这个的规则并不像整数那么简单:只考虑由某些字符组成的特定大小的字符串.我不知道文档中的任何部分指定了这一点,您可以通过阅读 查看行为这里.

  • 例如浮点数,它可以被认为是原子的"(即使在 Python 中该术语没有您认为的含义),但没有单例:

    i = 1.0j = 1.0i 是 j # 假

    当然,它们仍然会受到不断折叠的影响.正如您通过阅读所看到的:'is' 运算符与浮点数发生意外

<块引用>

Immutable 和 Iterable 对象可能是单例,但不完全是它们的哈希值相等

空的不可变集合是signletons;这又是一个在 Python 参考中找不到的实现细节,只有查看源代码才能真正发现.

查看这里的实现:当 '[] is []' 和 '{} is {}' 返回 False 时,为什么 '() is ()' 返回 True?

<块引用>

通过双重解引用传递字典作为它的浅拷贝.

是的.尽管该术语不是双重解引用,但它是解压缩的.

<块引用>

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

那些被视为实现细节的内容不需要以您查找 max 函数文档的方式进行记录.如果做出这样的决定,这些是很容易改变的具体事情.

I would like to better understand Python 3.x data model. But I do not find complete and precise explanation of Python Object behaviours.

I am looking for references, it would be great if every case that I show below could be linked to a Python API reference or PEP or anything else valuable. Thank you further for your wise advises...

Let say we have some complex Python structure for testing purposes:

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) Immutable atomic objects are singletons

Whatever the way I create a new integer:

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

No new copy of this number is created, instead it points towards the same object as d1['id']. More concisely

 d1['id'] is n1
 ...

They all do have the same id, I cannot create a new instance of int with value 5432, therefore it is a singleton.

2) Immutable and Iterable objects might be singletons...

Previous observation also works for str, which are immutable and iterable. All following variables:

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

Point towards the copy initially created d1['name']. String are also singletons.

...but not exactly...

Tuple are also immutable and iterable, but they do not behave like string. It is know that the magic empty tuple is a singleton:

() is ()

But other tuples are not.

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

...instead they hash equally

They do not have the same id:

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

But all items within those two structures are atomic, so they point towards same instances. The important point is, both structures hash the same way:

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

So they can be used as dictionary keys. This is even true for nested tuples:

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

They do have the same hash.

3) Passing dictionary by double dereferencing works as shallow copy of it

Lets define the following function:

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

Which will receive our trial dictionary by double dereferencing (** operator) first degree members will be copied, but deepest will be passed by reference.

Output of this simple program, illustrates it:

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

It returns:

{'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'>}

The dictionary d1 has been modified by function f1, but not completely. Member id'is kept back because we worked on a copy, but member pets is also a dictionary and the shallow copy did not copy it, then it has been modified.

This behaviour is similar to copy.copy behaviour for dict object. Where we need copy.deepcopy to have a recursive and complete copy of object.

My requests are:

  • Are my observations correctly interpreted?

    1. Immutable atomic objects are singletons

    2. Immutable and Iterable objects might be singletons but not exactly instead they hash equally

    3. Passing dictionary by double dereferencing works as shallow copy of it

  • Are those behaviours well documented somewhere?
  • For each case states correct properties & behaviours.

解决方案

Immutable atomic objects are singletons

Nope, some are and some aren't, this is a detail of the CPython implementation.

  • Integers in the range (-6, 256] are cached and when a new request for these is made the already existing objects are returned. Numbers outside that range are subject to constant folding where the interpreter re-uses constants during compilation as a slight optimization. This is documented in the section on creating new PyLong objects.

    Also, see the following for a discussion on these:

  • Strings literals are subject to interning during the compilation to bytecode as do ints. The rules for governing this are not as simplistic as for ints, though: Strings under a certain size composed of certain characters are only considered. I am not aware of any section in the docs specifying this, you could take a look at the behavior by reading here.

  • Floats, for example, which could be considered "atomic" (even though in Python that term doesn't have the meaning you think) there are no singletons:

    i = 1.0
    j = 1.0
    i is j # False
    

    they are still of course subject to constant folding. As you can see by reading: 'is' operator behaves unexpectedly with floats

Immutable and Iterable objects might be singletons but not exactly instead they hash equally

Empty immutables collections are signletons; this is again an implementation detail that can't be found in the Python Reference but truly only discovered if you look at the source.

See here for a look at the implementation: Why does '() is ()' return True when '[] is []' and '{} is {}' return False?

Passing dictionary by double dereferencing works as shallow copy of it.

Yes. Though the term isn't double dereferencing, it is unpacking.

Are those behaviours well documented somewhere?

Those that are considered an implementation detail needn't be documented in the way you'd find documentation for the max function for example. These are specific things that might easily change if the decision is made so.

这篇关于深入理解Python对象机制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆