切记python 中所有的变量都是引用。
举例d1 = dict() ,意思是,我们首先创建了一个类型为 dict 的 (匿名)对象,接着创建了一个变量 d1,然后让 d1 “引用” 这个 dict 的对象,如果类比于 C 语言的话,可以理解为 dict *d1 = new dict()。d1 它只是一个引用。
这里和 C 语言存在一个巨大的差异。这个差异是什么呢?我们知道 C 语言提供了解引用的操作符 (*),这样程序员可以通过 *d1 来获得这个对象的本体,而作为脚本语言的 python,却并没有(也没有必要)提供这个功能。因此,除了通过 “引用” (换句话说,就是指针)的方式之外,我们无从触摸到这个对象的本体。
想象一下,如果 C 语言中没有了对指针做 * 的运算,我们要如何来修改某个对象呢?比如:
char c = 'A';
char *p = &c;
我们手上只有一个 p,要怎么修改 *p 的内容呢?毫无疑问,我们没有办法。而这就正是 python 里的现实情况。char 就是属于 immutable 的类型,我们再举一个 C++ 的例子:
class foo {
char m_c;
public:
foo () : m_c('A') {}
void set (char c) {
m_c = c;
}
char get () {
return m_c;
}
};
foo *p = new foo();
std::cout << "foo: " << p->get() << std::endl;
p->set('B');
std::cout << "foo: " << p->get() << std::endl;
在上面这个例子里,如果说我们仍然没有对指针的 * 运算,但我们依然可以改变 *p 的内容,这是因为 p 所指向的对象,具有修改自身的能力(提供了相关的方法)。这种类型,在 python 里面,就是 mutable 的。我们常见的 list,dict 等等,就属于此类。而 python 的字符串类,则属于 immutable 的。
这里有两个地方需要注意:
1. 变量赋值,实际上并没有产生对象的拷贝,仅仅是 “引用” (指针) 的拷贝。比如:
d1 = {'1' : 1, '2' : 2, }
d2 = d1
d2['3'] = 3
for k in d1:
print k, ' => ', d1[k]
上面的代码,我们会发现,d1['3'] 也存在了,而且值就是 3。所以我们可以把 python 里面的所有变量都想象成指针;所有的变量对变量的 = 操作都想象成指针的赋值,所有的 . 操作都想象成 ->,那么则庶几不会有问题了。
2. 通过显式构造函数的方法来拷贝对象,还是上面的例子,如果我们用:
d1 = {'1' : 1, '2' : 2, }
d2 = dict(d1)
d2['3'] = 3
for k in d1:
print k, ' => ', d1[k]
这样,d2 和 d1 就毫无关系了,这种情况下,dict(d1) 产生了一个新的(匿名)dict 对象,同时将 d2 变成对它的引用。
如果我们把所有 x = Type(var) 的赋值想象成 x = new Type(var) 就相当于抓住了事物的本质。