1、函数的基本概念
函数: 是一段可以重复多次调用的代码,通过输入的参数值,返回需要的结果。通俗地说,函数就是完成特定功能的一个语句组,这组语句可以作为一个单位使用,并且给它取一个名字,这样,我们就可以通过函数名在程序的不同地方多次执行(这通常叫做函数调用),却不需要在所有地方都重复编写这些语句。另外,每次使用函数时可以提供不同的参数作为输入,以便对不同的数据进行处理;函数处理后,还可以将相应的结果反馈给我们。
2、定义函数:
2.1、函数定义的语法:
def function_name(argument1[=default_value1], argument2[=default_value2]...):
''' docString '''
pass
return expression
- def 关键字:definition 的缩写,表示后面定义的是一个函数或方法。
- 函数名:以字母、数字或下划线组成的字符串,但是不能以数字开头。推荐全部使用小写字母,单词之间以下划线隔开
- 文档字符串:是包, 模块, 类或函数里的第一个语句。 这些字符串可以通过对象的
__doc__
成员被自动提取, 并且被 pydoc 所用。 - 函数的参数:放在一对圆括号中,参数的个数可以有零个、一个或多个,参数之间用逗号隔开。
- 形参、实参:在定义函数时,函数名后面圆括号中的变量名称叫做“形式参数”,或简称为“形参”;在调用函数时,函数名后面圆括号中的变量名称叫做“实际参数”,或简称为“实参”。
- 缺省参数:在定义函数时,我们可以用赋值符号给某些形参指定默认值,这样当调用该函数的时候,如果调用方没有为该参数提供值的话,则使用默认值;如果调用该函数的时候为该参数提供了值的话,则使用调用方提供的值 -- 像这样的参数我们称之为缺省参数。
- return 语句:return 语句的作用是结束函数调用,并将结果返回给调用者。不过,对于函数来说,该语句是可选的,并且可以出现在函数体的任意位置;对于没有使用 return 语句的函数,它实际上也向调用者返回一个值,那就是 None。
2.2、定义一个空函数:
def function_name(argument1[=default_value1], argument2[=default_value2]...):
pass
pass 语句什么都不做,只是用来作为占位符,比如:现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。
3、函数调用:
3.1、函数调用语法:
function_name(argument1, argument2...)
在交互式命令行,比如说想要获得 print 函数的帮助信息,可以执行如下命令
>>> help('print')
3.2、参数传递的方法
- 标准调用:标准调用方式,传递的值按照形参定义的顺序相应地赋给它们。
- 关键字调用:即在调用函数时同时给出形式参数和实际参数。解释器能通过给出的关键字来匹配参数的值,所以这样就允许参数缺失或者不按定义函数时的形式参数的顺序提供实际参数。
- 默认值参数:只有在形参表末尾的那些参数可以有默认参数值,也就是说你不能在声明函数形参的时候,先声明有默认值的形参而后声明没有默认值的形参。这是因为赋给形参的值是根据位置而赋值的。例如,
def func(a, b=1)
是有效的,但是def func(a=1, b)
是无效的。
注:
默认参数的值是不可变的对象,比如 None、True、False、数字或字符串
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# environment:python3.6.4
def print_info( a , b = [] ):
print("value: %s, id: %d" %(b, id(b)))
return b
if __name__ == '__main__':
result = print_info(1)
result.append('error')
print_info(2)
执行结果:
value: [], id: 42733832
value: ['error'], id: 42733832
3.2、参数检查:
个数检查:调用函数时,如果参数个数不对,Python 解释器会自动检查出来,并抛出
TypeError
类型检查:内置函数
isinstance
可以用来判断对象的类型,基本语法如下:
isinstance(object, class-or-type-or-tuple) -> bool
第二个参数可以是一个类型名或一个元组,如果是元组的话,对象类型与元组中类型名之一相同即返回True。
判断一个对象是否是字符串
>>> s = 'abc'
>>> isinstance(s,basestring)
True
>>> s = 3
>>> isinstance(s,basestring)
False
检查到错误之后,我们可以通过 raise 自己触发一个异常
3.4、变量的作用域
在 Python 中的任何变量都有其特定的作用域,比如在一个函数中定义的变量一般只能在该函数内部使用,这些只能在程序的特定部分使用的变量我们称之为局部变量;比如在一个文件顶部定义的变量可以供该文件中的任何函数调用,这些可以为整个程序所使用的变量称为全局变量。
上面是从空间的角度来考察变量的局部性和全局性,如果从时间的角度来看,不妨简单地认为在程序运行的整个过程中,全局变量一直占据着内存,并且它的值可以供所有函数访问;而局部变量则是只有在其所在函数被调用时才给它分配内存,当函数返回时,其所占内存就会被释放,所以它只能供其所在的函数所访问——换句话说,当某个函数退出时,其局部变量原先所占的内存将被分配给其它函数的局部变量。打个不太贴切的比方,全局变量就好比您自己买的房子,通常一家人要在那里住上好几十年,如果我要找您要债,我会老往您家里跑;而局部变量好像旅馆,今天您租住这件房间,我可以到这个房间来找您,但是到明天再来这间房间的话,找到的可能就是别人了。
在同一个源文件中,全局变量和局部变量同名时,在局部变量的作用范围内,全局变量不起作用。像下面这个例子,在函数 func 外部定义的变量 x ,和在函数内部定义的 x 互不影响
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# environment:python3.6.4
def func(x):
print("x 在函数内,改变前:%d, x is %d" %(id(x), x))
x = 2
print("x 在函数内,改变后:%d, x is %d" %(id(x), x))
if __name__ == '__main__':
x = 50
print("x 在函数外:%d, x is %d" %(id(x), x))
func(x)
print('x 在函数外:%d, x is still %d' %(id(x), x))
输出:
x 在函数外:500593664, x is 50
x 在函数内,改变前:500593664, x is 50
x 在函数内,改变后:500592128, x is 2
x 在函数外:500593664, x is still 50
这个过程,用文字描述如下:
- “x = 50”:新建了一个整形对象 50,分配了内存地址为:500593664,并且将函数外变量 x 绑定到了这个对象上。
- “func(x)”:新建了一个函数内变量 x,并且将 x 绑定到这个对象上,所以此时,对象内存地址未变
- “x = 2”:新建了一个整型对象 2,分配了内存地址:500592128,并将函数内变量 x 重新绑定到了这个对象上
如果你想要为一个定义在函数外的变量赋值,那么你就得告诉 Python 这个变量名不是局部的,而是全局的。我们使用 global 语句完成这一功能。
global 语句的作用是将某些变量声明为全局变量,变量名可以是一个,也可以是多个。当关键词 global 后面跟随多个变量名称时,各名称之间要用逗号分隔开来。
#!/usr/bin/python
# Filename: func_global.py
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
输出
]# python func_global.py
x is 50
Changed global x to 2
Value of x is 2
注1:
在 Python 中,字符串,整形,浮点型,tuple 是不可更改的对象,而 list , dict 等是可以更改的对象。
- 不可更改的类型:类似 c++ 的值传递,如:整数、字符串、元组。如fun(a),传递的只是 a 的值,没有影响 a 对象本身。比如在 fun(a) 内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。
- 可更改的类型:类似 c++ 的引用传递,如:列表,字典。如 fun(a),则是将 a 真正的传过去,修改后 fun 外部的 a 也会受影响
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# environment:python3.6.4
def chagne_list( b ):
print('函数中一开始 b 的值:{}, id 值为:%d' .format( b ) %id(b))
b.append(1000)
print('函数中 b 赋值后的值:{}, id 值为:%d' .format( b ) %id(b))
if __name__ == '__main__':
b = [1, 2, 3, 4, 5]
chagne_list(b)
print('最后输出 b 的值:{}, id 值为:%d'.format(b) %id(b))
执行结果为:
函数中一开始 b 的值:[1, 2, 3, 4, 5], id 值为:42799368
函数中 b 赋值后的值:[1, 2, 3, 4, 5, 1000], id 值为:42799368
最后输出 b 的值:[1, 2, 3, 4, 5, 1000], id 值为:42799368
注2:
如果要避免上面这种情况发生,可以向函数传递一个字典或列表的副本,而不是它本身
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# environment:python3.6.4
import copy
def chagne_list( b ):
print('函数中一开始 b 的值:{}, id 值为:%d' .format( b ) %id(b))
b.append(1000)
print('函数中 b 赋值后的值:{}, id 值为:%d' .format( b ) %id(b))
if __name__ == '__main__':
b = [1, 2, 3, 4, 5]
print('函数外一开始 b 的值:{}, id 值为:%d'.format(b) % id(b))
chagne_list(copy.deepcopy(b))
执行结果为:
函数外一开始 b 的值:[1, 2, 3, 4, 5], id 值为:43472904
函数中一开始 b 的值:[1, 2, 3, 4, 5], id 值为:43473096
函数中 b 赋值后的值:[1, 2, 3, 4, 5, 1000], id 值为:43473096
注3:
上面这个例子,又涉及到了 python 的浅拷贝和深拷贝,再举一个例子来说明一下:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# environment:python3.6.4
import copy
if __name__ == '__main__':
original = [[1], "test"]
shallow_copy = copy.copy(original)
deep_copy = copy.deepcopy(original)
print("改变前:")
print("original 值为:{}, id 为:{}".format(original, id(original)))
print("shallow_copy 值为:{}, id 为:{}".format(shallow_copy, id(shallow_copy)))
print("deep_copy 值为:{}, id 为:{}".format(deep_copy, id(deep_copy)))
original[0].append(2)
print ("改变后:")
print("original 值为:{}, id 为:{}".format(original, id(original)))
print("shallow_copy 值为:{}, id 为:{}".format(shallow_copy, id(shallow_copy)))
print("deep_copy 值为:{}, id 为:{}".format(deep_copy, id(deep_copy)))
执行结果:
改变前:
original 值为:[[1], 'test'], id 为:43338056
shallow_copy 值为:[[1], 'test'], id 为:43339336
deep_copy 值为:[[1], 'test'], id 为:43339272
改变后:
original 值为:[[1, 2], 'test'], id 为:43338056
shallow_copy 值为:[[1, 2], 'test'], id 为:43339336
deep_copy 值为:[[1], 'test'], id 为:43339272
3.5、传递长度可变的参数:
预先并不知道, 函数使用者会传递多少个参数给你, 所以在这个场景下使用如下两个关键字。
- 在函数的参数前使用标识符
*
:通常的写法*args
,可以将数量不定的,以位置参数方式传入的参数打包一个元组中。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# environment:python3.6.4
def print_everything(*args):
for count, thing in enumerate(args):
print('{0}. {1}'.format(count, thing))
if __name__ == '__main__':
print_everything('apple', 'banana', 'cabbage')
输出:
0. apple
1. banana
2. cabbage
- 在函数的参数前使用标识符
**
:通常的写法**kwargs
,可以将数量不定的,以关键字方式传入的参数打包到一个字典中。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# environment:python3.6.4
def greet_me(**kwargs):
for key, value in kwargs.items():
print("{0} == {1}".format(key, value))
if __name__ == '__main__':
greet_me(name="yasoob",age=15)
输出
name == yasoob
age == 15
4、函数的返回
python 函数返回值有两种形式:
- 返回一个值
现看看返回一个值的吧。
def firstvalue(a,b):
c = a + b
return c
print firstvalue(1,2)
结果:
3
- 返回多个值。
再看看返回多个值的:
def secondvalue(a,b):
c = a + b
return (a,b,c)
x,y,z = secondvalue(1,2)
print 'x:',x,'y:',y,'z:',z
结果:
x: 1 y: 2 z: 3