Python函数构建

函数的构建格式

在Python中,函数的构建要遵循一定的格式,如下所示:

  1. 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
  2. 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
  3. 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  4. 函数内容以冒号起始,并且缩进。
  5. 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等则是可以修改的对象。

  1. 不可变类型:变量赋值a=5后再赋值a=10,这里实际是新生成一个int值对象10,再让a指向它,而5被丢弃,不是改变a的值,相当于新生成了a。
  2. 可变类型:变量赋值la=[1,2,3,4]后再赋值la[2]=5则是将listla的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

python函数的参数传递:

  1. 不可变类型:如整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在fun(a)内部修改a的值,只是修改另一个复制的对象,不会影响a本身。
  2. 可变类型:如列表,字典。如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]]

传入函数的和在末尾添加新内容的对象用的是同一个引用,因此输出了相同的结果。

函数的参数

以下是调用函数时可使用的正式参数类型:

  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语句这样标准的形式定义一个函数,它的特点如下:

  1. lambda只是一个表达式,函数体比def简单很多。
  2. lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
  3. lambda函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
  4. 虽然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']

代码解释:

  1. os.listdir()方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。这个列表以字母顺序,括号中的参数可以是路径,例如c:\downloads,也可以不加,用'.'来表示。
  2. 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种,分别是:

  1. L(Local)局部作用域

  2. E(Enclosing)闭包函数外的函数中

  3. G(Global)全局作用域

  4. 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/exceptfor/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使用的是局部变量,未定义,无法修改。

你可能感兴趣的:(Python函数构建)