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中设置这个文件的变量的。后来发现,不行。他并非每次都重新导入的。
有个办法是这样的,就是每个module都一个命名空间。在这个命名空间中的变量变化,会实时的到体现。
那有个办法就是:
1. Import配置文件时,不要from xxx import *, 而要import config.py
2. 在config.py文件中,用set_xxxValue()和get_xxxValue来提供外部访问接口,这个好处是,可以让全局变量在每次调用的时候都能得到刷新
3. 其他文件使用get_xxxValue()获取到全局变量的最新值。
另外,对于global这个声明,他只是在同一个文件中有效,并不能跨文件,就是夸module.所以不要妄想通过global来控制不同文件间的共享变量
大家都知道这样使用全局变量是可以滴
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.aprint 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。
深拷贝是在另一块地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅仅是值相同而已,是完全的副本。也就是说( 新瓶装新酒 )。