您的位置:首页 > 编程语言 > Python开发

python学习笔记11(函数二): 参数的传递、变量的作用域

2014-12-03 22:30 1136 查看
一、函数形参实参的区别

  形参全称是形式参数,在用def关键字定义函数时函数名后面括号里的变量称作为形式参数。

实参全称为实际参数,在调用函数时提供的值或者变量称作为实际参数。

>>> def add(a,b):   #这里的a和b是形参
  return a+b

>>> add(1,2)       # 这里的1和2是实参
3
>>> x=2            # 这里的x和y是实参
>>> y=3
>>> add(x,y)
5


二、参数的传递和改变

在Python中一切皆对象,变量中存放的是对象的引用。

例子:

print id(5) # id(object)函数是返回对象object在其生命周期内位于内存中的地址,id函数的参数类型是一个对象
print id('python')
x = 2
print id(x)
print id(2)
y = 'Hello'
print id(y)
print id('Hello')


代码的运行结果:



从运行结果中可以看到,id(x)和id(2)的值是一样的,id(y)和id('Hello')的值也是一样的。

在Python中一切皆对象,像2,'hello'这样的值都是对象,只不过5是一个整型对象,而'hello'是一个字符串对象。

上面的x=2,在Python中实际的处理过程是这样的:先申请一段内存分配给一个整型对象来存储整型值2,然后让变量x去指向这个对象,实际上就是指向这段内存(这里有点和C语言中的指针类似)。

而id(2)和id(x)的结果一样,说明id函数在作用于变量时,其返回的是变量指向的对象的地址。因为变量也是对象,所以在这里可以将x看成是对象2的一个引用。

再看一个例子:

x=2
print id(x)
y=2
print id(y)
s='hello'
print id(s)
t=s
print id(t)


代码运行结果:



从运行结果可以看到id(x)和id(y)的结果是相同的,id(s)和id(t)的结果也是相同的。这说明x和y指向的是同一对象,而t和s也是指向的同一对象。

x=2这句让变量x指向了int类型的对象2,而y=2这句执行时,并不重新为2分配空间,而是让y直接指向了已经存在的int类型的对象2。

这个很好理解,因为本身只是想给y赋一个值2,而在内存中已经存在了这样一个int类型对象2,所以就直接让y指向了已经存在的对象。

这样一来不仅能达到目的,还能节约内存空间。t=s这句变量互相赋值,也相当于是让t指向了已经存在的字符串类型的对象'hello'(这个原理和C语言中指针的互相赋值有点类似)。

下面再看个例子:

x=2
print id(2)
print id(x)
x=3
print id(3)
print id(x)
L=[1,2,3]
M=L
print id(L)
print id(M)
print id(L[2])
L[0]=2
print id(L)
print M


代码运行结果:



两次的id(x)的值不同,这个可能让人有点难以理解。注意,在Python中,单一元素的对象是不允许更改的,比如整型数据、字符串、浮点数等。

x=3这句的执行过程并不是先获取x原来指向的对象的地址,再把内存中的值更改为3,而是新申请一段内存来存储对象3,再让x去指向对象3,所以两次id(x)的值不同。

然而为何改变了L中的某个子元素的值后,id(L)的值没有发生改变?在Python中,复杂元素的对象是允许更改的,比如列表、字典、元组等。

Python中变量存储的是对象的引用,对于列表,其id()值返回的是列表第一个子元素L[0]的存储地址。

就像上面的例子,L=[1,2,3],这里的L有三个子元素L[0],L[1],L[2],L[0]、L[1]、L[2]分别指向对象1、2、3,id(L)值和对象3的存储地址相同。

因为L和M指向的是同一对象,所以在更改了L中子元素的值后,M也相应改变了,但是id(L)值并没有改变,因为这句L[0]=2只是让L[0]重新指向了对象2,而L[0]本身的存储地址并没有发生改变,所以id(L)的值没有改变( id(L)的值实际等于L[0]本身的存储地址)。

结合例子看看函数的参数传递和改变这个问题

>>> a = 1
>>> def change_integer(a):
a = a + 1
return a

>>> print change_integer(a)
2
>>> print a
1
>>> b = [1,2,3]
>>> def change_list(b):
b[0] = b[0] + 1
return b

>>> print change_list(b)
[2, 2, 3]
>>> print b
[2, 2, 3]


第一个例子,我们将一个整数变量传递给函数,函数对它进行操作,但原整数变量a不发生变化。

第二个例子,我们将一个表传递给函数,函数进行操作,原来的表b发生变化。

对于基本数据类型的变量,变量传递给函数后,函数会在内存中复制一个新的变量,从而不影响原来的变量。(我们称此为值传递

但是对于表来说,表传递给函数的是一个指针,指针指向序列在内存中的位置,在函数中对表的操作将在原有内存中进行,从而影响原有变量。 (我们称此为指针传递

三、变量的作用域

局部变量 当你在函数定义内声明变量的时候,它们与函数外具有相同名称的其他变量没有任何关系,即变量名称对于函数来说是 局部 的。这称为变量的 作用域 。所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开始。

def func(x):
print 'x is', x
x = 2     # x=2只在func函数内其作用
print 'Changed local x to', x

x = 50
func(x)       # func函数并没有返回数据过来
print 'x is still', x


输出:



原理:在函数中,我们第一次使用
x
的 值 的时候,Python使用函数声明的形参的值。

接下来,我们把值
2
赋给
x
x
是函数的局部变量。所以,当我们在函数内改变
x
的值的时候,在主块中定义的
x
不受影响。

在最后一个
print
语句中,我们证明了主块中的
x
的值确实没有受到影响。

全部变量 它是在函数外部定义的,作用域是整个文件,全局变量可以直接在函数里面应用,但是如果要在函数内部改变全局变量,必须使用 global 关键字进行声明。

全局变量一个特征就是:除非被删除,否则他们存活到脚本运行结束,且对于所有的函数都可访问。

def func():
global x

print 'x is', x
x = 2
print 'Changed local x to', x

x = 50
func()
print 'Value of x is', x


输出:



原理:
global
语句被用来声明
x
是全局的——因此,当我们在函数内把值赋给
x
的时候,这个变化也反映在我们在主块中使用
x
的值的时候。

详细例子:

bar = 10            #声明一个全局变量bar
def foo():
global bar      #指定bar是全局变量,谁都不能替换!但是能修改原值!
bar2 = 0.1      #声明一个局部变量bar2
bar = 0.0001    #是对全局变量bar的修改,而不是在又声明一个局部变量将全局变量覆盖。
return bar+bar2

bar = 10000         #又对全局变量bar进行修改,但是为时已晚了,因为在foo函数里已经 return bar+bar2了
print foo()         #综上,输出:0.1001
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐