函数是一段具备特定功能的、可复用的语句组。python中函数用函数名来表示,并经过函数名进行功能调用。它是一种功能抽象,与黑盒相似,因此只要了解函数的输入输出方式便可,不用深究内部实现原理。函数的最突出优势是:数组
在python中能够把函数分为:系统自带函数、第三方库函数、自定义函数。须要重点掌握的是「自定义函数」。app
自定义函数函数
自定义函数语法: def 函数名([参数列表]): 函数体 return语句 # 示例 def add1(x): x = x + 1 return x
函数经过「参数」和「返回值」来传递信息,并经过「参数列表」和「return语句」实现对二者的控制,详见下图:
学习
注意事项:测试
在定义好函数以后,有两种方式对其进行调用:code
# 从本文件调用 def add1(x): x = x+2 return x add1(10) # 从其余文件调用:从名为addx的文件调用已经定义好的add1函数 import os os.chdir('D:\\data\\python_file') # 从其余文件调用方法1 import addx addx.add1(4) # 从其余文件调用方法2 from addx import add1 add1(9)
程序入口对象
如C、C++等语言都有一个main函数做为程序的入口,main去调用函数库,函数库之间不能相互调用(以下图A和B之间),即程序的运行是从main函数开始。而python是脚本语言,没有统一入口,函数库之间能够互相调用。排序
因此,python代码执行有两种状况:递归
而python经过if__name__ == 'main' : 语句,控制这两种不一样状况下的代码执行。
if__name__ == 'main' : 模拟入口
基于python代码执行机理,可用if__name__ == 'main' : 语句模拟程序入口,实现对python代码执行的控制。
# 直接做为脚本执行 def add1(x): x = x+2 return x add1(10) # 模块重用执行 import os os.chdir('D:\\data\\python_file') from addx import add1 add1(9)
因此,想运行以脚本直接执行时才执行的命令时,能够将这些命令语句放在if__name__ == 'main': 判断语句以后:
# 自定义一个函数add1 def add1(a): a=a+1 return a print(__name__) if __name__ == "__main__": # 脚本直接执行时,运行后面的语句;被import执行时,不运行后面的语句 print(add1(2)) # 函数正确性验证和测试
从上面可知,函数最重要的三部分就是参数、函数体、返回值,而参数分为形参和实参:
注意事项:
def printmax(a,b): # a, b是形参 if a>b: print(a) printmax(3,4) # 3, 4是实参 # 形参修改不影响实参 def add2(x): x = x+2 return x x = 10 print(add2(x)) print(x) # 形参修改影响实参 def add2(x): x.append(2) return x y = [1, 1] # 实参y是变序列(列表、字典、集合) print(add2(y)) # 函数返回值 print(y) # 修改形参影响实参
在定义函数时无需指定形参类型,在调用函数时,python会根据实参类型来自动推断。而定义函数和调用函数的过程,能够简化为下面图中三步,实质就是经过参数和返回值传递信息,而参数的传递发生在第一和第三步。
函数参数多种多样,根据参数传递发生的前后顺序,能够从两个角度学习常见的一些参数:
同时,这些参数能够组合使用(可变参数没法和关键字参数组合),且参数定义的顺序从左至右分别是:位置参数 >> 默认值参数 >> 可变参数 / 关键字参数 / 命名关键字参数。参数传递还有一种高级用法——参数传递的序列解包。
默认值参数
默认参数就是在调用函数的时候使用一些包含默认值的参数。
# 默认值参数b=5, c=10 def demo(a, b=5, c=10): print(a, b, c) demo(1, 2)
注意事项:
可变参数
可变参数就是容许在调用参数的时候传入多个(≥0个)参数,可变参数分为两种状况:
# 可变位置参数 def demo(*p): print(p) demo(1, 2, 3) # 参数在传入时被自动组装成一个元组 # 可变关键字参数 def demo(**p): print(p) demo(b='2', c='5', a='1') # 参数在传入时被自动组装成一个字典
位置参数
位置参数特色是调用函数时,要保证明参和形参的顺序一致、数量相同。
# 位置参数 def demo(a, b, c): print(a, b, c) demo(1, 2, 3)
关键字参数
关键字参数容许在调用时以字典形式传入0个或多个参数,且在传递参数时用等号(=)链接键和值。关键字参数最大优势,就是使实参顺序能够和形参顺序不一致,但不影响传递结果。
# 关键参数 def demo(a, b, c): print(a, b, c) demo(b=2, c=5, a=1) # 改变参数顺序对结果不影响
命名关键字参数
命名关键字参数是在关键字参数的基础上,限制传入的的关键字的变量名。和普通关键字参数不一样,命名关键字参数须要一个用来区分的分隔符*,它后面的参数被认为是命名关键字参数。
# 这里星号分割符后面的city、job是命名关键字参数 def person_info(name, age, *, city, job): print(name, age, city, job) person_info("Alex", 17, city="Beijing", job="Engineer")
参数传递的序列解包
参数传递的序列解包,是经过在实参序列前加星号(*)将其解包,而后按顺序传递给多个形参。根据解包序列的不一样,能够分为以下5种状况:
序列解包 | 示例 |
---|---|
列表的序列解包 | *[3,4,5] |
元组的序列解包 | *(3,4,5) |
集合的序列解包 | *{3,4,5} |
字典的键的序列解包 | 若字典为dic={'a':1,'b':2,'c':3},则解包代码为:*dic |
字典的值的序列解包 | 若字典为dic={'a':1,'b':2,'c':3},则解包代码为:*dic.values() |
注意事项:
"""函数参数的序列解包""" def demo(a, b, c): print(a+b+c) demo(*[3, 4, 5]) # 列表的序列解包 demo(*(3, 4, 5)) # 元组的序列解包 demo(*{3, 4, 5}) # 集合的序列解包 dic = {'a': 1, 'b': 2, 'c': 3} demo(*dic) # 字典的键的序列解包 demo(*dic.values()) # 字典的值的序列解包 """位置参数和序列解包同时使用""" def demo(a, b, c): print(a, b, c) demo(*(1, 2, 3)) # 元组的序列解包 demo(1, *(2, 3)) # 位置参数和序列解包同时使用 demo(c=1, *(2, 3)) # 序列解包至关于位置参数,优先处理,正确用法 demo(*(3,), **{'c': 1, 'b': 2}) # 序列解包必须在关键字参数解包以前,正确用法
变量起做用的代码范围称为「变量的做用域」。不一样做用域内变量名能够相同,但互不影响。从变量做用的范围分类,能够把变量分类为:
须要特别指出的是,局部变量的引用比全局变量速度快,应考虑优先使用。
全局变量声明
有两种方式能够声明全局变量:
特殊状况,若局部变量和全局变量同名,那么全局变量会在局部变量的做用域内被隐藏掉。
d = 2 # 全局变量 def func(a, b): c = a*b return c func(2, 3) def func(a, b): c = a*b d = 2 # 局部变量 return c func(2, 3) """声明的全局变量,已在函数外定义""" n = 1 def func(a, b): global n n = b c = a*b return c s = func("knock~", 2) print(s, n) """声明的全局变量,未在函数外定义,则新增""" def func(a, b): c = a*b global d # 声明d为全局变量 d = 2 return c func(2, 3) """局部变量和全局变量同名,则全局变量在函数内会被隐藏""" d = 10 # 全局变量d def func(a, b): d = 3 # 局部变量d c = a+b+d return c func(1, 2) d
lambda函数,又称匿名函数,即没有函数名字临时使用的小函数。其语法以下:
lambda 函数参数:函数表达式 注意: - 匿名函数只能有一个表达式 - 该表达式的结果,就是函数的返回值 - 不容许包含其余复杂语句,但表达式中能够调用其余函数
lambda函数的使用场景,主要在两方面:
def f(x, y, z): return x+y+z # 位置参数 f(1, 2, 3) def f1(x, y=10, z=10): return x+y+z # 默认值参数 f(1) """把匿名函数赋值给一个变量,再利用变量来调用该函数,做用等价于自定义函数""" f=lambda x,y,z:x+y+z f(y=1,x=2,z=3) #关键值参数 L = ['ab', 'abcd', 'dfdfdg', 'a'] L.sort(key=lambda x: len(x)) # 按长度排序 L L=[('小明',90,80),('小花',70,90),('小张',98,99)] L.sort(key=lambda x:x[1],reverse=True) # 降序排序 L
在函数内部,能够调用其余函数。若是一个函数在内部调用自身自己,这个函数就是递归函数。但不是的函数调用本身都是递归,递归有其自身的特性:
从上图可知,递归过程是函数调用本身,本身再调用本身,...,当某个条件获得知足(基例)的时候就再也不调用,而后再一层一层地返回,直到该函数的第一次调用。递归函数的优势是逻辑简单清晰,缺点是过深的调用会致使栈溢出,在Python中,一般状况下,这个深度是1000层,超过将抛出异常。
案例:用递归实现阶乘
# 案例一:用递归实现阶乘 def fact(n): if n==0: return 1 else: return n*fact(n-1) fact(5) # 案例二:实现字符串反转 # 方法1,先转成列表,调用列表的revers方法,再把列表转成字符串 s = 'abcde' l = list(s) l.reverse() ''.join(l) # 方法2,切片的方法 s = 'abcde' s[::-1] # 方法3,递归的方法 s = 'abc' def reverse1(s): if s == '': return s else: print(s) return reverse1(s[1:])+s[0] reverse1(s)