函数
为什么要有函数?
在还未认识函数之前,一直遵循:面向过程编程。
即:根据业务逻辑从上到下实现功能,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,也就是将之前实现的代码块复制到现需功能处,如下:
while True: if cpu利用率 > 90%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 if 硬盘使用空间 > 90%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 if 内存占用 > 80%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接
因此我们需要一种重用性和可读性都更强的方式来解决上述冗长的代码问题,所以我们接触到了函数式变成。
例如上面的代码可以优化成这样
def 发送邮件(内容) #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 while True: if cpu利用率 > 90%: 发送邮件('CPU报警') if 硬盘使用空间 > 90%: 发送邮件('硬盘报警') if 内存占用 > 80%: 发送邮件('内存报警')
参照这种情况下照抄一个 武sir 视频中出现的报警器代码
https://www.cnblogs.com/evenyao/p/9170493.html
函数还能干什么?
先接触函数,后面我们会进一步对函数进行整合、分类和封装
- 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象:对函数进行分类和封装,让开发“更快更好更强...”
一、定义函数和结构
通常来说,函数的定义主要是如下结构:
def 函数名(参数): ... 函数体 ... return 返回值
- def:表示函数的关键字
- 函数名:函数的名称,日后根据函数名调用函数
- 函数注释:解释这段函数
- 函数体:函数中进行一系列的逻辑计算,如:发送邮件、计算出 [11,22,38,888,2]中的最大数等...
- 参数:为函数体提供数据
- 返回值:当函数执行完毕后,可以给调用者返回数据。
函数执行的顺序
二、参数
1.普通参数
严格按照顺序,将实际参数赋值给形式参数
def send(a,b): print("显示测试",a,b) return True send('root','even') #显示测试 root even
2.默认参数
定义函数时,必须将默认参数放置在参数列表的最后,例如 def function(a,b = '指定值'):
def send(a,b = '指定值'): print("显示测试",a,b) return True send('root','even') send('root2') #显示测试 root even #显示测试 root2 指定值
3.指定参数
将实际参数赋值给指定形式参数
def send(a,b): print("显示测试",a,b) return True send('root','even') send(b = 'root',a = 'even') #显示测试 root even #显示测试 even root
4.动态参数:*args 和 **kwargs
*arges:默认将传入的参数,全部放置在元组中,如 f1(*[11,22,33])
def f(*args): print(args,type(args)) n1 =[11,22,33,"root"] n2 = "even" f(n1) f(*n1) #将每一个元素转换到元组里面 f(n2) f(*n2) #([11, 22, 33, 'root'],) <class 'tuple'> #(11, 22, 33, 'root') <class 'tuple'> #('even',) <class 'tuple'> #('e', 'v', 'e', 'n') <class 'tuple'> #相当于做了一次内部for循环
**kwargs:默认将传入的参数,全部放置在字典中,如 f1(**{"k1":"v1","k2":"v2"})
def f(**kwargs): print(kwargs,type(kwargs)) f(n1 ="alex") f(n1 ="alex",n2 = 18) dic = {'k1':'v1','k2':'v2'} f(kk =dic) #{'n1': 'alex'} <class 'dict'> #{'n1': 'alex', 'n2': 18} <class 'dict'> #{'kk': {'k1': 'v1', 'k2': 'v2'}} <class 'dict'>
当实际参数为 f(**dic) 则直接传字典。相当于直接把字典赋值到函数中
def f(**kwargs): print(kwargs,type(kwargs)) dic = {'k1':'v1','k2':'v2'} f(**dic) #{'k1': 'v1', 'k2': 'v2'} <class 'dict'>
5.万能参数 (*args,**kwargs)
必须按照顺序,名字可以自己命名,但一般使用 *args和 **kwargs
def f1(*args,**kwargs): print(args) print(kwargs) f1(11,22,33,44,k1 = "v1",k2 = "v2") #(11, 22, 33, 44) #{'k1': 'v1', 'k2': 'v2'}
三、全局变量与局部变量
在下列情况中,name = 'even' 是属于 def f1() 中的局部变量,因此def f2()无法使用它
要想 def f2()也能调用到该变量,它必须提到最外层,使其变成全局变量
def f1(): name = 'even' print(name) def f2(): print(name) #NameError: name 'name' is not defined #even
变换之后得到
name = "even" def f1(): age = 18 print(age,name) def f2(): age = 19 print(age,name) f1() f2() #18 even #19 even
如果一个函数本身有自己的局部变量,优先使用自己的局部变量,如果自己没有,再去调用全局变量(有点网络基础中 DNS查询顺序的意思)
下面示例中由于def f1()中含有局部变量 name = "root",优先使用自己的局部变量,因此输出结果如下
name = "even" def f1(): age = 18 name = "root" print(age,name) def f2(): age = 19 print(age,name) f1() f2() #18 root #19 even
*** 对全局变量进行【重新赋值】,需要global + 变量名
*** 对于特殊的:列表、字典,读的过程中,本身可修改,但不可重新赋值
例如:
下面示例中全局变量本来是 name = "even",在def f1()中申明了global name,并在下面对name进行了重新赋值,name = "root"
因此def f2()调用name的时候,也使用name = "root"
name = "even" def f1(): age = 18 global name #表示name是全局变量 name = "root" print(age,name) def f2(): age = 19 print(age,name) f1() f2() #18 root #19 root
但对于特殊的:如列表、字典
因为在调用的过程中,本身可修改,如下
name = [11,22,33,44] def f1(): age = 18 print(name) name.append(999) print(age,name) def f2(): age = 19 print(age,name) f1() f2() #[11, 22, 33, 44] #18 [11, 22, 33, 44, 999] #19 [11, 22, 33, 44, 999]
***关于全局变量的潜规则:
对于所有的全局变量,建议全部使用大写来命名
这样当看到一个大写的变量名的时候,就可以立即知道这是某个地方的一个全局变量
如:
NAME = "even" def f1(): age = 18 print(age,NAME) def f2(): age = 19 print(age,NAME) f1() f2()
四、着手使用函数
之前写过一个用户注册并进行登陆验证的一个程序,但是面向过程编写的,现在熟悉完了函数之后,我们可以让这个程序变得更好一些
1 #!/user/bin/env python 2 # -*- coding:utf8 -*- 3 4 5 def main(): 6 print("====== 欢迎登录系统 ======") 7 choice = input('1:登录;2:注册') 8 9 if choice == '1': 10 print("=== 您已进入登录视图 ===") 11 user = input("请输入用户名>>>") 12 pwd = input("请输入密码>>>") 13 l = login(user,pwd) #定义l调用函数def login() 14 if l: 15 print("登录成功") 16 else: 17 print("登录失败") 18 19 elif choice == '2': 20 print("=== 您已进入注册视图 ===") 21 user = input("请输入要注册的用户名>>>") 22 pwd = input("请输入要注册的密码>>>") 23 is_exist = user_exist(user) #定义is_exist调用判断用户名是否存在的函数def user_exist() 24 if is_exist: #如果True 25 print("用户已经存在,无法注册") 26 else: #否则开启注册选项 27 result = register(user,pwd) #定义result调用注册函数def register() 28 if result: 29 print("注册成功") 30 else: 31 print("注册失败") 32 33 else: 34 print("输入有误请重新输入") 35 36 37 def register(username,password): 38 """ 39 用于用户注册 40 :param username:用户名 41 :param password:密码 42 :return: 43 """ 44 with open('db', "a", encoding="utf-8") as f: 45 temp = " " + username + "|" + password 46 f.write(temp) 47 f.close() 48 return True 49 50 51 def login(username,password): 52 """ 53 用于用户登录 54 :param username:用户输入的用户名 55 :param password:用户输入的密码 56 :return: True表示登录成功,False表示登录失败 57 """ 58 with open('db','r',encoding='utf-8') as f: 59 for line in f: #一行一行的进行读取 60 new_line = line.strip().split("|") #移除换行符,并以|符号进行分割 61 if new_line[0] == username and new_line[1] == password: 62 return True 63 return False 64 65 66 def user_exist(username): 67 """ 68 用来检查用户名是否存在 69 :param username:用户名 70 :return:True表示用户名已经存在,False表示不存在 71 """ 72 with open('db', "r", encoding="utf-8") as f: 73 for line in f: 74 line = line.strip() 75 new_line = line.split("|") 76 if username == new_line[0]: 77 return True 78 return False 79 80 81 main()
其他补充:
三元(目)运算
三元运算(三目运算),是对简单的条件语句的缩写。
# 书写格式 result = 值1 if 条件 else 值2 # 如果条件成立,那么将 “值1” 赋值给result变量,否则,将“值2”赋值给result变量
#if,else形式 if 1 == 1: name = "even" else: name = "root" #三元运算形式 name = "even" if 1 == 1 else "root"
lambda表达式
lambda表达式,是对于简单函数的一种表示方式。
# ###################### 普通函数 ###################### # 定义函数(普通方式) def func(arg): return arg + 1 # 执行函数 result = func(123) # ###################### lambda ###################### # 定义函数(lambda表达式) my_lambda = lambda arg : arg + 1 # 执行函数 result = my_lambda(123)
回顾 format 格式化
s1 = 'i am {0}, age{1}'.format("even",18) print(s1) s2 = 'i am {0}, age{1}'.format(*["even",18]) print(s2) #i am even,age 18. #i am even,age 18.
传值流程分析
同理于 **kwargs 但是必须指定变量,相当于直接传字典
s = 'i am {name}, age{age}'.format(name = 'even',age = 18) print(s) #i am even, age 18
dic = {'name':'even','age':18} s = 'i am {name}, age{age}'.format(**dic) print(s) #i am even, age 18
如果还是指定 0,1就会报错
dic = {0:'even',1:18} s = 'i am {0}, age{1}'.format(**dic) print(s)
函数的补充1
有这样一段代码 思考一下最后的分返回值是哪一个 输出的结果是什么?
程序执行的过程究竟是怎么样的?
def f1(a1, a2): return a1 + a2 def f1(a1, a2): return a1 * a2 ret = f1(8,8) print(ret)
经过反复推敲 我们觉得过程其实是这样的
执行到第一句 def 的时候在内存中创建区域 a1 + a2
让 f1函数指向这个 a1 + a2
但是后来又创建了一个 a1 * a2,就相当于 f1由原来指向的 a1 + a2变成指向 a1 * a2
这个时候 a1 + a2就成为了 "垃圾 "
好在 Python有一套垃圾清理机制,所以直接就只执行下面这一个函数 a1 * a2
我们还通过了pycharm的 debug功能验证了这个执行顺序的真实性
函数的补充2
Python函数传递参数的时候是是怎样进行操作的?
def f1(a1): a1.append(999) li = [11,22,33,44] f1(li) print(li)
这里有两张分析图,可以用来分析参数传递的过程中,到底是引用?还是传递另一份值?
我们查看一下这个 def 的输出结果是
#[11, 22, 33, 44, 999]
最后我们得出结论Python函数传递参数的时候是传递的引用,而不是另外一份值,所以传递的过程应该是这样一个图
所以 li最后输出为 [11,22,33,44,999]
那么这一个输出结果是什么
li = [11,22,33,44] def f1(arg): arg.append(55) li = f1(li) print(li)
#None
单看 f1(li)本来已经是 [11,22,33,44,55],但函数的 return默认为 None
又一次重新赋值,所以结果为None....