python:函数拓展

参数传递
1、在python中,值的类型属于对象,变量是没有类型的
2、赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 )。
3、修改不可变对象(str、tuple)需要开辟新的空间
4、修改可变对象(list等)不需要开辟新的空间
5、如果函数接收到的参数是一个可变对象(列表,字典),就会改变对象的原始值。 
6、如果函数接收到的参数是一个不可变对象,即基本数据类型(数值型,字符串,布尔),就不会改变对象的原始值。
7、无论什么参数,都是传引用,只不过基本数据类型的赋值其实是重新构造并指向了一个新的对象,内存地址不一样,可以用id()来查询内存地址~
8、对于可变对象(类类型,列表,字典)来说,如果直接x=y的话,x和y的内存地址相同;如果只是想简单赋值,就得用 x=y[:]。对于不可变对象(数值型,字符串,布尔)来说,直接等就可以了,

例1:

a = [1,2,3]
str = "zhou"

在上面代码中,[1,2,3]是list类型,"zhou"是string类型。而变量a和str是没有类型的,它仅仅是一个对象的引用(一个指针),可以是list类型也可以指向string类型


可更改对象和不可更改对象:

在python中字符串、元组、数字类型是不可更改对象、而列表、集合等则是可更改对象

1、不可变类型:
变量赋值(a = 5)后再赋值(a = 10),这里实际上是新生成一个int值对象10,再让a指向它,而5则被丢弃,不是改变了a的值,而是相当于新生成了a

2、可变类型:
变量赋值(list = [1,2,3,4] )后再赋值( list[2] = 5 )则是将列表list的第三个元素值更改,本身list没有动,只是其内部的一部分值被修改了。

python 函数的参数传递
不可变类型:
类似 c++ 的值传递,如整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a)内部修改a的值,只是修改另一个复制的对象,不会影响a本身。
可变类型:
类似 c++ 的引用传递,如列表,字典。如 fun(list),则是将 list真正的传过去,修改后fun外部的list也会受影响

总结:python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对

值传递:
方法调用时,实际上是实际参数把它的值传递给对应的形参,方法执行中形式参数的值得改变不会影响到实际参数的值

例1:

a1 = 520
a2 = a1
print (a1)
print (a2)

a2 = a1 + 1

print (a2)
print(a1)
#上面函数的输出结果为:520,520,521,520
引用传递:
也称地址传递,在方法调用时,实际上是把参数的引用(传的是地址,而不是参数的值)传递给方法中对应的形式参数,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值。
例2:
a = [1,2,3]
b = a
b.append(4)
print(a,b)
#上面函数的输出结果为:[1, 2, 3, 4] [1, 2, 3, 4]
1、在Python中,数字、字符串或者元组等不可变对象类型都属于值传递,而字典dict和列表list等可变对象类型属于引用传递
2、如果要想修改新赋值且原对象不变,则需要用到python的copy模块,即对象拷贝。对象拷贝又包含浅拷贝和深拷贝。
例3:对简单对象的浅复制
import copy
list_1 = [1,2,3]
list_2 = copy.copy(list_1)
list_2.append(4)
print(list_1,list_2)

#上面函数的输出结果为:[1, 2, 3] [1, 2, 3, 4](改变复制的对象的值不会影响到原对象的值)
例4:对简单对象的深复制
import copy
list_1 = [1,2,3]
list_2 = copy.deepcopy(list_1)
list_2.append(4)
print(list_1,list_2)

#上面函数的输出结果为:[1, 2, 3] [1, 2, 3, 4](改变复制的对象的值不会影响到原对象的值)

总结:从简单对象的浅copy和深copy可以看出,对原列表中的元素进行修改,copy的对象里面的元素不会发生改变。

例5:对复杂对象的浅复制

import copy
list_1 = [1,2,3,[4,5]]
list_2 = copy.copy(list_1)
list_2[3][0] = 7  #改变原对象的子对象
list_2[0] = 8
print(list_1,list_2)

#上面函数的输出结果为:[1, 2, 3, [7, 5]] [8, 2, 3, [7, 5]](对复杂对象进行浅copy时,如果对复杂对象的子对象元素进行修改,原列表和copy的列表里面的子对象元素都会产生变化 )
例6:对复杂对象的深复制
import copy
list_1 = [1,2,3,[4,5]]
list_2 = copy.deepcopy(list_1)
list_2[3][0] = 7  #改变原对象的子对象
list_2[0] = 8
print(list_1,list_2)

#上面函数的输出结果为:[1, 2, 3, [4, 5]] [8, 2, 3, [7, 5]](对复杂对象进行深copy时,若复杂对象的子对象元素发生了变化,copy的对象的所有元素都不会发生变化)

总结:

1、copy 浅拷贝:只拷贝父对象,不会拷贝对象的内部的子对象。

2、deepcopy 深拷贝:拷贝对象及其子对象


python传不可变对象实例:
例7:

def function(a):
    a = 10
b = 2
function(b)
print(b)

#上面函数的输出结果为:2

实例中实例中有 int 对象 2,指向它的变量是 b,在传递给 function函数时,按传值的方式复制了变量 b,a和b都指向了同一个Int对象,在a=10时,则新生成一个int值对象10并让 a 指向它。

python传可变对象实例:

例8:

def function(number,list = []):

    list.append(number)
    return list
list_1 = function(10)
list_2 = function(123,[])  #此处空列表的作用为初始化列表
list_3 = function("a")
print(list_1,list_2,list_3)#全部执行完后再打印结果

#上面函数的输出结果为:[10, 'a'] [123] [10, 'a']

例9:

def function(number,list = []):
    list.append(number)
    print(list) #先执行第一个并打印结果,再执行第二个并打印结果
function(1)
function(2)
function(3)

#上面函数的输出结果为:[1],[1, 2],[1, 2, 3]
之所以出现上面的情况是因为:
1、Python函数在定义的时候,默认参数list的值就被计算出来了,即[ ]。此时list指向[ ]。所以如果list中的内容改变了,下次调用引用的内容也就不再是[ ]了,而是新生成的list。所以要牢记一点 定义默认参数必须指向不可变对象
2、python中的"def"语句是可执行的,而且默认参数是在"def"语句的环境下求值的,如果"def"语句执行了多次,那么它每次将产生新的函数对象(对象会带着全新的默认值)

若必须使用可变对象作为函数的默认参数,又要避免上面出现的情况则我们可以 使用占位符来代替默认值,None是一个常用的值
例10:
def function(number,list = None):
    if list == None:
        list = []
    list.append(number)
    return list
list_1 = function(10)
list_2 = function(123,[])
list_3 = function("a")
print(list_1,list_2,list_3)
#上面代码的输出结果为:[10],[123],["a"]


猜你喜欢

转载自blog.csdn.net/qq_39314932/article/details/79831766
今日推荐