函数的构建格式
在Python中,函数的构建要遵循一定的格式,如下所示:
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
- 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
-
return [表达式]
结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
Python函数的构建方法示意图如下所示:
[图片上传失败...(image-52b046-1551584695056)]
一个简单的函数构建
在下面的例子中,构建了一个将华氏度转换为摄氏度的函数,如下所示:
def fahrenheit_coverter(C):
fahrenheit = C*9/5 +32
return str(fahrenheit) +' F'
C2F = fahrenheit_coverter(35)
print(C2F)
将上述文件保存在C盘,命名为test.py,运行结果如下所示:
C:\Users\20161111>python t.py
95.0 F
在交互模式下定义函数
如果在交互模式下面定义函数,解释器会显示三个小点来提醒你定义还没有完成,如下所示:
>>> def test_python():
...
在函数定义完毕的结尾,必须输入一行空白行。定义函数会创建一个函数类的对象,如下所示:
>>> def test_python():
... print("I test def function")
...
>>> test_python()
I test def function
>>>
第一行:def test_python(): 定义函数
第二行:print("I test def function") 输入函数的内容,前面要空四格
第三行:是一空行,输入几个空格,直接回车就行;
第四行:继续回车,函数创建完毕,输入test_python(),显示结果。
函数的嵌套
在函数中也能创建函数,如下面的案例:
def print_lyrics():
print("I'm a lumberjack, and I'm okay.")
print("I sleep all night and I work all day.")
def repeat_lyrics():
print_lyrics()
repeat_lyrics()
运行后如下所示:
C:\Users\20161111>python test.py
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
函数的参数传递
在Python中,类型属于对象,变量是没有类型的,先看下面的代码:
a = [1, 2, 3]
a = "Runoob"
在上述代码中,[1, 2, 3]
是List类型,"Runoob"
是String类型,而变量a是没有类型的,它仅仅是一个对象的引用(一个指针),它可以指向List类型的对象,也哦可以是指向String类型对象。
可更改(mutable)与不可更改(immutable)对象
在python中,strings,tuples,和numbers是不可更改的对象,而list,dict等则是可以修改的对象。
- 不可变类型:变量赋值a=5后再赋值a=10,这里实际是新生成一个int值对象10,再让a指向它,而5被丢弃,不是改变a的值,相当于新生成了a。
- 可变类型:变量赋值la=[1,2,3,4]后再赋值la[2]=5则是将listla的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。
python函数的参数传递:
- 不可变类型:如整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在fun(a)内部修改a的值,只是修改另一个复制的对象,不会影响a本身。
- 可变类型:如列表,字典。如fun(la),则是将la真正的传过去,修改后fun外部的la也会受影响
在python中,一切皆对象,严格意义上来讲,我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
python传不可变对象实例
看这个案例:
biotest@biotest-VirtualBox:~/python3/01basic$ cat para.py
#!/usr/bin/python3
def ChangeInt(a):
a = 10
b = 2
ChangeInt(b)
print(b)
# The result is 2
biotest@biotest-VirtualBox:~/python3/01basic$ python3 para.py
2
在这个案例中,有int对象2,指向它的变量是b,在传递给ChangeInt函数时,按传值的方式复制了变量b,a和b都指向了同一个Int对象,在a=10时,则新生成一个int值对象10,并让a指向它。
python传可变对象实例
可变对象在函数里修改了参数,那么在调用这个函数的函数里,原始的参数也被改变了,如下所示:
biotest@biotest-VirtualBox:~/python3/01basic$ cat para2.py
#!/usr/bin/python3
def changeme(mylist):
mylist.append([1,2,3,4])
print("The value inside function is: ",mylist)
return
mylist=[10,20,30]
changeme(mylist)
print("The value outside function is: ",mylist)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 para2.py
The value inside function is: [10, 20, 30, [1, 2, 3, 4]]
The value outside function is: [10, 20, 30, [1, 2, 3, 4]]
传入函数的和在末尾添加新内容的对象用的是同一个引用,因此输出了相同的结果。
函数的参数
以下是调用函数时可使用的正式参数类型:
- 必需参数
- 关键字参数
- 默认参数
- 不定长参数
必需参数
必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。调用printme()函数,你必须传入一个参数,不然会出现语法错误,如下所示:
biotest@biotest-VirtualBox:~/python3/01basic$ cat function1.py
#!/usr/bin/python3
def printme(str):
print(str)
return
printme()
biotest@biotest-VirtualBox:~/python3/01basic$ python3 function1.py
Traceback (most recent call last):
File "function1.py", line 7, in
printme()
TypeError: printme() missing 1 required positional argument: 'str'
关键字参数
关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。以下实例在函数 printme() 调用时使用参数名:
biotest@biotest-VirtualBox:~/python3/01basic$ cat function2.py
#!/usr/bin/python3
def printme(str):
print(str)
return
printme(str="Runoob tutorial")
biotest@biotest-VirtualBox:~/python3/01basic$ python3 function2.py
Runoob tutorial
以下实例中演示了函数参数的使用不需要使用指定顺序:
biotest@biotest-VirtualBox:~/python3/01basic$ cat function3.py
#!/usr/bin/python3
def printinfo(name, age):
print("Name: ",name)
print("Age: ",age)
return
printinfo(age=50,name="runoob")
printinfo("Zhang",20)
printinfo(20,"Zhang")
biotest@biotest-VirtualBox:~/python3/01basic$ python3 function3.py
Name: runoob
Age: 50
Name: Zhang
Age: 20
Name: 20
Age: Zhang
从结果来看,如果不按顺序输入参数,那么可以使用赋值语句将实参直接赋值给相应的形参。
默认参数
调用函数时,如果没有传递参数,则会使用默认参数。以下实例中如果没有传入age参数,则使用默认值:
biotest@biotest-VirtualBox:~/python3/01basic$ cat function4.py
#!/usr/bin/python3
def printinfo(name,age=35):
print("Name: ",name)
print("Age: ",age)
return
printinfo(age=50, name="runoob")
print("------------------------")
printinfo(name="runoob")
printinfo(20,"runoob")
printinfo("runoob",20)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 function4.py
Name: runoob
Age: 50
------------------------
Name: runoob
Age: 35
Name: 20
Age: runoob
Name: runoob
Age: 20
不定长参数
你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述2种参数不同,声明时不会命名。基本语法如下:
def functionname([formal_args,] *var_args_tuple ):
"函数_文档字符串"
function_suite
return [expression]
加了星号(*
)的变量名会存放所有未命名的变量参数。如果在函数调用时没有指定参数,它就是一个空元组。我们也可以不向函数传递未命名的变量。如下实例:
biotest@biotest-VirtualBox:~/python3/01basic$ cat function5.py
#!/usr/bin/python3
def printinfo(arg1, *vartuple):
print("Output: ")
print(arg1)
for var in vartuple:
print(var)
return
printinfo(10)
printinfo(11,12,13)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 function5.py
Output:
10
Output:
11
12
13
匿名函数lambda
python使用lambda来创建匿名函数。所谓匿名,意即不再使用def语句这样标准的形式定义一个函数,它的特点如下:
- lambda只是一个表达式,函数体比def简单很多。
- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
- 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
语法格式如下所示:
lambda [arg1 [,arg2,.....argn]]:expression
看一个简单的例子:
biotest@biotest-VirtualBox:~/python3/01basic$ cat lambda.py
#!/usr/bin/python3
sum = lambda arg1, arg2: arg1 + arg2
# 这里就是定义了一个函数,这个函数实现的功能就是两个数字相加
print("The value summed is : ",sum(10, 20))
print("The value summed is : ",sum(20,20))
biotest@biotest-VirtualBox:~/python3/01basic$ python3 lambda.py
The value summed is : 30
The value summed is : 40
上面的这个案例如果改成常规的函数写法,则是如下所示:
biotest@biotest-VirtualBox:~/python3/01basic$ cat lambda.py
#!/usr/bin/python3
sum = lambda arg1, arg2: arg1 + arg2
print("The value summed is : ",sum(10, 20))
print("The value summed is : ",sum(20,20))
biotest@biotest-VirtualBox:~/python3/01basic$ python3 lambda.py
The value summed is : 30
The value summed is : 40
匿名函数与普通函数的区别
看下面的一段代码:
biotest@biotest-VirtualBox:~/python3/03file$ cat lambda.py
#!/usr/bin/python3
def add(a,b):
return a+b
a=2
b=3
print('a+b=', add(a,b))
a=2
b=3
addl = lambda a,b:a+b
print('a+b=',addl(a,b))
print('type(add(a,b)):',type(add(a,b)))
print('type(addl):',type(addl))
biotest@biotest-VirtualBox:~/python3/03file$ python3 lambda.py
a+b= 5
a+b= 5
type(add(a,b)):
type(addl):
从上面我们可以看见add(a,b)的类型是int,而addl(a,b)的类型则是函数。从这里也能看出Lambda表达式确实是一种函数的表示方式。lambda的一般形式是关键字lambda后面跟一个或多个参数,紧跟一个冒号,以后是一个表达式。lambda是一个表达式而不是一个语句。它能够出现在python语法不允许def出现的地方。
lambda案例第1:快速组建列表
>>> list1 = lambda x:x**2
>>> l = [list1(i) for i in range(10)]
>>> print(l)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
lambda函数:减少函数命名
biotest@biotest-VirtualBox:~/python3/03file$ cat lambda1.py
#!/usr/bin/python3
def eat(f):
def many(n):
return n*f
return many
food = input('What kind of fruti do you like?')
f = eat(food)
print(f(5))
biotest@biotest-VirtualBox:~/python3/03file$ python3 lambda1.py
What kind of fruti do you like? Yes
Yes Yes Yes Yes Yes
上述代码改写为lambda则是如下所示:
def eat(f):
return lambda n:f*n
food = input('What kind of fruit do you like?')
f = eat(food)
print(f(5))
再看一个案例:
biotest@biotest-VirtualBox:~/python3/03file$ cat original.py # 这是原代码
#!/usr/bin/python3
freshfruit = [' banana',' loganberry ','passion fruit'] # 建立一个列表
print(freshfruit) #显示列表,发现前2个元素有空格
list3 =[w.strip() for w in freshfruit] # 去除空格
print(list3) #空格去掉后的效果
biotest@biotest-VirtualBox:~/python3/03file$ python3 original.py
[' banana', ' loganberry ', 'passion fruit']
['banana', 'loganberry', 'passion fruit']
biotest@biotest-VirtualBox:~/python3/03file$ cat original_trans.py # 改写为lambda形式
#!/usr/bin/python3
freshfruit = [' banana',' loganberry ','passion fruit']
list3 = list(map(lambda x:x.strip(),freshfruit))
# lambda x:x.strip()这里就相当于一个函数,map则是将这个函数映射到freshfruit的每个元素上
print(list3)
biotest@biotest-VirtualBox:~/python3/03file$ python3 original_trans.py
['banana', 'loganberry', 'passion fruit']
案例:使用列表推导式实现嵌套列表的平铺,如下所示:
>>> vec = [[1,2,3],[4,5,6],[7,8,9,10]]
>>> print([num for elem in vec for num in elem])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
这个列表推导式中有2个循环,第1个循环可以看作是外循环,执行得慢,第2个循环是内循环,执行得快,上面代码等价于下面的:
biotest@biotest-VirtualBox:~/python3/03file$ cat test1.py
#!/usr/bin/python3
vec = [[1,2,3],[4,5,6],[7,8,9,10]]
result = []
for elem in vec:
for num in elem:
result.append(num)
print(result)
biotest@biotest-VirtualBox:~/python3/03file$ python3 test1.py
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
案例:过滤掉不合条件的元素
列表推导式中可以使用if语句来进行筛选,只在结果列表中保留符合条件的元素,下面的代码是过滤当前目录下的所有文件,只保留以.py
结尾的文件,如下所示:
>>> import os
>>> print([filename for filename in os.listdir('.') if filename.endswith('.py')])
['a.py', 'count_line.py', 'practice.py', 'python.py', 't.py']
代码解释:
-
os.listdir()
方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。这个列表以字母顺序,括号中的参数可以是路径,例如c:\downloads,也可以不加,用'.'来表示。 -
endswith()
方法用于判断字符串是否以指定后缀结尾,如果以指定后缀结尾返回True,否则返回False。可选参数"start"与"end"为检索字符串的开始与结束位置。
案例:过滤满足一定条件的元素
>>> list4= [-1, -4, 6, 7.5 ,-2.3, 9, -11]
>>> print(list4)
[-1, -4, 6, 7.5, -2.3, 9, -11]
>>> result = [i for i in list4 if i < 0]
>>> print(result)
[-1, -4, -2.3, -11]
带有返回值的函数
return[表达式]
语句用于退出函数,选择性地向调用方返回一个表达式。不带参数值的return
语句返回None
,如下所示:
biotest@biotest-VirtualBox:~/python3/01basic$ cat return.py
#!/usr/bin/python3
def sum(arg1, arg2):
total = arg1 + arg2
print("Inside function is : ",total)
return total
total = sum(10, 20)
print("Outside function is : ",total)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 return.py
Inside function is : 30
Outside function is : 30
变量作用域
Python中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种,分别是:
L(Local)局部作用域
E(Enclosing)闭包函数外的函数中
G(Global)全局作用域
B(Built-in)内建作用域
Pyhton以L–>E–>G–>B
的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找,如下所示:
x = int(2.9) # 内建作用域
g_count = 0 # 全局作用域
def outer():
o_count = 1 # 闭包函数外的函数中
def inner():
i_count = 2 # 局部作用域
Python中只有模块(module)
,类(class)
以及函数(def、lambda)
才会引入新的作用域,其它的代码块(如if/elif/else/
、try/except
、for/while
等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问,如下代码:
>>> if True:
... msg = "I am a learner"
...
>>> msg
'I am a learner'
在这个案例中,msg变量定义在if语句块中,但外部还是可以访问的。如果将msg定义在函数中,则它就是局部变量,外部不能访问,如下所示:
biotest@biotest-VirtualBox:~/python3/01basic$ cat local_variable.py
#!/usr/bin/python3
def test():
msg_inner = "I am a learner"
print(msg_inner)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 local_variable.py
Traceback (most recent call last):
File "local_variable.py", line 6, in
print(msg_inner)
NameError: name 'msg_inner' is not defined
从错误提示就能看出,msg_inner并示未定义,因为它是局部变量,只能在函数内部使用。
全局变量和局部变量
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中,如下所示:
biotest@biotest-VirtualBox:~/python3/01basic$ cat global_local_variable.py
#!/usr/bin/python3
total = 0
# this is a global variable
def sum(arg1, arg2):
total = arg1 + arg2
# Here, total is a local variable
print("Variable inside function is local variable: ",total)
return total
sum(10, 20)
print("Variable outside function is global variable: ",total)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 global_local_variable.py
Variable inside function is local variable: 30
Variable outside function is global variable: 0
global和nonlocal关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字,在下面的案例中,修改了全局变量num:
biotest@biotest-VirtualBox:~/python3/01basic$ cat nonlocal.py
#!/usr/bin/python3
num = 1
def fun1():
global num # need to keyword "global" for statement
print(num)
num = 123
print(num)
fun1()
biotest@biotest-VirtualBox:~/python3/01basic$ python3 nonlocal.py
1
123
如果要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量则需要nonlocal关键字了,如下所示:
biotest@biotest-VirtualBox:~/python3/01basic$ cat nonlocal2.py
#!/usr/bin/python3
def outer():
num = 10
def inner():
nonlocal num
num = 100
print(num)
inner()
print(num)
outer()
biotest@biotest-VirtualBox:~/python3/01basic$ python3 nonlocal2.py
100
100
再看一个案例:
biotest@biotest-VirtualBox:~/python3/01basic$ cat action_scope_error.py
#!/usr/bin/python3
a = 10
def test():
a = a + 1
print(a)
test()
biotest@biotest-VirtualBox:~/python3/01basic$ python3 action_scope_error.py
Traceback (most recent call last):
File "action_scope_error.py", line 7, in
test()
File "action_scope_error.py", line 5, in test
a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment
错误信息为局部作用域引用错误,因为test函数中的a使用的是局部变量,未定义,无法修改。