python基础10(函数二)

 

一、函数形参实参的区别

  形参全称是形式参数,在用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]本身的存储地址)。

 

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

在python中参数传递采用的是值传递,这个和C语言有点类似。

 1 def modify1(m,k):
 2     m = 2 
 3     k = [4,5,6]
 4     return 
 5     
 6 def modify2(m,k):
 7     m = 2 
 8     k[0] = 0
 9     return
10     
11 n = 100
12 L = [1,2,3]
13 modify1(n,L)
14 print n
15 print L 
16 modify2(n,L)
17 print n
18 print L

 

代码运行结果:

从结果可以看出,执行modify1( )之后,n和L都没有发生任何改变;执行modify2( )后,n还是没有改变,L发生了改变。因为在Python中参数传递采用的是值传递方式,在执行函数modify1时,先获取n和L的id( )值,然后为形参m和K分配空间,让m和K分别指向对象100和对象[1,2,3]。

m=2这句让m重新指向对象2,而K=[4,5,6]这句让K重新指向对象[4,5,6]。这种改变并不会影响到实参n和L,所以在执行modify1之后,n和L没有发生任何改变;在执行函数modify2时,

同理,让m和K分别指向对象2和对象[1,2,3],然而K[0]=0让K[0]重新指向了对象0(注意这里K和L指向的是同一段内存),所以对K指向的内存数据进行的任何改变也会影响到L,因此在执行modify2后,L发生了改变。

 

三、变量的作用域

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

def func(x):
    print x is, x
    x = 2
    print Changed local x to, x

x = 50
func(x)
print x is still, x

 

输出:

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

        接下来,我们把值2赋给xx是函数的局部变量。所以,当我们在函数内改变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的值的时候。

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。