python引用类全局变量_python 值传递 引用传递 全局变量共享, 深拷贝,浅拷贝...

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

a1tag.png

整数 1 赋值给变量 a 就相当于是在整数1上绑定了一个 a 标签。

a = 2

a2tag.png

整数 2 赋值给变量 a,相当于把原来整数 1 身上的 a 标签撕掉,贴到整数 2 身上。

b = a

ab2tag.png

把变量 a 赋值给另外一个变量 b,相当于在对象 2 上贴了 a,b 两个标签,通过这两个变量都可以对对象 2 进行操作。

变量本身没有类型信息,类型信息存储在对象中,这和C/C++中的变量有非常大的出入(C中的变量是一段内存区域)

函数参数

Python 函数中,参数的传递本质上是一种赋值操作,而赋值操作是一种名字到对象的绑定过程,清楚了赋值和参数传递的本质之后,现在再来分析前面两段代码。

python引用类全局变量_python 值传递 引用传递 全局变量共享, 深拷贝,浅拷贝..._第1张图片

def bar(args):

args.append(1)

b = []

print(b)# 输出:[]

print(id(b)) # 输出:4324106952

bar(b)

print(b) # 输出:[1]

print(id(b)) # 输出:4324106952

python引用类全局变量_python 值传递 引用传递 全局变量共享, 深拷贝,浅拷贝..._第2张图片

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']

python引用类全局变量_python 值传递 引用传递 全局变量共享, 深拷贝,浅拷贝..._第3张图片

而正确的方式是,把参数默认值指定为None

def good_append(new_item, a_list=None):

if a_list is None:

a_list = []

a_list.append(new_item)

return a_list

python引用类全局变量_python 值传递 引用传递 全局变量共享, 深拷贝,浅拷贝..._第4张图片

参考: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。

深拷贝是在另一块地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅仅是值相同而已,是完全的副本。也就是说( 新瓶装新酒 )。

你可能感兴趣的:(python引用类全局变量)