第四篇 函数
函数可以实现代码的复用,对于多次调用的代码片段可以封装成函数以便于调用和后期维护。
1.定义函数与调用
1 def 函数名(参数): 2 函数体 3 return 返回值
注:
- 1.def是定义函数关键词,def和函数名之间需要有空格。
- 2.函数名为函数的名字,函数名可以为字符,数字和下划线,但是不能以数字开头。
- 3.参数为调用函数需要传入的参数。
- 4.返回值为调用函数后返回的值,不写return,默认返回None。
实例:
def sum(a, b): return a + b s = sum(12, 13) print(s) # 25
注:
- 调用函数直接使用函数名()即可,当然当函数需要参数时,需要在括号内传入指定的参数。
- 在开发中,如果希望给函数添加注释,应该在定义函数的下方,使用连续的三对引号,在引号之间编写对函数的说明文字,在函数调用位置,使用快捷键CTRL+Q可以查看函数的说明信息。
- 因为函数体相对比较独立,函数定义的上方,应该和其他代码(包括注释)保留两个空行。
2.函数的返回值
函数的返回值就是调用函数后返回的值,如果函数中没有return关键字,则该函数默认返回None。
1)没有返回值
def sum(a, b): s = a + b s = sum(12, 13) print(s) # None
注:sum函数中没有return返回值,该函数默认返回了None,另外当return关键字后没有值,函数也是返回None,此用法常用来结束函数。
2)返回一个值
def sum(a, b): s = a + b return s s = sum(12, 13) print(s) # 25
注:sum函数中返回了一个值s,然后把该值赋予s,并输出s的值。
3)返回多个值
def demoReturnMul(): return 1, "1,2", [1, 2, 3] print(demoReturnMul()) # (1, '1,2', [1, 2, 3])
注:可以看出,函数返回多个值,是以元组的形式返回的,实际上python把多个逗号分隔的值认为是元组。可以使用多个值进行接收,但是接收的变量的个数要与返回的值的个数相等。
可以使用多个值获取函数的返回值。
def measure(): print("开始测量....") temp = 39 wetness = 30 print("测量结束....") return temp, wetness gl_temp, gl_wetness = measure() print(gl_temp) print(gl_wetness)
3.函数的参数
1)形参和实参
形参是在定义函数时函数中的参数,实参是调用函数中传递给函数的参数。
2)多个参数
函数可以定义多个参数,每个参数之间使用逗号分隔。
# a,b,c,d均为参数 def sum(a, b, c, d): return a + b + c + d print(sum(1, 2, 3, 4)) # 10
3)位置参数
调用函数时根据定义函数时定义的参数来传递参数。
def hello_name(name, sex): dicts = {'1': '男士', '2': '女士'} print('hello {} {},welcome you'.format(name, dicts.get(sex))) hello_name("zhangsan", '1') # hello zhangsan 男士,welcome you
4)默认参数
定义函数时为函数提供默认参数,可以在调用函数时不传递该参数值,函数自动取该默认值,位置参数必须位于默认值参数前面。
def hello_name(name, sex = '2'): dicts = {'1': '男士', '2': '女士'} print('hello {} {},welcome you'.format(name, dicts.get(sex))) hello_name("zhangsan") # hello zhangsan 女士,welcome you
5)动态参数
函数参数前有一个*,则函数把参数值转换为一个元组,函数参数前有两个*,则函数把参数值转换为一个字典,默认参数放在args的后面,但是如果有**kwargs的话,**kwargs必须是最后的。
*和**在函数定义时代表聚合,在函数调用时代表打散。
def sum_nums_3(a, *args, b=22, c=33, **kwargs): print(a) print(b) print(c) print(args) print(kwargs) sum_nums_3(100, 200, 300, 400, 500, 600, 700, b=1, c=2, mm=800, nn=900) -------输出结果-------- 100 1 2 (200, 300, 400, 500, 600, 700) {'mm': 800, 'nn': 900}
说明:如果传递的参数是可变类型,在函数内部,使用方法修改了数据的内容,会影响到外部的数据。
列表变量调用+=本质上是在执行列表变量的extend方法,不会修改变量的引用,因此会修改外部变量的值。
def func(num_list): num_list += num_list num_list = [1, 2, 3] func(num_list) print(num_list) # [1, 2, 3, 1, 2, 3]
在有一些编程语言里,允许函数重名,但是在Python里不允许函数重名,如果函数重名了,后一个函数会覆盖前一个函数。在Python中,函数名可以理解成为一个变量名。
6)给函数增加文档注释
def print_line(char, times): """ 打印一行字符 :param char: 需要打印的字符 :param times: 打印字符的测试 """ print(char * times) print_line("*", 10) # **********
说明:在PyCharm中把光标定位在函数名字,选择小灯泡中的Insert documentation string subs。
4.递归函数
递归(Recursion),在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。
在使用递归时,需要注意以下几点:
- 递归就是在过程或函数里调用自身。
- 必须有一个明确的递归结束条件,称为递归出口。
注意: 切勿忘记递归出口,避免函数无限调用。
典型的算法:阶乘
n! = 1 × 2 × 3 × … × n
递归实现:
# n! = 1*2*3*....*n def factorial(n): if n == 1: return 1 else: return n * factorial(n - 1) print(factorial(5))
5.函数的实例
# 生活小助理 import random phone_numbers_str = "匪警[110],火警[119],急救中心[120],道路交通事故报警[122],水上求救专用电话[12395],天气预报[12121],报时服务[12117],森林火警[12119],电力服务[95598],红十字会急救台[999],公安短信报警[12110],通用紧急求救[112],信产部IP/网站备案[010-66411166]" weather_str="北京,2019年1月12日,多云,8°C,-4°C,南风3级~上海,2019年1月12日,小雨,9°C,6°C,北风2级~广州,2019年1月12日,阵雨转多云,22°C,15°C,持续无风向微风" # r = random.randint(1,16) # print(r) # 生成双色球 def generate_unionlotto(number): for j in range(0, int(number)): for i in range(0, 6): red = random.randint(1, 33) print(red, end=" ") blue = random.randint(1, 16) print(blue) # 号码百事通功能 def find_phone(keyword): phone_list = phone_numbers_str.split(",") # print(phone_list) for p in phone_list: if p.find(keyword) != -1: print(p) # 得到 def get_weather(city): city_list = weather_str.split("~") # print(city_list) weather_data = {} for i in range(0, len(city_list)): w = city_list[i].split(",") # print(w) weather = {"name": w[0], "date": w[1], "weather": w[2], "max": w[3], "min": w[4], "wind": w[5]} # print(weather) weather_data[weather["name"]] = weather # print(weather_data) if city in weather_data: return weather_data.get(city) else: return {}
while (True): print("1-双色球随机选号") print("2-号码百事通") print("3-明日天气预报") print("0-结束程序") c = input("请输入功能编号:") if c == "1": n = input("您要生成几注双色球号码:") generate_unionlotto(n) elif c == "2": n = input("请输入您要查询的机构或者电话号码:") find_phone(keyword=n) elif c == "3": n = input("请输入您要查询的城市:") w = get_weather(n) print(w) if "name" in w: print("{date} {name} {weather} {max}/{min} {wind}".format_map(w)) else: print("未找到{0}的天气数据".format(n)) elif c == "0": break else: print("您输入的功能编号有误,请重新输入") print("============================") print("感谢您的使用,祝您生活愉快,再见!")
6.命名空间和作用域
1)命名空间
命名空间定义:变量名到对象的映射,各个命名空间是独立的关系,一个命名空间不能有相同的变量名,不同的命名空间可以有相同的变量名。
命名空间分类:
- 1.全局命名空间:程序一运行就创建的命名空间。
- 2.局部命名空间:程序运行过程中创建的临时命名空间,如函数和类等。
- 3.内置命名空间:程序运行加载代码前创建的命名空间,用于存放python常用内置方法。
命名空间加载顺序:
内置命名空间(程序运行加载代码前)----全局命名空间(程序运行过程中,从上往下)----局部命名空间(程序运行中,从上往下)
命名空间取值顺序:
1.在局部调用:局部命名空间----全局命名空间----内置命名空间
1 i=1 2 def func(): 3 i=2 4 print(i) 5 6 func() 7 8 -------输出结果------ 9 2
2.在全局调用:全局命名空间----内置命名空间
1 print(len('hello')) #没有自己定义len方法,调用的是系统内置的len方法 2 3 -----输出结果------ 4 5
1 def len(o): #自己定义的len方法 2 return "调用的是非内置方法" 3 4 print(len('hello')) #由于调用顺序是全局命名空间,然后是内置命名空间,因此此处是调用自己定义的len方法 5 6 ------输出结果------- 7 调用的是非内置方法
2)作用域
全局作用域:包括内置命名空间和全局命名空间,在整个文件中均可被调用,如果调用的位置在全局命名空间的前面,则调用的是内置命名空间的内容(如果有的话)。
1 print(len('hello')) #因为自定义的方法在调用的方法后面,调用的是系统内置方法 2 3 def len(o): 4 return "调用的是非内置方法" 5 6 ------输出结果------ 7 5
局部作用域:包括局部命名空间,只能在局部范围内有效。
globals()和locals()的区别:
i = 10 def func(): name = "boxiaoyuan" age = 10 print(locals()) # 返回的是字典,字典里面的键值对,当前作用域的所有的内容
print(globals()) # func() 返回的是字典,字典里面的键值对,全局作用域的所有内容
函数默认参数的坑:
def func(a, lst = []): lst.append(a) return lst print(func(10)) # [10] print(func(20, [])) # [20] print(func(100)) # [10, 100]
说明:如果你的默认参数指向的是可变的数据类型,那么无论调用多少次这个默认参数,都是同一个。
global:用来声明一个全局变量,在局部作用域中想要对全局变量进行修改时可以使用此关键词进行声明。
1 i=1 2 def func(): 3 i+=1 4 print(i) 5 6 func() 7 8 -----输出结果----- 9 UnboundLocalError: local variable 'i' referenced before assignment
上面报错了,因为i为全局变量,但是在func方法中对该变量进行修改,会认为是局部变量,但是i在局部中没有进行定义,因此会报错,可以在函数内把i声明为全局变量,这样就不会报错了。
1 i=1 2 def func(): 3 global i 4 i+=1 5 print(i) 6 7 func() 8 9 ------输出结果------ 10 2
nonlocal:嵌套函数中内部函数修改外部变量的值。
1 def outsideFunc(): 2 i=2 3 def insideFunc(): 4 i+=2 5 print(i) 6 insideFunc() 7 outsideFunc() 8 9 ------输出结果------ 10 UnboundLocalError: local variable 'i' referenced before assignment
可以看出在内部函数中想要对外部函数的变量进行修改,会报错,在内部函数中修改外部函数变量进行修改python会默认把变量进行隐藏,可以使用nonlocal关键词进行声明。
1 def outsideFunc(): 2 i=2 3 def insideFunc(): 4 nonlocal i 5 i+=2 6 print(i) 7 insideFunc() 8 outsideFunc() 9 10 -----输出结果----- 11 4
注:global和nonlocal的区别是global声明的变量为全局变量,而nonlocal声明的变量为外部函数的变量。
7.函数名的本质
函数名的本质实际上就是函数的内存地址。
1 def func(): 2 pass 3 4 print(func) 5 6 -----输出结果---- 7 <function func at 0x000002118E5C00D0>
1)函数名可以赋值给其他变量
1 def func(): 2 print("函数调用了。。。") 3 4 a=func 5 a() 6 7 ------输出结果----- 8 函数调用了。。。
2)函数名可以当作容器类的元素
1 def func1(): 2 print("func1函数调用了。。。") 3 def func2(): 4 print("func2函数调用了。。。") 5 def func3(): 6 print("func3函数调用了。。。") 7 8 lists=[func1,func2,func3] 9 10 for i in lists: 11 i() 12 13 -------输出结果----- 14 func1函数调用了。。。 15 func2函数调用了。。。 16 func3函数调用了。。。
3)函数名可以当作参数传递给其他函数
1 def func1(): 2 print("func1调用了") 3 4 def func2(func1): 5 func1() 6 7 func2(func1) 8 9 ------输出结果----- 10 func1调用了
4)函数名可以当作返回值
1 def func1(): 2 print("func1调用了") 3 4 def func2(fun): 5 return fun 6 7 f=func2(func1) 8 f() 9 10 ----输出结果----- 11 func1调用了
8.匿名函数
Python使用lambda关键字来创建匿名函数。
形式通常为:lambda x:x * x。其相当于如下的函数:
def f(x): return x * x
说明:lambda表示匿名函数,冒号前的x表示参数,x * x表示执行代码。
匿名函数用来表达一个简单的函数,函数调用的次数很少,基本上就是调用一次。
调用匿名函数的两种方式:
1.给它定义一个名字(很少这样使用)
mul = lambda a, b: a * b print(mul(4, 6)) # 24
2.把这个函数当做参数传给另一个函数使用(使用场景比较多)
def calc(a, b, fn): c = fn(a, b) return c x1 = calc(3, 4, lambda x, y: x + y) x2 = calc(3, 4, lambda x, y: x - y) x3 = calc(3, 4, lambda x, y: x * y) x4 = calc(3, 4, lambda x, y: x / y) print(x1, x2, x3, x4) # 7 -1 12 0.75
8.1.sort方法的使用
nums = [4, 8, 2, 1, 7, 6] # 列表的sort方法,会直接对列表进行排序 # nums.sort() # print(nums) # [1, 2, 4, 6, 7, 8] # sorted内置函数,不会改变原有的数据,而是生成一个新的结果 sorted(nums) print(nums) # [4, 8, 2, 1, 7, 6]
students = [ {'name': 'zhangsan', 'age': 18, 'score': 98, 'height': 180}, {'name': 'lisi', 'age': '22', 'score': '97', 'height': 185}, {'name': 'jack', 'age': '23', 'score': '100', 'height': 175}, {'name': 'tony', 'age': '24', 'score': '90', 'height': 176}, {'name': 'henry', 'age': '20', 'score': '95', 'height': 182}, ] # 在sort内部实现的时候,调用foo方法,并且传入了一个参数,参数就是列表里的元素 students.sort(key=lambda ele: ele['height']) print(students)
8.2.filter,map,reduce为内置函数
filter(function, sequence):对sequence中的item依次执行function(item),将执行结果为True的item组成一个List/String/Tuple(取决于sequence的类型)。
map(function, sequence) :对sequence中的item依次执行function(item),将执行结果function(item)组成一个List返回。
reduce(function, sequence, starting_value):对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用。function接收的参数个数只能为2,先把sequence中第一个值和第二个值当参数传给function,再把function的返回值和第三个值当参数传给function,然后只返回一个结果。
from functools import reduce # 1.filter用法:返回执行结果为TRUE的入参(入参是列表字符元组) print(filter(lambda x: x * x - 9, range(10))) # 结果:[0, 1, 3, 4, 5, 6, 7, 8, 9] # 2.map的用法:对列表入参依次执行函数。入参为列表,有多少个列表,就应该有多少个入参。 print(map(lambda x: x * x - 4, range(10))) # 结果:[-4, -3, 0, 5, 12, 21, 32, 45, 60, 77] print(map(lambda x, y: x * y - 4, range(3), [8, 9, 10])) # 结果:[-4, 5, 16] # 3.reduce用法:先把sequence中第一个值和第二个值当参数传给function,再把function的返回值和第三个值当参数传给fuction,最终返回一个结果值 # 接收的入参个数只能为2 print(reduce(lambda x, y: x * y - 4, range(4))) # 结果:-40 # 计算0到100的和 print(reduce(lambda x, y: x + y, range(101))) # 结果:5050 print(reduce(lambda x, y: x + y, range(101), 100)) # 结果:5150
9.推导式
Python中提供了不同类型的推导式。
1)列表式推导式
列表推导式分为两种模式:
- 1.循环模式:[变量(加工的变量) for 变量 in iterable]
- 2.筛选模式: [变量(加工的变量) for 变量 in iterable if 条件]
a.循环模式
例如:从python1期到python18期写入列表lst
# 以f开头表示在字符串内支持大括号内的python表达式 lst = [f'python{i}' for i in range(1, 19)] print(lst)
b.筛选模式
a = [x * x for x in range(1, 5) if x % 2 == 0] print(a) # [4, 16]
2)字典式推导式
1 # -*- coding:utf-8 -*- 2 3 4 a = {x: x * x for x in range(1, 5) if x % 2 == 0} 5 6 print(a)
3)集合推导式
1 # -*- coding:utf-8 -*- 2 3 4 a = {x for x in range(1, 5) if x % 2 == 0} 5 6 print(a)
4)元组推导式
1 # -*- coding:utf-8 -*- 2 3 4 a = tuple(x for x in range(1, 5) if x % 2 == 0) 5 6 print(a)
10.闭包
闭包的定义:内部函数对外部函数非全局变量的引用,并且外部函数返回内部函数的引用(内存地址)。
实例:
def outside(): i = 10 def inside(): print(i) return inside ins = outside() # inside保存了内函数的内存地址 ins() # 10
使用__closure__判断函数是不是闭包函数:
def outside(): i = 10 def inside(): print(i) print(inside.__closure__) return inside ins = outside() # ins保存了内函数的内存地址 ins() # 10 -----输出结果----- (<cell at 0x0346A950: int object at 0x555C6520>,) 10
闭包的作用:保存局部信息不被销毁,保证数据的安全性。
闭包的应用:
-
可以保存一些非全局变量但是不易被销毁、改变的数据。
-
装饰器。
11.装饰器
装饰器的功能:在不修改原函数及其调用方式的前提下扩展函数的功能。
1)装饰不带参数的函数:
import time def timer(f): def inner(): beginTime=time.time() f() endTime=time.time() print("执行时间为:",endTime-beginTime) return inner @timer # 第一件事调用timer;第二件事把被装饰的函数传递给f def func(): print("hello world") time.sleep(1) func() # 第三件事:当再次调用func函数时,这时的demo函数已经不再是上面的func,而是timer函数的返回值。 -----输出结果---- hello world 执行时间为: 1.0007951259613037
2)装饰带参数的函数:
import time # 带参数 def timer(f): def inner(name): beginTime = time.time() f(name) endTime = time.time() print("执行时间为:", endTime - beginTime) return inner @timer # func=timer(func) def func(name): print("hello world", name) time.sleep(1) func("zhangsan")
3)装饰任意个数参数的函数:
import time # 带参数 def timer(f): def inner(*args, **kwargs): beginTime = time.time() f(*args, **kwargs) endTime = time.time() print("执行时间为:", endTime - beginTime) return inner @timer # func=timer(func) def func(name): print("hello world", name) time.sleep(1) @timer def func1(name, age): print("hello world %s,you are %s" % (name, age)) time.sleep(2) func("zhangsan") func1('lisi', 19)
4)装饰带有返回值的函数:
import time # 带参数 def timer(f): def inner(*args, **kwargs): beginTime = time.time() fr = f(*args, **kwargs) endTime = time.time() print("执行时间为:", endTime - beginTime) return fr return inner @timer # func=timer(func) def func(name): time.sleep(1) return name @timer def func1(name, age): time.sleep(2) return name, age print(func("zhangsan")) print(func1('lisi', 19))
12.元组和字典的拆包
在调用带有多值参数的函数时,如果希望将一个元组变量直接传递给args或者将一个字典变量直接传递给kwargs,就可以使用拆包,拆包的方式是在元组变量前加一个*,在字典变量前,加两个*。
def func(*args, **kwargs): print(args) print(kwargs) gl_num = (1, 2, 3) gl_dict = {"name": "小明", "age": 18} func(*gl_num, **gl_dict)