1. 程式人生 > 實用技巧 >資料開發_Python和Java在函式引數傳遞以及賦值的總結

資料開發_Python和Java在函式引數傳遞以及賦值的總結

理解的角度

函式引數傳遞機制 和變數賦值

函式呼叫的角度

 值傳遞(passl-by-value),是把實參的值賦值給形參。那麼對形參的修改,不影響實參的值 
   值傳遞(passl-by-value)過程中,被調函式的形式引數作為被調函式的區域性變數處理,
    即在堆疊中開闢了記憶體空間以存放由主調函式放進來的實參的值,從而成為了實參的一個副本。
    值傳遞的特點是被調函式對形式引數的任何操作都是作為區域性變數進行,不會影響主調函式的實參變數的值。   
引用傳遞(pass-by-reference)過程中,
  被調函式的形式引數雖然也作為區域性變數在堆疊中開闢了記憶體空間,
  但是這時存放的是由主調函式放進來的實參變數的地址。被調函式對形參的任何操作都被處理成間接定址,
 即通過堆疊中存放的地址訪問主調函式中的實參變數。
  正因為如此,被調函式對形參做的任何操作都影響了主調函式中的實參變數。

Java引數傳遞機制 - Java中只有值傳遞

 基本型別(byte,short,int,long,double,float,char,boolean)為傳值
       String、Integer、Double等immutable型別因為類的變數設為final屬性,無法被修改,只能重新賦值或生成物件。
  當Integer作為方法引數傳遞時,對其賦值會導致原有的引用被指向了方法內的棧地址,失去原有的的地址指向,
  所以對賦值後的Integer做任何操作都不會影響原有值
 物件型別(Object,陣列,容器)為   主要檢視--物件的記憶體區域--
 物件作為引數傳遞--建立副本 -地址的副本 把實參物件引用的地址當做值傳遞給了形式引數
    物件作為引數傳遞時,同樣是在方法內改變了物件的值,為什麼有的是改變了原物件的值,而有的並沒有改變原物件的值呢
	在Java中物件作為引數傳遞時,是把物件在記憶體中的地址拷貝了一份傳給了引數
	   沒有提供改變自身方法的引用型別  提供了改變自身方法的引用型別,但是不使用,而是使用賦值運算子
	   提供了改變自身方法的引用型別
	      這段程式碼,會new一個String,在把引用交給name,即等價於name = new String("hohu")

Python函式引數傳遞機制

輸入: 可變 和不可變
 引用傳遞方式的底層實現,採用的依然還是值傳遞的方式
 應用:
   如果需要讓函式修改某些資料,
     則可以通過把這些資料包裝成列表、字典等可變物件,然後把列表、字典等可變物件作為引數傳入函式,
     在函式中通過列表、字典的方法修改它們,這樣才能改變這些資料。

Python中函式引數列表

 形參 是指出現在函式定義中的名稱,
 實參 則是在呼叫函式時實際傳入的值
引數分類
 # 引數分為兩種: 關鍵字引數和位置引數
 #關鍵字引數: 在函式呼叫中前面帶有識別符號(例如 name=)或者作為包含在前面帶有 ** 的字典裡的值傳入
 	  強制關鍵字引數
 #位置引數: 不屬於關鍵字引數的引數。
      位置引數可出現於引數列表的開頭以及/或者作為前面帶有 * 的 iterable 裡的元素被傳入
 * 和 ** 說明符 收集引數
 # 預設引數 預設引數是一個可修改的容器比如一個列表、集合或者字典,可以使用None作為預設值
    並放到引數列表最後
     預設引數的值僅僅在函式定義的時候賦值一次
 	預設引數的值應該是不可變的物件,比如None、True、False、數字或字串。
 #匿名函式
   lambda表示式中的x是一個自由變數, 在執行時繫結值,而不是定義時就繫結,這跟函式的預設值引數定義是不同的
     funcs = [lambda x      : x+n for n in range(5)]
 	# 函式預設值引數形式,lambda函式在定義時就能繫結到值。
     funcs = [lambda x, n=n : x+n for n in range(5)]
 示例eg:
 class dict(**kwarg)
 class dict(mapping, **kwarg)
 class dict(iterable, **kwarg)
 #  可迭代物件的例子包括所有序列型別 (例如 list, str 和 tuple)
 >>> b = {'one': 1, 'two': 2, 'three': 3}
 # 如果給出一個位置引數並且其屬於對映物件,將建立一個具有與對映物件相同鍵值對的字典。 
 >>> a = dict(one=1, two=2, three=3)
 >>> c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
 >>> d = dict([('two', 2), ('one', 1), ('three', 3)])
 >>> e = dict({'three': 3, 'one': 1, 'two': 2})
 >>> f = dict({'one': 1, 'three': 3}, two=2)   
 引數
     位置引數 關鍵字引數 
     一個*引數只能出現在函式定義中最後一個位置引數後面,   而 **引數只能出現在最後一個引數
     有位置引數會被放到args元組中,所有關鍵字引數會被放到字典kwargs
     def anyargs(*args, **kwargs):
         print(args)   # A tuple
         print(kwargs) # A dict
    def avg(first, *args):
    """# rest是由所有其他位置引數組成的元組
    一個*引數只能出現在函式定義中最後一個位置引數後面
    在*引數後面仍然可以定義其他引數
    """
    return (first+sum(args))/(1+len(args))


def make_str(nm, **kwargs):
    """attrs是一個包含所有被傳入進來的關鍵字引數的字典
     **引數只能出現在最後一個引數  強制關鍵字引數
    """
    keyvals = [' %s="%s"' % item for item in kwargs.items()]
    attr_str = '##'.join(keyvals)
    return nm+attr_str


def wrapper(*args, debug=False, **kwargs):
    # 增加了一步名字檢查
    # 預設引數的值應該是不可變的物件,比如None、True、False、數字或字串
    # 匿名或行內函數 lambda表示式
    if debug:
        print('Calling', *args)
    return (*args, [item for item in kwargs.items()])


if __name__ == "__main__":
    data = avg(1, 2, 3)
    print(data)
    data2 = make_str('item', size='large', quantity=6)
    print(data2)
    """ 
     可變長度的   位置引數或關鍵字引數 後增加 常規引數  給其賦值必須強制通過關鍵字傳入
     在帶星號的“可變引數”後面增加新的引數,必須在呼叫的時候“強制命名引數”
    即
     “*”之後的都是強制關鍵字引數
    當強制關鍵字引數無預設值時,呼叫時必須給其賦值,否則報錯;
    當強制關鍵字引數有預設值時,若呼叫時不給其顯示賦值,其使用預設值。"""

Python賦值

 Python 要看01.資料型別以及 02.對應的操作是什麼
   如果我們有一個可變物件 (list, dict, set 等等),
        我們可以使用某些特定的操作來改變它,所有指向它的變數都會顯示它的改變。
   如果我們有一個不可變物件 (str, int, tuple 等等),所有指向它的變數都將顯示相同樣的值,
      但凡是會改變這個值的操作將總是返回一個新物件。
   如果你想知道兩個變數是否指向相同的物件,你可以使用 is 運算子,或內建函式 id()。

  賦值
     賦值是建立了物件的引用 -即 是複製了新物件的引用
  發生改變: 
     01. 通過返回一個結果元組  assigned to new objects  建立了新的物件
	 02. 通過傳遞一個可變 (即可原地修改的) 物件:
	       mutable list        -- changes a shared object
           mutable dictionary  -- change it in-place
	 03.在一個類例項中捆綁值  -- args is a mutable Namespace

Python的拷貝

 淺拷貝會建立新物件,其內容是原物件的引用。
    淺拷貝有三種形式: 切片操作,copy模組中的copy函式, 工廠函式,
	  list_a中有一個巢狀的list,如果我們修改了它,情況就不一樣 ,物件是否還包含物件
	 切片操作:list_b =  list_a[:]   或者 list_b = [each for each in list_a]
     工廠函式:list_b =  list(list_a)
     copy函式:list_b =  copy.copy(list_a)
	深拷貝
	   拷貝了物件的所有元素,包括多層巢狀的元素
  1、對於非容器型別,如數字,字元,以及其它“原子”型別,沒有拷貝一說。產生的都是原物件的引用。

   2、如果元組變數值包含原子型別物件,即使採用了深拷貝,也只能得到淺拷貝

參考:

https://docs.python.org/zh-cn/3/faq/programming.html#faq-argument-vs-parameter
python 之 賦值和拷貝(你真的瞭解嗎),python賦值 https://blog.csdn.net/solariens/article/details/53527373