Python函数参数传递方式

    Python  函数参数传递方式并不像其他语言那样是  指针/引用  和  值传递,  而是  对象引用传递,
        本质上是  “引用传递”,  可以说  python  中不存在  值传递,而是存在  “不可变对象”

容易误判的操作

        容易被误认为是  引用传递  的操作:

def case1(l: List[int]) -> None:
    l.append(233)

alist = list()
case1(alist)
print(alist)  # 输出: [233], 列表在函数内被改变了, 很像是 “引用/指针传递” 的效果

        容易被误认为是  值传递  的操作:

def case2(s: str) -> None:
    s += "dola"
    print(s)

astring = ""
case2(astring)
print(s)  # 输出仍然是 空字符串,但函数内的 输出为 “dola”, 很像是 “值传递” 的效果
对象引用传递

        上面的两个例子中,实际本质都是其他语言中的  “引用传递”。

        第一个例子比较好理解,alist  是一个对象的引用,与函数内的l,  指向的是同一个对象。
        第二个例子  astring  其实也是对象的引用,不一样的是,这个对象是  不可变对象,第一个例子的对象是可变对象。
                python  规定了  str  是  不可变对象,对不可变对象进行操作,都需要重新拷贝一份。
                也就是说,函数  def  case2(s:  str)  中,  一开始,  s  和  函数外的  alist  指向的是同一个对象,当执行
                s  +=  "dola"  时,拷贝了一个新的对象进行操作,因此在此操作后,s  和  函数外的  alist  指向的是不同的对象了。
                效果上,就像是其他语言的值传递。

可变对象  和  不可变对象

        在  Python中,对  数据类型进行了一层封装,  所有数据类型都是对象,分为两类:
                可变对象:list,  dict,  set
                不变对象:bool,  int,  float,  tuple,  str,  frozenset

        因此,数据对象的操作,解释器会根据其对象类型进行不同的操作。
        对于  可变对象:会直接操作引用所指向对象
        对于  不可变对象:  会先拷贝出一个新的对象,然后对新的对象做相关操作。

默认参数只计算  1  次
def test(l=[1]):
    l.append(1)
    print(l)

test() # [1, 1]
test() # [1, 1, 1]
可变参数  *args,  **kwargs

        args  会被打包成  tuple
        kwargs  会被打包成  dict