http://blog.csdn.net/longshenlmj/article/details/13773977
函数参数传递本质上和变量整体复制一样,只是两个变量分别为形参a和实参b。那么,a=b后,a变了,b值是否跟着变呢?这取决于对象内容可变不可变
首先解释一下,什么是Python对象的内容可变不可变?
python的变量是无类型的,如n=1 #变量n无类型(n相当于指针),其指向int数据类型的值,这个值是int类型。
所以,python中,strings, tuples元祖, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。
举个列子,
不可变:如,a=5后,a=10,这里实际是新生成一个int值对象10,再让a指向它,而5被丢弃,不是改变a的值,相当于新生成了a。
可变:如,la=[1,2,3,4]后,la[2]=5则是将list la的第二个元素值更改,本身la没有动,只是其内部的一部分值被修改了。
那么,python函数的参数传递:
可变类型,则类似c++的引用,如list、dict。如fun(la),则是将la真正的传过去,修改后fun外部的la也会受影响
而不可变类型,则类似c++的值传递,如int。如fun(a),传递的只是a的值,没有影响a对象本身。比如在fun(a)内部修改a的值,只是修改另一个复制的对象,不会影响a本身。
同样的道理,python变量复制也是一样,a=b:
变量间复制,可变对象是引用,不可变是值copy(新生成值空间,不是变量对象空间)
样例代码如下:
a={1:'a',2:'b',3:'c'}
b=a
a[4]='d'
print a,b
#输出:{1: 'a', 2: 'b', 3: 'c', 4: 'd'} {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
a=10
b=a
a=6
print a,b
#输出: 6 10
==============================================
http://www.cnblogs.com/loleina/p/5276918.html
def foo(arg): print(id(arg)) arg = 2 print(id(arg)) print (arg) a = 1 print(id(a)) foo(a) print(a)
输出:
31805800
31805800
31805776
2
1
https://foofish.net/python-function-args.html
变量与对象
Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象。而变量是对象的一个引用(又称为名字或者标签),对象的操作都是通过引用来完成的。例如,[]
是一个空列表对象,变量 a
是该对象的一个引用
a = []
a.append(1)
在 Python 中,「变量」更准确叫法是「名字」,赋值操作 =
就是把一个名字绑定到一个对象上。就像给对象添加一个标签。
a = 1
整数 1 赋值给变量 a 就相当于是在整数1上绑定了一个 a 标签。
a = 2
整数 2 赋值给变量 a,相当于把原来整数 1 身上的 a 标签撕掉,贴到整数 2 身上。
b = a
把变量 a 赋值给另外一个变量 b,相当于在对象 2 上贴了 a,b 两个标签,通过这两个变量都可以对对象 2 进行操作。
变量本身没有类型信息,类型信息存储在对象中,这和C/C++中的变量有非常大的出入(C中的变量是一段内存区域)
函数参数
Python 函数中,参数的传递本质上是一种赋值操作,而赋值操作是一种名字到对象的绑定过程,清楚了赋值和参数传递的本质之后,现在再来分析前面两段代码。
def bar(args): args.append(1) b = [] print(b)# 输出:[] print(id(b)) # 输出:4324106952 bar(b) print(b) # 输出:[1] print(id(b)) # 输出:4324106952
def bad_append(new_item, a_list=[]):
a_list.append(new_item)
return a_list
这段代码是初学者最容易犯的错误,
用可变(mutable)对象作为参数的默认值。函数定义好之后,默认参数 a_list
就会指向(绑定)到一个空列表对象,每次调用函数时,都是对同一个对象进行 append 操作。
因此这样写就会有潜在的bug,同样的调用方式返回了不一样的结果。
>>> print bad_append('one')
['one']
>>> print bad_append('one')
['one', 'one']
而正确的方式是,把参数默认值指定为None
def good_append(new_item, a_list=None): if a_list is None: a_list = []
a_list.append(new_item)
return a_list
参考:http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
=========================
from ctypes import * import os.path import sys def test(c): print "test before " print id(c) c+=2 print "test after +" print id(c) return c def printIt(t): for i in range(len(t)): print t[i] if __name__=="__main__": a=2 print "main before invoke test" print id(a) n=test(a) print "main afterf invoke test" print a print id(a)
输出:
>>>
main before invoke test
39601564
test before
39601564
test after +
39601540
main afterf invoke test
2
39601564
如果还不能理解,先看下面例子
>>> a=1
>>> b=1
>>> id(a)
40650152
>>> id(b)
40650152
>>> a=2
>>> id(a)
40650140
结论:python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。这种方式相当于传值和传引用的一种综合。
如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象--相当于通过“传值'来传递对象。
=================
http://www.cnblogs.com/Richardzhu/p/4723750.html
Python中的对象之间赋值时是按引用传递的,如果需要拷贝对象,需要使用标准库中的copy模块。
1、copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。
2、copy.deepcopy 深拷贝 拷贝对象及其子对象>>> import copy
>>> a = [1,2,3,4,['a','b']] #原始对象
>>> b = a #赋值,传对象的引用
>>> c = copy.copy(a) //一旦拷贝之后,c就是一个完全独立的对象,对源对象a的操作并不会影响c。但对于浅拷贝不会拷贝a对象的内部的子对象,对子对象依然是引用,所以a[4].append('c')也会影响c
>>> d = copy.deepcopy(a) //深拷贝,父对象,子对象都是完全独立的对象,不再受源对象的任何影响
>>> a.append(5) >>> a[4].append('c')
>>> print 'a=',a a= [1, 2, 3, 4, ['a', 'b', 'c'], 5] >>> print 'b=',b b= [1, 2, 3, 4, ['a', 'b', 'c'], 5] >>> print 'c=',c c= [1, 2, 3, 4, ['a', 'b', 'c']] >>> print 'd=',d d= [1, 2, 3, 4, ['a', 'b']]
=================
http://bbs.chinaunix.net/thread-943223-1-1.html
http://www.51testing.com/html/49/101349-815499.html
本来想用一个配置文件config.py作为全局文件,以方便不同文件共享这里面设置的变量,同时也可以在不同的module中设置这个文件的变量的。后来发现,不行。他并非每次都重新导入的。
大家都知道这样使用全局变量是可以滴 a=1 def m(b): global a a=b m(2) print a 这样a就变成2了. 但是现在我是这样的,我的变量和函数定义在另外一个文件中(utils.py),我在主控脚本(main.py)中调用 比如: main.py: from utils import * print a print b modify(5,6) print a print b utils.py: a=1 b=2 def modify(c,d): global a a=c global b b=d 这样怎么就不行了呢?
可行的方法是: #main.py import utils print utils.a print utils.b utils.modify(5,6) print utils.a print utils.b #utils.py a=1 b=2 def modify(c,d): global a a=c global b b=d
应该尽量避免使用全局变量。不同的模块都可以自由的访问全局变量,可能会导致全局变量的不可预知性。对全局变量,如果程序员甲修改了_a的值,程序员乙同时也要使用_a,这时可能导致程序中的错误。这种错误是很难发现和更正的。
全局变量降低了函数或模块之间的通用性,不同的函数或模块都要依赖于全局变量。同样,全局变量降低了代码的可读性,阅读者可能并不知道调用的某个变量是全局变量。
但是某些时候,全局变量能够解决局部变量所难以解决的问题。事物要一分为二。
python里面全局变量有两种灵活的用法:
1 声明法
在文件开头声明全局变量variable,
在具体函数中使用该变量时,需要事先声明 global variable,否则系统将该变量视为局部变量。
CONSTANT = 0 (将全局变量大写便于识别)
def modifyConstant() :
global CONSTANT
print CONSTANT
CONSTANT += 1
return
if __name__ == '__main__' :
modifyConstant()
print CONSTANT
2模块法(推荐)
把全局变量定义在一个单独的模块中:
#gl.py
gl_1 = 'hello'
gl_2 = 'world'
在其它模块中使用
#a.py
import gl
def hello_world()
print gl.gl_1, gl.gl_2
#b.py
import gl
def fun1()
gl.gl_1 = 'Hello'
gl.gl_2 = 'World'
第二种方法,适用于不同文件之间的变量共享,而且一定程度上避免了开头所说的全局变量的弊端,推荐!
第三种方法:传递一个可变对象的引用完成共享
python
中的深拷贝和浅拷贝和java
里面的概念是一样的,所谓浅拷贝就是对引用的拷贝,所谓深拷贝就是对对象的资源的拷贝。
首先,对赋值操作我们要有以下认识:
- 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 )。
- 修改不可变对象(
str
、tuple
)需要开辟新的空间 - 修改可变对象(
list
等)不需要开辟新的空间
- 浅拷贝仅仅复制了容器中元素的地址
>>> a=['hello',[1,2,3]] >>> b=a[:] >>> [id(x) for x in a] [55792504, 6444104] >>> [id(x) for x in b] [55792504, 6444104] >>> a[0]='world' 修改不可变对象 >>> a[1].append(4) >>> print(a) ['world', [1, 2, 3, 4]] >>> print(b) ['hello', [1, 2, 3, 4]]
这里可以看出,未修改前,a
和b
中元素的地址都是相同的,不可变的hello,
和可变的list
地址都一样,说明浅拷贝只是将容器内的元素的地址复制了一份。
这可以通过修改后,b
中字符串没改变,但是list
元素随着a
相应改变得到验证。
浅拷贝是在另一块地址中创建一个新的变量或容器,但是容器内的元素的地址均是源对象的元素的地址的拷贝。也就是说新的容器中指向了旧的元素( 新瓶装旧酒 )。
- 深拷贝,完全拷贝了一个副本,容器内部元素地址都不一样
>>> from copy import deepcopy >>> a=['hello',[1,2,3]] >>> b=deepcopy(a) >>> [id(x) for x in a] [55792504, 55645000] >>> [id(x) for x in b] [55792504, 58338824] >>> a[0]='world' >>> a[1].append(4) >>> >>> print(a) ['world', [1, 2, 3, 4]] >>> print(b) ['hello', [1, 2, 3]]
这里可以看出,深拷贝后,a
和b
的地址以及a
和b
中的元素地址均不同,这是完全拷贝的一个副本,修改a
后,发现b
没有发生任何改变,因为b
是一个完全的副本,元素地址与a
均不同,a
修改不影响b
。
深拷贝是在另一块地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅仅是值相同而已,是完全的副本。也就是说( 新瓶装新酒 )。