首页 > 技术文章 > 数据开发_Python和Java在函数参数传递以及赋值的总结

ytwang 2020-11-04 19:04 原文

理解的角度

函数参数传递机制 和变量赋值

函数调用的角度

 值传递(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、如果元组变量值包含原子类型对象,即使采用了深拷贝,也只能得到浅拷贝

 3.示例
# 数据结构: 列表(只原始数据/包含容器数据) 操作:赋值 切片 后续操作: 排序等
# 赋值 - 指向同一对象 t1.sort()
t = [3, 1, 2]
t1 = t
t1[0] = 999
print("赋值源数据 ", t, "赋值源数据 ",t1)
# 赋值源数据  [999, 1, 2] 赋值源数据  [999, 1, 2]
# 切片 -浅拷贝
t = [3, 1, 2]
t2 = t[:]
t2[0] = 9
print("没有嵌套的切片源数据 ", t, "没有嵌套的切片数据 ",t2 )
# 没有嵌套的切片源数据  [3, 1, 2] 没有嵌套的切片数据  [9, 1, 2]
t = [3, 1, [7, 6, 2]]
t3 = t
t3[2][0] = 8
print("有嵌套的切片源数据 ", t, "没有嵌套的切片数据 ",t3)
# 有嵌套的切片源数据  [3, 1, [8, 6, 2]] 没有嵌套的切片数据  [3, 1, [8, 6, 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

推荐阅读