为什么我觉得好不方便啊......
比如如下的代码:
1 2 3 4 5 | <span class = "n" >x</span><span class = "o" >=</span><span class = "p" >[</span><span class = "mi" >1</span><span class = "p" >,</span><span class = "mi" >2</span><span class = "p" >,</span><span class = "mi" >3</span><span class = "p" >,</span><span class = "mi" >4</span><span class = "p" >]</span>
<span class = "n" >y</span><span class = "o" >=</span><span class = "n" >x</span>
<span class = "n" >y</span><span class = "p" >[</span><span class = "mi" >0</span><span class = "p" >]</span><span class = "o" >=</span><span class = "mi" >4</span>
<span class = "k" > print </span> <span class = "n" >x</span>
<span class = "o" >>></span> <span class = "n" >x</span> <span class = "o" >=</span> <span class = "p" >[</span><span class = "mi" >4</span> <span class = "mi" >2</span> <span class = "mi" >3</span> <span class = "mi" >4</span><span class = "p" >]</span>
|
登录后复制
回复内容:
挺方便的
object.h
而Python中一切皆来源于此,而这两个宏定义为:
其实可发现PyObject_VAR_HEAD也只是PyObject_HEAD加上一个ob_size,于是Python中,每一个对象都拥有相同的对象头部,于是我们只需要用一个PyObject *就可以引用任意的一个对象,而不论该对象实际是一个什么对象,所以,当内存中存在某个Python对象时,该对象的开始的几个字节的含义一定会符合我们的预期,即PyObject_HEAD。而PyObject_HEAD宏定义中的_PyObject_HEAD_EXTRA其实只是指向_object的一个双向链表,
而ob_refcnt则是引用计数的计数器,而struct _typeobject *obtype则是类型,表示对象类型的类型对象:
通过PyObject_VAR_HEAD可以发现类型其实也是一个对象(一切皆对象),而类型对象的类型则是类型对象所关联的类型:PyType_Type:
而即使如简单的int也是来填满这里的东西:
于是,即使是int也是对象
所以即使你使用
i = 47
j = 47
也是对47的一个引用,而
通过id,可以看出其在内存引用位置相同,而为什么相同?因为为了减少频繁调用开销,使用了small int对象池的技术:
于是只要超过257(包括257),则变了:
所以, m,n虽然都是对258对象的引用,却是不同的内存地址了.
剩下的你就可以继续按照这条路线探寻Python的机制了,而你的问题也基本得到解答了,当你疑惑一些问题时,源码是最好的解释。 :-)
P.S. Python版本: 2.7.8
变量一般有三种风格:
引用式:变量是对象的名字,在运行时可以绑定到任意对象上,比如python。
值式:变量是存储位置的名字,在编译时绑定已经完成,运行时不可以改变,比如c。
混合式:某些类型是值式的,某些类型是引用式的,比如c#的struct和class。
c++比较特殊,基本上可以说是值式的,但是由于有别名存在,所以也有些引用式的特点。
引用式和值式的区别就是在赋值操作上,引用式的赋值是变量名的重新绑定,而值式的赋值是对象的拷贝。
他们两者是可以互相模拟的。引用式的想要有值式的赋值,只需要显式的拷贝或者copy-on-write就可以了。而值式的想要有引用式的赋值只需要使用指针就可以了。
你可能只是习惯了c/c++的值式的风格,还没适应引用式的风格。
看起来可能风格统一比较好。但是实际用起来我觉得c#的混合式是最好用的。
其实现在大部分的语言都是这样的:对象以引用的方式提供给编程者,对象赋值只是多个变量指向同一个对象。
大概只有C/C++的赋值是真的拷贝一份。
a = 258
在Python看来就是:
创建一个PyIntObject对象,值为258;a是一个指向PyObject的指针,将a指向此PyIntObject对象
之所以能这么干就和 @蓝色的答案解释的一样,所有的PyObject都有同样的头部。
在Python中有些对象是可以在原处进行改变的(即可变对象),这种对象包括了列表、字典、集合和一些自定义的对象。而对于整数和字符串等不可变对象是不会存在题主所说的问题。比如:
1 2 3 4 5 6 | <code class = "language-python" ><span class = "o" >>>></span> <span class = "n" >a</span> <span class = "o" >=</span> <span class = "mi" >123</span>
<span class = "o" >>>></span> <span class = "n" >b</span> <span class = "o" >=</span> <span class = "n" >a</span>
<span class = "o" >>>></span> <span class = "n" >a</span> <span class = "o" >=</span> <span class = "mi" >321</span>
<span class = "o" >>>></span> <span class = "k" > print </span> <span class = "n" >a</span><span class = "p" >,</span> <span class = "n" >b</span>
<span class = "mi" >321</span> <span class = "mi" >123</span>
</code>
|
登录后复制
问题的关键是,你写下y=x时,其实并没有新建一个y,而是一个类似c++中引用的机制,具体可以这样看:
>>> x = [1,2,3,4]
>>> y = x
>>> id(x)
43246536L
>>> id(y)
43246536L
所以x和y其实是一个东西。
如果要复制一个list,需要使用
>>> y = list(x)
或者
>>> y = x[:]
你加上“指针”一起理解。
你想深拷贝就用y=x[:]。
其实也没啥不好的,py的对象分为可变和不可变两种,只要理解了这一点一切都很清晰。
id()