最近看了几篇文章,介绍函数传参数的,下面一一介绍,希望对你理解Python有帮助。

(一)python中函数的传参问题

        前段时间有写了一篇博文介绍了函数参数,其中提到了函数参数的传值方式,其中提到,不可变参数是“通过值”进行传递,可变对象是通过“指针”进行传递。参见博文:http://11026142.blog.51cto.com/11016142/1841703最近看的书上提到到Python函数参数既不是传值,也不是传引用,正确的叫法应该是叫传对象(call by object)或者说传对象的引用(call-by-object-reference)。函数参数在传递的过程中将整个对象传入,对可变对象的修改在函数外部及内部都可见,调用者和被调用者之间共享这个对象,而对于不变对象,由于不能真正被修改,因此修改往往是通过生成一个新对象然后来实现的。 

下面来通过简单例子来一一讨论

(1)是不是传引用呢?看看下面一个简单的例子

#coding=utf-8
def func(n):
    print id(n)
    n = n + 1
    print id(n)
n = 3
print id(n)
func(n)
print n

运行结果如下:

34450040

34450040

34450028

3

 按照传引用概念,上面的例子进过func()函数后,打印出的n值应该是4,并且func()函数里面执行操作n=n+1的前后n的id值应该是不变的,可事实上并不是这样,显然传引用是不恰当的。

(2)是不是传值呢?再看一例子

#coding=utf-8
def change_list(orginator_list):
  print "orginator_list is:",orginator_list
  new_list = orginator_list
  new_list.append("I am new")
  print "new list is:",new_list
  retrun new_list

orginator_list = ['a','b','c']
new_list = change_list(orginator_list)
print new_list
print orginator_list

输出结果:

orginator_list is:['a','b','c']

new list is:['a','b','c','I am new']

['a','b','c','I am new']

['a','b','c','I am new']

  传值通俗的来讲是:你在内存中有一个位置,我也有一个位置,我把我的值复制给你,以后你做什么跟我没有关系,我是我,你是你,咱们没有任何关系。可是上面的例子不是这么回事呀,“你”改变也带动了‘我’改变,所以传值也不恰当。

(3)可变对象传引用,不可变对象传值?是不是这样呢?看下面例子

>>>def change_me(org_list):

    print id(org_list)

    new_list = org_list

    print id(new_list)

    if len(new_list) > 5:

         new_list = ['a','b','c']

    for i,e in enumerate(e,new_list):

         if isinstance(e,list):

            new_list[i] = "***"

    print new_list

    print id(new_list)


如果传入参数org_list是列表的话,则是可变对象,按照可变对象传引用理解,new_list和org_list指向同一块内存,因此两者的id值输出应该是一致的,任何对new_list的操作都会直接反应到org_list上,那下面来测试下吧

>>>test_list1 = [1,['a',1,3],[2,1],6]  #长度小于5

>>>change_me(test_list1)

35260216

35260216

[1,'***','***',6]

35260216

>>>print test_list1

[1,'***','***',6]

>>>test_list2 = [1,2,3,4,5,6[1]]    #长度大于5

>>>change_me(test_list2)

35260136

35260136

['a','b','c']

35260664

>>>print test_list2

[1,2,3,4,5,6[1]]


上例中,对于test_list1,new_list和org_list的表现和我们理解的传引用一致,最后test_list被修改为[1,'***','***',6];对于test_list2,new_list和org_list的id输出在进行列表相关的操作前是一致的,但操作后new_list的id值却变成了35260664,整个test_list2在调用函数前后却没有发生改变,可按照传引用的理解期望值是['a','b','c'],但事实不是。所以可变对象传引用并不恰当


Python中的赋值与C/C++的赋值的机制并不一样。C/C++中假设事先a=5,当执行b=a时,在内存中申请一块内存并将a的值复制到该内存,当我们执行b=7之后是将b对应的值从5修改为7。

但是在Python中,赋值并不是赋值,b=a操作是b与a引用同一个对象。而b=7是将b指向对象7(b=7会在内存中重新创建一块内存存放7并将b指向该内存)



(二)python中默认参数问题

默认参数的使用,给函数的使用带来了很高的灵活性,但在使用时要注意它存在的潜在问题。看下例:

#coding=utf-8
def func(newitem,lists=[]):
    print id(lists)
    lists.append(newitem)
    print id(lists)
    return lists

 print func('a',['b',2,4,[1,2]])

输出结果如下:

140598839371680

140598839371680

['b', 2, 4, [1, 2], 'a']

这是没有问题的,如果连续调用两次该函数呢?会是怎么样的呢?如下代码:

print func('8')
print func('6')

结果如下:

140113046309056

140113046309056

['8']

140113046309056

140113046309056

['8', '6']

python中函数函数传递的是对象,可变对象在调用者和被调用者之间共享。因此func('8')时,[]变成了['8'],再次调用func('6')时由于默认参数不会重新计算,在['8']的基础上变成了['8','6']。

这是没问题的,看你的需求,如果说你想连续调用时默认参数所指向的对象在所有函数调用中不被共享,而是函数调用过程动态生成,也就是说func('8')返回的值是['8'],再连续调用时,func('6')返回值是['6'],而不是['8', '6']那么这么,设计代码就有问题了,我们在定义时就需要用None对象作为占位符。


上面例子代码改进如下:

#coding=utf-8
def func(newitem,lists=None):
    if lists is None:
        lists = []
    lists.append(newitem)
    return lists