文章目录
九、函数式编程介绍
1、没有使用函数式编程之前带来的问题
- 代码的组织结构不清晰,可读性差
- 实现重复的功能时,你只能重复编写实现功能的代码,导致代码冗余,白白耗费精力
- 假如某一部分功能需要扩展或更新时,需要找出所有实现此功能的地方,一一修改,无法统一管理,加大了维护难度
2、函数是什么
- 函数是对实现某一功能的代码的封装(代码分解,松耦合,按功能划分)
- 函数可以实现代码的复用,从而减少代码的重复编写
3、Python 中函数的特性
- 函数的参数可以是python 中的任意数据类型,并且参数的数量可以是零个或多个。
- 函数也可以通过关键字
return
返回任何数量的 Python 中的任意数据类型,作为结果
4、函数分类
为了方便我们的开发,针对一些简单的功能,python解释器已经为我们定义好了的函数即内置函数, 内部提供很多方法,常用功能罗列出来,类似为使用方便而创建的快捷方式
对于内置函数,我们可以拿来就用而无需事先定义,如len(),sum(),max()
help()
type()
id()
bool() # 布尔值
max()
min()
sum()
len()
all() #接受一个序列,判断所有值如果是真的(非空),返回True 否则返回false
l = ['aaa','bbb']
all(l)
any() #只要有一个是真,就是真
练习一下 all() any()
#_*_ coding:utf-8 _*_
"""
代码注释
"""
l = [1, 2, 3]
a = 'aaa'
print(vars()) #当前模块的所有变量
print(__file__) #当前模块文件路径
print(__doc__) #当前模块的文档信息
print(__name__) # python 默认在执行 .py 文件时,__name__ = __main__
- 自定义函数
很明显内置函数所能提供的功能是有限的,这就需要我们自己根据需求,事先定制好我们自己的函数来实现某 种功能,以后,在遇到应用场景时,调用自定义的函数即可。
- 导入函数
5、函数的定义
1 、如何自定义函数?
函数的定义中可能会涉及到如下几点:
语法
def 函数名(参数1,参数2,参数3,...):
'''注释'''
函数体
return 返回的值
# 函数名要能反映函数本身所实现的意义
- def:表示定义函数的关键字
- 函数名:函数的名称,日后根据函数名调用函数
- 函数体:函数中进行一系列的逻辑计算,如:发送邮件、计算出 [11,22,38,888,2]中的最大数等…
- 参数:为函数体提供数据
- return:当函数执行完毕后,可以给调用者返回数据(以后的代码不再执行)。
实例
def f():
pass
def myfunc():
's' + 1
2. 函数在定义阶段都干了哪些事?
只检测定义函数所要求的语法,不执行函数体内的代码
也就说,语法错误在函数定义阶段就会检测出来,而代码的逻辑错误只有在调用执行时才会知道。
def get_result():
r - 1
get_result()
# 调用函数后会输出如下错误提示结果:
NameError: name 'r' is not defined
get_reuslt = """
r - 1
"""
6、函数调用
1、函数调用
函数的调用:函数名加小括号
func()
- 先找到名字
- 根据名字调用代码
def myfunc():
url = "www.qfedu.com"
# 调用
myfunc()
2、函数使用的原则:必须先定义,才可以调用
定义函数就是在定义“变量”,“变量”必须先定义后,才可以使用。
不定义而直接调用函数,就相当于在使用一个不存在的变量名
# 情景一
def foo():
print('from foo')
func()
foo() # 报错
# 情景二
def func():
print('from func')
def foo():
print('from foo')
func()
foo() # 正常执行
# 情景三
def foo():
print('from foo')
func()
def func():
print('from func')
foo() # 可以正常执行吗?
需求,自定义函数实现监控告警(cpu,disk,mem)
def email发送邮件()
(连接,发送,关闭)
def CPU告警()
cpu > 80
email发送邮件
def disk告警()
disk > 80
email发送邮件
def mem告警()
mem > 60
email发送邮件
#!/usr/bin/python3
import yagmail
import sys
yag = yagmail.SMTP(
user='xxxx@163.com',
password='',
host='smtp.163.com', # 邮局的 smtp 地址
port='25', # 邮局的 smtp 端口
smtp_ssl=False)
yag.send(to=sys.argv[1],
subject=sys.argv[2],
contents=sys.argv[3])
3、总结
- 函数的使用,必须遵循的原则是:先定义,后调用
- 在使用函数时,我们一定要明确地区分定义阶段和调用阶段
- 在函数体里面的任何代码都只是定义而已,只有在调用此函数时,这个函数内的代码才会执行。
# 定义阶段
def foo():
print('from foo')
func()
def func():
print('from func')
# 调用阶段
foo()
7、函数返回值 return
- 不定义,默认返回 None
- 返回多个值时,每个值用逗号隔开,也就是元组的形式
1、如何自定义返回值
使用 return
关键字
def foo():
x = 1
return x
ret = foo() # 给你, 这一步相当于下面的一行代码,就是 foo() 执行完毕,
#创建了 1 这个对象,之后把变量名 ret 分配给对象 1
# ret = 1
print(ret)
2、接收函数的返回值
注意:必须先执行函数,此函数的返回值才会被创建并返回
# 可以使用一个变量来接收一个函数的返回值
ret = foo()
print("foo 函数的返回值是:", ret)
# 也可以把函数的返回值直接作为参数来使用,这时函数 `foo` 会先被执行,
# 之后把其返回值放在其原来的位置,以供 `print` 函数作为参数使用
print("foo 函数的返回值是:", foo())
在函数执行的过程中,当在函数体内遇到了
return
关键字,函数就会立刻停止执行,并返回其返回值,return
之后的代码不会
被执行。
def func():
x = 100
return
print('ok') # 不执行
print('qf') # 执行,因为此代码已不是函数体内的代码了,注意缩进。
func()
定义一个有返回值的函数,并调用此函数
def echo(arg):
return arg # 用关键字 return 定义函数的返回
# 调用函数,并用一个变量接收其返回值
ret = echo('yangge')
print(ret)
# 执行 print 的结果
'yangge'
# 当一个函数没有显式的调用 return 时,默认会返回 None
def do_nothing():
print('ok')
# 下面的方式,会先执行函数体内的代码,之后把函数自身的返回值
# 放在其函数原来的位置,并且作为参数给 print
print(do_nothing())
ok # 函数体本身执行的结果
None # 函数自身的返回值
# 函数可以返回一个以及多个Python 的任何数据类型的结果
8、函数的参数
以下是重点,需要加强练习,理解原理,掌握用法和技巧
- 函数的参数是为函数体内的逻辑代码提供数据的。
- 函数的参数形式有 形参和实参
- 分类有:
- 普通参数
- 默认参数
- 动态参数
1. 什么是形参
对于函数来说,形式参数简称形参,是指在定义函数时,定义的一个变量名;
下面的代码中,x、y、z 就是形参
def foo(x, y, z):
print("第一个参数是", x)
print("第二个参数是", y)
print("第三个参数是", z)
2. 什么是实参
对于函数来说,实际参数简称实参。
是指在调用函数时传入的实际的数据,这会被绑定到函数的形参上;
函数调用时,将值绑定到变量名上,函数调用结束,解除绑定,并且实参将不再存在于程序中。
foo(1,2,3)
上面的 1、 2 和 3 都是实参
3. 形参分为:位置参数和默认参数
a. 位置参数
def send_mail(to, title, content):
send_info = {"user": to,
"title": title,
"content": content}
return send_info
形参的位置参数,在调用函数时必须给其传递实参。
但是,在函数内使用与否 ,没有限制。
b. 默认参数
def default_mail(title, content, to='xiannv@163.com'):
send_info = {"user": to,
"title": title,
"content": content}
return send_info
默认参数在函数定义的时候,就已经绑定了实际的值了。
调用函数时,可以给其传值,也可以不传。
- 不传值 就使用定义函数时绑定的值。
- 传值 就是使用传递的值。
同样,在函数内使用与否 ,没有限制。
4. 实参分为:位置参数和关键字参数
说的实参实质就是在调用函数时,给位置参数的进行赋值,这个行为通常叫做给函数 传参
因此,大家要明白,这里的 位置参数
和 关键字参数
是在函数调用的情况下的概念。
a. 位置参数传递参数
给形参的位置参数传参
info = send_mail("xiannv@163.com",
"一生问候",
"告诉你,一想到你,我这张丑脸上就泛起微笑。")
print(info)
还可以这样传参
def myfunc(x, y, z):
print(x, y, z)
tuple_vec = (1, 0, 1)
dict_vec = {'x': 1, 'y': 0, 'z': 1}
>>> myfunc(*tuple_vec)
1, 0, 1
>>> myfunc(**dict_vec)
1, 0, 1
给默认参数传参
info = default_mail( "一生问候",
"""因为你太美好了,
我等你等了这么久,
才能跟你在一起,
我害怕得不得了,
生怕自己搞砸了""",
"xiannv@163.com", )
print(info)
当使用实参的位置参数传参时,需要考虑到定义函数时的形参的位置。
b. 关键字参数传递参数
info = send_mail(content="我爱你,不光是因为你的样子,还因为和你在一起时,我的样子",
title="一生问候",
to="jingjing@126.com")
print(info)
使用关键字参数传参时,不必考虑形参的位置。但是需要注意,关键字必须和在定义函数时形参一致。
实参角度的参数结论:
- 在调用函数时,给形参的位置参数或形参的默认参数进行赋值时,可以用实参的位置参数进行传参; 此时传递的实参的顺序,必须和形参的位置顺序一一对应
- 在调用函数时,给形参的位置参数或形参的默认参数进行赋值时, 也可以使用实参的关键字参数进行传参; 此时,关键字参数之间是不区分位置顺序的
- 在调用函数时,形参的默认参数不传参数给它,此时,默认参数的值就是原来定义的值;假如传了参数,就使用传入的参数的值。
5. 万能参数: *args
和 **kwargs
(动态参数)
a. 用 *
表达式接收传进来的任意多个未明确定义的位置参数
def foo(x, y, *args):
print(args)
foo(1, 3, 5, 6)
list1 = [1,2,3,4,5]
foo(*list1)
foo(list1)
def foo(x, y, *args):
print(args)
print(x)
print(y)
# foo(1, 3, 5, 6)
# foo(1,2,3,4,5,6)
# print(foo(1,2,3,4,5))
list1=['a','b','c','d']
t1=('a','b','c','d')
s1='helloworld'
#foo(list1,t1)
foo(1,2,list1,t1) # 不是序列的可以不用加*
foo(1,2,*list1) #只有在传入序列的时候加*
# 输出如下
(['a', 'b', 'c', 'd'], ('a', 'b', 'c', 'd'))
1
2
('a', 'b', 'c', 'd')+
1
2
在函数体内接收到的参数会放在元组中
b. 用 **
表达式接收传进来的任意多个未明确定义的关键字参数
def func(x, y, **kwargs):
print(kwargs)
func(1, 3, name='shark', age=18)
func(1,2,3,4,5) #是否会报错
dict1={'k1':'a','k2':'b'}
func(1,2,**dict1)
def func(x,y,*args,**kwargs):
print(kwargs)
print(args)
print(x)
print(y)
#func(1,2,3,4)
func(1,3,name='aaa',age=18)
d2 = {'k1':'aaa','k2':'18'}
l1 = [1,2,3,4]
print(type(d2))
func(1,2,*l1,**d2) # 加*号调用,就会以序列的元素为元素组成一个元祖
func(1,2,l1) # 直接调用序列,会把整个序列作为元祖的一个元素
# 输出如下
{'age': 18, 'name': 'aaa'}
()
1
3
<type 'dict'>
{'k2': '18', 'k1': 'aaa'}
(1, 2, 3, 4)
1
2
{}
([1, 2, 3, 4],)
1
2
在函数体内接收到的参数会放在字典中
相结合的方式
def func1(*args,**kwargs):
print(args)
print(kwargs)
func1(1,2,3)
func1(k1=1,k2=2)
func1(1,2,3,k1='a',k2='b')
6、重要的东西,需要总结一下吧
函数参数从形参角度来说,就是在定义函数时定义的变量名,分为三种:
位置参数
如: arg1,arg2
所用的参数是有位置的特性的,位置是固定的,传参时的值也是要一一对应的
默认参数
如:arg = 5动态参数
如: *args,**kwargs, 可以同时用
三种形参在定义函数时的顺序如下:
In [108]: def position_arg(arg1, arg2, *args, **kwargs):
...: pass
...:
函数参数从实参角度来说,就是在调用函数时给函数传入的实际的值,分为两种
位置参数
在传参时,值和定义好的形参有一一对应的关系
关键字参数
在传参时,利用key = value 的方式传参,没有对位置的要求
十、名称空间和作用域
可以简单理解为存放变量名和变量值之间绑定关系的地方。
1、名称空间
在 Python 中有各种各样的名称空间:
- 全局名称空间:每个程序的主要部分定义了全局的变量名和变量值的对应关系,这样就叫做全局名称空间
- 局部名称空间:在函数的运行中定义的临时的空间叫做局部名称空间,只可以被这个函数所有。函数运行结束后,这个局部的名称空间就会销毁。
- 内置名称空间:内置名称空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple…它们都是我们熟悉的,拿过来就可以用的方法。
1、三种名称空间之间的加载顺序
python3 test.py
# 1、python解释器先启动,因而首先加载的是:内置名称空间
# 2、执行test.py文件,然后以文件为基础,加载全局名称空间
# 3、在执行文件的过程中如果调用函数,则临时产生局部名称空间
2、名字的查找顺序
当我们要使用一个变量时,先从哪里找?
局部使用:局部名称空间——>全局名称空间——>内置名称空间
x = 1
def f1():
x = 10
print(x)
f1() # 输出 10
全局使用:全局——>内置
#需要注意的是:在全局无法查看局部的,在局部可以查看全局的,如下示例
x = 1
def f1():
x = 10
print(x)
print(x) # 输出 1
2、作用域
作用域就是变量名的生效范围
分为: 全局作用域 和 局部作用域
1、全局作用域
内置的名称空间中的名字和全局名称空间的名字属于这个全局作用域。
- 全局作用域中的名字,在整个文件的任意位置都能被引用,全局存活,全局内都有效。
2、局部作用域
局部名称空间的名字属于局部作用域。
- 局部作用域中的名字只能在其本地局部生效和使用。
python
中局部作用域就是函数内定义或生成的名字,临时存活,随着函数调用的结束而消失。