面向过程:
函数式:
函数可以理解为:一个一个的 功能块,你把一个大的功能拆分成一块一块的,用某项功能的时候就去调用某个函数,
函数可以理解为:乐高积木,给你一块一块的,你可以用这些积木块组成你最终想要实现的东西。
函数可以调用函数!主函数(main)的作用就是把函数进行串联、调用!函数本身是不能自己执行的如果你不调用就永不执行!
在学习函数之前,一直遵循:面向过程编程,即:根据业务逻辑从上到下实现功能,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,也就是将之前实现的代码块复制到现需功能处,如下:
while True:
if cpu利用率 > 90%:
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
if 硬盘使用空间 > 90%:
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
if 内存占用 > 80%:
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
定眼一看上述代码,if条件语句下的内容可以被提取出来公用,如下:
def 发送邮件(内容)
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
while True:
if cpu利用率 > 90%:
发送邮件('CPU报警')
if 硬盘使用空间 > 90%:
发送邮件('硬盘报警')
if 内存占用 > 80%:
发送邮件('内存报警')
对于上述的两种实现方式,第二次必然比第一次的重(chong)用性和可读性要好,其实这就是 函数式编程 和 面向过程编程 的区别:
def 函数名(参数):
...
函数体
...
函数的定义主要有如下要点:
以上要点中,比较重要有参数和返回值:
函数是一个功能块,该功能到底执行成功与否,需要通过返回值来告知调用者。
def 发送短信():
# 发送短信的代码...
if 发送成功:
return True
else:
return False
while True:
# 每次执行发送短信函数,都会将返回值自动赋值给result
# 之后,可以根据result来写日志,或重发等操作
result = 发送短信()
if result == False:
记录日志,短信发送失败...
为什么要有参数?
不使用参数:
def CPU报警邮件()
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
def 硬盘报警邮件()
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
def 内存报警邮件()
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
while True:
if cpu利用率 > 90%:
CPU报警邮件()
if 硬盘使用空间 > 90%:
硬盘报警邮件()
if 内存占用 > 80%:
内存报警邮件()
使用参数:
def 发送邮件(邮件内容)
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
while True:
if cpu利用率 > 90%:
发送邮件("CPU报警了。")
if 硬盘使用空间 > 90%:
发送邮件("硬盘报警了。")
if 内存占用 > 80%:
发送邮件("内存报警了。")
函数的参数分为以下几种:
######### 定义函数 #########
# name 叫做函数send的形式参数,简称:形参
def send(name):
print(name)
######### 调用函数 #########
# "Kevin" 叫做函数send的实际参数,简称:实参
send("Kevin")
Kevin
# 指定参数传递
def send(A,B,C):
print(A,B,C)
# 指定参数必须所有参数都要指定
send(B="Lina", A="Kevin", C="Gary")
Kevin Lina Gary
# 默认参数,没有传递参数,使用默认参数,传递了则使用传递的参数
def send(A, B, C="stupid"):
print(A, B, C)
send("Gary", "you're")
Gary you're stupid
# 关键字可变长参数,也可接收任意个参数,参数传递形式:key-value
def send(**kwargs): # 注意这里,定义函数这里的参数书写格式需要写成这样
print(kwargs)
f1_dict = {
'a': 'aaa',
'b': 'bbb',
'c': 'ccc'
}
send(a='aaa', b='bbb', c='ccc')
# 达到如上一样的效果
send(**f1_dict)
{'a': 'aaa', 'b': 'bbb', 'c': 'ccc'} {'a': 'aaa', 'b': 'bbb', 'c': 'ccc'}
# 非关键字可变长参数,可接收任意个参数
def send(*args):
print(args)
send("panwenbin", "lianglian", "zhaohongfei")
send(*["panwenbin", "lianglian", "zhaohongfei"]) # 使用"*"可遍历序列,递归传參
('panwenbin', 'lianglian', 'zhaohongfei') ('panwenbin', 'lianglian', 'zhaohongfei')
# 万能参数,可接收任意个参数,关键字可变长参数和非关键字可变长参数同时使用,接收任意类型任意个数参数
def send(*args, **kwargs):
print(*args)
print(kwargs)
send(1, 11, 21, **{"k1": "v1", "k2": "v2"})
1 11 21 {'k1': 'v1', 'k2': 'v2'}
python中使用到万能参数的内置函数:
# string的format()方法用到万能参数的例子
s1 = "i am {0}, age {1}".format("lianglian", 12)
print(s1)
s2 = "i am {0}, age {1}".format(*["lianglian", 12])
print(s2)
s3 = "i am {name}, age {age}".format(name="lianglian", age=12)
print(s3)
dict={'name': 'liangian', 'age':12}
s4 = "i am {name}, age {age}".format(**dict)
print(s4)
i am lianglian, age 12 i am lianglian, age 12 i am lianglian, age 12 i am liangian, age 12
python 函数多次定义被覆盖:
# python 函数多次定义被覆盖
def f1(a1, a2):
return a1 + a2
def f1(a1, a2):
return a1 * a2
ret = f1(8,8)
print(ret)
64
python 参数传递是引用:
# python 参数传递是引用
def f1(a1):
a1.append(999)
li = [11, 22, 33, 44]
f1(li)
print(li)
[11, 22, 33, 44, 999]
函数内定义的变量只在该函数内有效:
# 函数内定义的变量只在该函数内有效(注意作用域)
# global 修改全局变量
NAME = 'lianglian'
def f1():
age = 18
global NAME # 使用global修改过全局生效,作用于全局
NAME = 'Kevin'
print(age, NAME)
def f2():
age = 19
print(age, NAME)
f1()
f2()
18 Kevin 19 Kevin
Python 没有常量这个数据类型,但是在行首定义的常量(变量),遵循不要重新赋值
# 序列(list,tuple...) 行首定义的序列(全局)可以修改 但是不能重新赋值
group = [11,22,33,44]
def f1():
age = 18
group.append(55)
print(age, group)
def f2():
age = 19
print(age, group)
f1()
f2()
18 [11, 22, 33, 44, 55] 19 [11, 22, 33, 44, 55]
函数实參和形參之间是引用,内部定义的变量和只在内部生效
# 函数实參和形參之间是引用
lists = [1, 2, 3, "q"]
print(id(lists)) # 创建变量这个变量得时候的地址空间
def name(li):
print(id(li)) # 引用实际参数,在这里内存地址空间还是一样
li = "c"
print(li) # 只在这个函数内生效,就像是在这个函数内重新定义了一个"li"变量,和外面那个没关系
print(id(li)) # 这里是重新赋值了形参,所以地址空间变了
name(lists)
print(lists)
4367712648 4367712648 c 4331025664 [1, 2, 3, 'q']
# 函数内重新赋值作用域只限于函数内,函数内变了,外面没变
name = "Kevin"
def show():
name='liang'
print(name)
show()
print(name)
liang Kevin
# lambda 表达式会自动retrun结果
# lambda表达式 (简单的函数表达式,只能写一行)
def f1(a1):
return a1 + 100
ret = f1(10)
print(ret)
f2 = lambda a1: a1 + 100
r2 = f2(10)
print(r2)
110 110
装饰器也是个函数,只不过该函数可以具有特殊的含义,装饰器用来装饰函数或类,使用装饰器可以在函数执行前和执行后添加相应操作(装饰:在不改变原函数本身情况下,给他添加额外得功能,就像在一个普通士兵给她装备各种牛逼装备只有他能变得异常强大,而装饰器就是哪些装在它身上的牛逼装备)。
一、初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。如下:
############### 基础平台提供的功能如下 ###############
def f1():
print 'f1'
def f2():
print 'f2'
def f3():
print 'f3'
def f4():
print 'f4'
############### 业务部门A 调用基础平台提供的功能 ###############
f1()
f2()
f3()
f4()
############### 业务部门B 调用基础平台提供的功能 ###############
f1()
f2()
f3()
f4()
目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码时候没有关注验证相关的问题,即:基础平台的提供的功能可以被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。
老大把工作交给 worker A,他是这么做的:
跟每个业务部门交涉,每个业务部门自己写代码,调用基础平台的功能之前先验证。诶,这样一来基础平台就不需要做任何修改了。
当天worker A 被开除了...
老大把工作交给 worker B,他是这么做的:
只对基础平台的代码进行重构,让N业务部门无需做任何修改
############### 基础平台提供的功能如下 ###############
def f1():
# 验证1
# 验证2
# 验证3
print 'f1'
def f2():
# 验证1
# 验证2
# 验证3
print 'f2'
def f3():
# 验证1
# 验证2
# 验证3
print 'f3'
def f4():
# 验证1
# 验证2
# 验证3
print 'f4'
过了一周 worker B 被开除了...
老大把工作交给 worker C,他是这么做的:
只对基础平台的代码进行重构,其他业务部门无需做任何修改
############### 基础平台提供的功能如下 ###############
def check_login():
# 验证1
# 验证2
# 验证3
pass
def f1():
check_login()
print 'f1'
def f2():
check_login()
print 'f2'
def f3():
check_login()
print 'f3'
def f4():
check_login()
print 'f4'
老大看了下Low BBB 的实现,嘴角漏出了一丝的欣慰的笑,语重心长的跟Low BBB聊了个天: 老大说:
写代码要遵循开发封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
如果将开放封闭原则应用在上述需求中,那么就不允许在函数 f1 、f2、f3、f4的内部进行修改代码,老板就给了Low BBB一个实现方案:
def w1(func):
def inner():
# 验证1
# 验证2
# 验证3
return func()
return inner
@w1
def f1():
print 'f1'
@w1
def f2():
print 'f2'
@w1
def f3():
print 'f3'
@w1
def f4():
print 'f4'
对于上述代码,也是仅仅对基础平台的代码进行修改,就可以实现在其他人调用函数 f1 f2 f3 f4 之前都进行【验证】操作,并且其他业务部门无需做任何操作。
worker C心惊胆战的问了下,这段代码的内部执行原理是什么呢?
单独以 f1 为例:
# @ + 函数名
# 功能: 1.自动执行这个函数,并且将下面的函数名f1当做参数传递
# 2.将outer函数的返回值,重新赋值给放f1
def outer(func):
def inner():
print('run inner')
return func()
return inner
@outer
def f1():
print("F1")
f1()
run inner F1
当执行的时候,python是由上到下执行的,首先执行到定义函数:def outer(func) 这里把def outer(func)加载到内存
当执行到 @outer 的时候@outer是python的魔法!它会把它下面的函数进行封装。
把f1这个函数作为def outer(func)的参数传进去!就是:f1()=outer(f1)
然后def outer(func): == outer(f1)就会执行:
def outer(func):
def inner():
print('run inner')
return func() # func() == 原函数f1
return inner # 然后把封装后的函数return的结果给原函数 (= f1)
@outer就相当于做了一个重新打了个包,包含原数据和新增数据,然后变成新的包给"f1"
def f1() <==> def inner()
@outer # outer(f1)
def f1(): # def inner():
print("F1") # print('run inner')
# return f1()
# return inner()
用装饰器写一个计时器来统计函数的执行时间:
import time
def timer(func):
def inner():
start_time = time.time()
result = func()
end_time = time.time()
time_used = end_time - start_time
print("time_used: %.2fs" % time_used)
return result
return inner
@timer
def f1():
time.sleep(10)
print(111)
f1()
111 time_used: 10.00s
被装饰的函数如果有参数呢?
# 装饰函数使用参数
def outer(func):
def inner(name): # func对应f1,f1有参数,这里也要接收参数好传递给内部funce
print("before")
r = func(name)
print("after")
return r # 不要丢弃原函数返回值
return inner
@outer
def f1(arge):
print(arge)
return "F1"
ret = f1('test')
print(ret)
before test after F1
装饰器装饰动态参数函数:
# 装饰器装饰动态参数函数
def outer(func):
def inner(*arge, **kwargs): # 装饰器使用动态参数,来应对装饰不同得函数参数不确定情况
print("before")
r = func(*arge, **kwargs) # 将原原函数的参数传递进来
print("after")
return r # 不要丢弃原函数返回值
return inner
@outer
def f1(a1): # 一个参数
print(a1)
return "F1"
@outer
def f2(a1, b1): # 两个参数
print(a1, b1)
return "F2"
f1('test1')
f2('test1', 'test2')
before test1 after
'F1'
before test1 test2 after
'F2'
多装饰器的使用:
USER_INFO = {'is_login': None, 'user_type': 1}
# USER_INFO['is_login']
# USER_INFO.get('is_login', None)
def check_login(func):
def inner(*args, **kwargs):
if USER_INFO.get('is_login', None):
ret = func(*args, **kwargs)
return ret
else:
print("请登录!")
return inner
def check_admin(func):
def inner(*args, **kwargs):
if USER_INFO.get('user_type', None) == 2:
ret = func(*args, **kwargs)
return ret
else:
print('无权查看')
return inner
@check_login
@check_admin
def index():
"""
管理员的功能
:return:
"""
print('Index')
------------------------华丽的分割线-----------------------------
# 装饰器装饰也是有顺序的,代码是从上到下加载到内存中,
# 我们知道装饰器会把被 [@装饰器] 下面的函数装饰,并将它当作参数传递到装饰器里面.
# 装饰顺序结构(0),(1),(2); (0)代表被装饰函数本体
@check_login
@check_admin
def index():
"""
管理员的功能
:return:
"""
print('Index')
if USER_INFO.get('is_login', None): # (2) check_login函数体
if USER_INFO.get('user_type', None) == 2: # (1) check_admin函数体
ret = print('Index') # (0) index函数本体
return ret # (0) index函数返回值
else:
print('无权查看') # (1) check_admin函数体
else:
print("请登录!") # (2) check_login函数体
因此由上可看出,多个装饰器装饰一个函数,其实就是将这个函数层层嵌套到装饰器得函数体里面.
多装饰器的顺序:
代码由上到下执行,加载到内存中,所有当一个函数上面有多个装饰器的时候,从上往下的顺序依次装饰函数,被装饰的函数则从被装饰的函数由下往上层层嵌套到装饰器中。
1、最上面的装饰器是最外一层嵌套
2、嵌套需要注意的是,最外层包裹内层,执行时是从上往下,考虑嵌套后代码执行顺序。
迭代器 是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件
特点:
a = iter([1,2,3,4,5])
a
a.__next__()
a.__next__()
a.__next__()
a.__next__()
a.__next__()
a.__next__() # 没有了,再迭代会报错
<list_iterator at 0x10461de48>
1
2
3
4
5
--------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-68-5d36a6bfb415> in <module>() 7 a.__next__() 8 ----> 9 a.__next__() StopIteration:
一个函数调用时返回一个迭代器,那这个函数就叫做 生成器(generator);如果函数中包含 yield 语法,那这个函数就会变成生成器;
def func():
yield 1
yield 2
yield 3
yield 4
上述代码中:func是函数称为生成器,当执行此函数func()时会得到一个迭代器。
temp = func()
temp.__next__()
temp.__next__()
temp.__next__()
temp.__next__()
1
2
3
4
Example:
def myrange(arg):
start = 0
while True:
if start > arg:
return
yield start
start += 1
ret = myrange(3)
ret.__next__()
ret.__next__()
ret.__next__()
ret.__next__()
0
1
2
3
# for 是一个自动迭代器,内部也是调的对象的 “__next__()” 方法
ret = myrange(3)
for item in ret:
print(item)
0 1 2 3
在函数体内重复调用自己本身,做到反复运行一个算法达到最终目的然后返回。
def func(n):
n += 1
if n >= 4:
print(n)
return "end"
return func(n)
r = func(1) # 我传递进去个1,经过递归最后给我返回4
print(r)
4 end
# 1*2*3*4*5*6*7
def func(num):
if num == 1:
return 1
return num * func(num-1) # 7 * 7-1 返回递归
x = func(7)
print(x)
5040