PHP 和 Python 语言中引用是非常重要的一个概念,这个概念对于理解两门语言非常重要,会让你更深刻的认识到两门语言的本质,所以简单的谈一谈。先说结论,Python 语言中一切都是对象,理解起来觉得更合理;PHP 则是混搭机制,对于引用的处理让人很困惑。
Python 中的引用
在 Python 中一切都是对象,整型值、字典、元祖都是对象,连函数、类本身也是对象,定义一个函数,即使程序没有调用,可在执行的时候,其本身也是一个对象。
对象分三部分,身份,类型和值,身份就是一个变量标识符,由 Python 解析器维护,可以通过 id() 函数获取变量编号。而值是具体存储的内容(在内存中有一个地址),通过变量名将具体的对象关联起来(可以说是引用)。当 Python 发现值(内容)没有变量引用了,就会进行回收。
结论在 Python 中,变量赋值和函数参数传递都是引用关系。
(1)看看一个赋值或者函数参数传递发生了什么:
m = "hello"
n = m
上述语句 m 和 n 变量其实是引用的同一块内容,m 和 n 不分主次,对它们进行查询,就是对 "hello" 这个内容体进行查询。
(2)当修改一个变量值的时候发生了什么?
这里要区分下可变对象和不可变对象,可变对象表示变量名赋值就是直接在对应的内容体进行操作;而不可变对象表示变量对应的内容体是只读的(假如重新赋值实际是从新开辟一块内存区)。
在 Python 中整型和字符串类型是不可变对象,其他是可变对象,看几个例子:
m = "hello"
n = m
n = "good"
这段代码中对 n 进行重新赋值操作的时候,不是在 "hello" 这个内容体上进行修改,而是重新创建了一个内容体"good",n 变量则引用了它。
m = []
n = m
n.append(10)
print m
对于可变对象来说,上述代码中,不管对 m 还是 n 进行更新,影响的是同一块内容体。
下面看几个例子,来更深刻的理解。
Python 中的函数传递
理解了变量赋值,其实也理解了函数传递,原理是一致的,就是变量和对象的引用关系(绑定)。
(1)简单的函数传递
def fun(m):
m.append(10)
o = []
fun(o)
print o
o 这个实参和函数内的 m 形参引用的是同一个内容体,这个普通的赋值其实是一样的处理机制。
(2)复杂点的例子
class classobj(object):
v = []
def fun(o) :
o.v.append(1)
x = o.v
return x
o = classobj()
s = fun(o)
print s ,o.v
o.v.append(2)
print s ,o.v
s.append(3)
print s ,o.v
上面的代码中,不管是对自定义类的实例属性(o.v)进行操作,还是对函数返回的变量(s)进行操作,引用的都是同一个内容存储体。
(3)更复杂的例子
上面说了,函数定义过程中一个形式参数,就算函数没有被调用,在函数这个命名空间内,这个形式参数也是存在的(由 Python 维护)。
def fun(s=[]):
s.append(10)
return s
o1 = fun()
print o1
o2 = fun()
print o1,o2
可以看下结果,两次函数调用,函数体内部指向的对象都是同一个,不管函数调用多少次,其形参的 id 值是一样的,其引用的内容体一直存在。
PHP 中的引用
PHP 刚出来的时候,为了简单,任何的赋值、函数传递都是拷贝方式的,虽然可能很耗费空间并降低了处理速度,但是对于编程来说却降低了难度。比如下面的代码
$a = "china";
$b = $a ;
$b
和 $a
完全没有关系,操作的是两块内存地址,相当与 $b
先读取 $a
的内容,然后自己存储一份。
PHP 作为一门语言,一直在演化,也有了引用的概念,但是需要通过&
符号来显示的指明,属于一个语法糖。比如说下面的代码:
$a = "china";
$b = &$a ;
$b = "world";
echo $a . "_" . $b ;
$a = "good";
echo $a . "_" . $b ;
通过&
符号,则代表$a
和$b
指向的是同一块内容。
引用的好处其实很简单,第一是节省内存空间,比如在函数调用中,假如某个参数变量对应的值有 1K,假如不使用引用传递,则在函数执行的时候,就要额外拷贝 1K 的内容,这样就节省了空间;第二就是提高处理速度,内存的拷贝必然会消耗 CPU 处理时间。
使用引用固然高效,但是也要留心,因为一不小心可能就破坏了原有变量的值,尤其在不理解引用的情况下,需要谨慎使用。
PHP 中的引用很困惑
手册上说,PHP 可以通过三种方式使用引用
- 赋值
- 对象赋值或者函数传递对象
- 引用返回
说明下,目前的 PHP 版本,对象赋值或者函数传递对象,默认就是引用方式,不用加 &
符号,其实我建议加,因为这样语言有一致性。
重点说下引用返回,上面 Python 例子中,函数 return 返回一个变量,当实际调用的时候,函数调用赋值给一个变量,当这个变量的值改变后,函数体里面的变量值也是同步修改的。比如:
class c(object):
o = []
def __init__(self, arg):
super(c, self).__init__()
self.arg = arg
def r(self):
return self.o
obj = c(10)
objo = obj.r()
print objo
objo.append(10)
print obj.r()
比如函数调用返回的 objo 变量(应用 c 类中的变量)值修改后,c 这个类中的实例变量 o 也会同步修改。而在 PHP 中是做不到的,比如:
class c
{
public $o = '10';
public function r()
{
return $this->o;
}
}
$obj = new c;
$objo = $obj->r();
print $objo ;
$objo = "good" ;
print $obj->r();
上面的代码 $objo 修改后,不会影响到实例的变量,可以通过引用返回解决这问题,但是语法让人觉得难以理解。
class c
{
public $o = '10';
public function &r()
{
return $this->o;
}
}
$obj = new c;
$objo = &$obj->r();
print $objo ;
$objo = "good" ;
print $obj->r();
这里面使用两个 &
符号,函数中的 &
符号代表函数返回是个引用,第二个 &
符号表示不是赋值而是变量引用。
总体来说,Python 中的引用和它的对象机制是一体的,而 PHP 中则是变相实现的引用。