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

整數 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 函數中,參數的傳遞本質上是一種賦值操作,而賦值操作是一種名字到對象的綁定過程,清楚了賦值和參數傳遞的本質之后,現在再來分析前面兩段代碼。

 

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

list_default_value.png

而正確的方式是,把參數默認值指定為None

def good_append(new_item, a_list=None): if a_list is None: a_list = []
    a_list.append(new_item)
    return a_list

list_none_value.png

參考: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.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里面的概念是一樣的,所謂淺拷貝就是對引用的拷貝,所謂深拷貝就是對對象的資源的拷貝。
首先,對賦值操作我們要有以下認識:

  1. 賦值是將一個對象的地址賦值給一個變量,讓變量指向該地址( 舊瓶裝舊酒 )。
  2. 修改不可變對象(strtuple)需要開辟新的空間
  3. 修改可變對象(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]] 

這里可以看出,未修改前,ab中元素的地址都是相同的,不可變的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]] 

這里可以看出,深拷貝后,ab的地址以及ab中的元素地址均不同,這是完全拷貝的一個副本,修改a后,發現b沒有發生任何改變,因為b是一個完全的副本,元素地址與a均不同,a修改不影響b

深拷貝是在另一塊地址中創建一個新的變量或容器,同時容器內的元素的地址也是新開辟的,僅僅是值相同而已,是完全的副本。也就是說( 新瓶裝新酒 )。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM