Python之路:(七)函数式编程

函数式编程

面向过程:根据业务逻辑从上到下写垒代码
函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用这个函数即可
函数作用是你的程序有良好的扩展性、复用性。
同样的功能要是用3次以上的话就建议使用函数。
 
函数可以理解为一个一个的功能块,你把一个大的功能拆分成一块一块的,用某项功能的时候就去调用某个函数,
函数可以理解为:乐高积木,给你一块一块的,你可以用这些积木块组成你最终想要实现的东西。
函数可以调用函数!主函数的作用就是把函数进行串联、调用!函数本身是不能自己执行的如果你不调用就永不执行!
 

一、背景

在学习函数之前,一直遵循:面向过程编程,即:根据业务逻辑从上到下实现功能,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,也就是将之前实现的代码块复制到现需功能处,如下:

while True:
    if cpu利用率 > 90%:
        #发送邮件提醒
        连接邮箱服务器
        发送邮件
        关闭连接
  
    if 硬盘使用空间 > 90%:
        #发送邮件提醒
        连接邮箱服务器
        发送邮件
        关闭连接
  
    if 内存占用 > 80%:
        #发送邮件提醒
        连接邮箱服务器
        发送邮件
        关闭连接

定眼一看上述代码,if条件语句下的内容可以被提取出来公用,如下:

def 发送邮件(内容)
    #发送邮件提醒
    连接邮箱服务器
    发送邮件
    关闭连接
  
while True:
  
    if cpu利用率 > 90%:
        发送邮件('CPU报警')
  
    if 硬盘使用空间 > 90%:
        发送邮件('硬盘报警')
  
    if 内存占用 > 80%:

对于上述的两种实现方式,第二次必然比第一次的重用性和可读性要好,其实这就是函数式编程和面向过程编程的区别:

  • 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
  • 面向对象:对函数进行分类和封装,让开发“更快更好更强...”

 

二、函数的定义

def 函数名(参数):
     
    ...
    函数体
    ...

函数的定义主要有如下要点:

  • def:表示函数的关键字
  • 函数名:函数的名称,日后根据函数名调用函数
  • 函数体:函数中进行一系列的逻辑计算,如:发送邮件、计算出 [11,22,38,888,2]中的最大数等...
  • 参数:为函数体提供数据
  • 返回值:当函数执行完毕后,可以给调用者返回数据。

以上要点中,比较重要有参数和返回值:

1、返回值

函数是一个功能块,该功能到底执行成功与否,需要通过返回值来告知调用者。

def 发送短信():
     
    发送短信的代码...
 
    if 发送成功:
        return True
    else:
        return False
 
 
while True:
     
    # 每次执行发送短信函数,都会将返回值自动赋值给result
    # 之后,可以根据result来写日志,或重发等操作
 
    result = 发送短信()
    if result == False:
        记录日志,短信发送失败...

2、参数

为什么要有参数?

不使用参数:

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)

# ######### 调用函数 #########
#  "lianglian" 叫做函数send的实际参数,简称:实参
send("lianglian") 
普通参数传递

 lianglian

指定参数传递: 

# 指定参数传递
def send(A,B,C):
    print(A,B,C)


send(B="panwenbin", A="lianglian", C="zhaohongfei")  # 指定参数必须所有参数都要指定
指定参数传递

lianglian panwenbin zhaohongfei

默认参数:

# 默认参数,没有传递参数,使用默认参数,传递了则使用传递的参数
def send(A, B, C="SB"):
    print(A, B, C)

send("eric", "alex")
默认参数

eric alex SB

关键字可变长参数:

# 关键字可变长参数,也可接收任意个参数,参数传递形式: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', 'c': 'ccc', 'b': 'bbb'}
{'a': 'aaa', 'b': 'bbb', 'c': 'ccc'} 

非关键字可变长参数: 

# 非关键字可变长参数,可接收任意个参数
def send(*args):
    print(args)

send("panwenbin", "lianglian", "zhaohongfei")
send(*["panwenbin", "lianglian", "zhaohongfei"])   # 使用"*"可遍历序列,递归传參
非关键字可变长参数

('pawenbin', 'lianglian', 'zhaohongfei')
('panwenbin', 'lianglian', 'zhaohongfei')

万能参数:
# 万能参数,可接收任意个参数,关键字可变长参数和非关键字可变长参数同时使用,接收任意类型任意个数参数
def send(*args, **kwargs):
    print(*args)
    print(*kwargs)

send(1, 11, 21, k1='k1', k2='k2')
万能参数

1 11 21
k2 k1

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)
python中使用到万能参数的内置函数

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)
python 函数多次定义被覆盖

64

 

python 参数传递是引用:

# python 参数传递是引用
def f1(a1):
    a1.append(999)

li = [11, 22, 33, 44]
f1(li)

print(li)
python 参数传递是引用

[11, 22, 33, 44, 999]

 

函数内定义的变量只在该函数内有效(注意作用域) 

# 函数内定义的变量只在该函数内有效(注意作用域)
# global 修改全局变量
NAME = 'alex'

def f1():
    age = 18
    global NAME      # 使用global修改过全局生效,作用于全局
    NAME = 'lianglian'  
    print(age, NAME)

def f2():
    age = 19
    print(age, NAME)

f1()
f2()
函数内定义的变量只在该函数内有效(注意作用域

18 lianglian
19 lianglian

 

序列(list,tuple...) 行首定义的序列(全局)可以修改 但是不能重新赋值:

# 序列(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()
序列(list,tuple...) 行首定义的序列(全局)可以修改 但是不能重新赋值

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)
函数实參和形參之间是引用

4494019272
4494019272
c
4492796232
[1, 2, 3, 'q']

 

函数内重新赋值作用域只限于函数内:

# 函数内重新赋值作用域只限于函数内,函数内变了,外面没变
name = "wu"
def  show():
    name='alex'
    print(name)
show()
print(name)
函数内重新赋值作用域只限于函数内

alex
wu

 

lambda表达式

# lambda 表达式会自动retrun结果
# lambda表达式  (简单的函数表达式,只能写一行)
def f1(a1):
    return a1 + 100

ret = f1(10)
print(ret)

f2 = lambda a1: a1 + 100

r2 = f2(10)
print(r2)
lambda 表达式

 

 

装饰器

装饰器也是个函数,只不过该函数可以具有特殊的含义,装饰器用来装饰函数或类,使用装饰器可以在函数执行前和执行后添加相应操作(装饰:在不改变原函数本身情况下,给他添加额外得功能,就像在一个普通士兵给她装备各种牛逼装备只有他能变得异常强大,而装饰器就是哪些装在它身上得牛逼装备)。

 

一、初创公司有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()
View Code 

目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码时候没有关注验证相关的问题,即:基础平台的提供的功能可以被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。

老大把工作交给 Low B,他是这么做的:

跟每个业务部门交涉,每个业务部门自己写代码,调用基础平台的功能之前先验证。诶,这样一来基础平台就不需要做任何修改了。

当天Low B 被开除了...

 

老大把工作交给 Low BB,他是这么做的:

只对基础平台的代码进行重构,让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'

############### 业务部门不变 ############### 
### 业务部门A 调用基础平台提供的功能### 

f1()
f2()
f3()
f4()

### 业务部门B 调用基础平台提供的功能 ### 

f1()
f2()
f3()
f4()
View Code

过了一周 Low BB 被开除了...

 

老大把工作交给 Low BBB,他是这么做的:

只对基础平台的代码进行重构,其他业务部门无需做任何修改
############### 基础平台提供的功能如下 ############### 

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'
View Code

老大看了下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'
View Code

对于上述代码,也是仅仅对基础平台的代码进行修改,就可以实现在其他人调用函数 f1 f2 f3 f4 之前都进行【验证】操作,并且其他业务部门无需做任何操作。

Low BBB心惊胆战的问了下,这段代码的内部执行原理是什么呢?

单独以f1为例:

# @ + 函数名
# 功能: 1.自动执行这个函数,并且将下面的函数名f1当做参数传递
#      2.将outer函数的返回值,重新赋值给放f1
def outer(func):
    def inner():
        print('log')
        return func()
    return inner

@outer
def f1():
    print("F1")

f1()

log
F1

 

当执行的时候,python是由上到下执行的,首先执行到def outer(func):这里把def outer(func)加载到内存

当执行到@outer的时候@outer是python的魔法!它会把它下面的函数进行封装。

把f1这个函数作为def outer(func)的参数传进去!就是:f1()=outer(f1)

然后def outer(func):  == outer(f1)就会执行:

def inner():
        print('log')
        return func()  # func() == 原函数f1
    return inner    # 然后把封装后的函数return的结果给原函数 (= f1)

@outer就相当于做了一个重新打了个包,包含原数据和新增数据,然后变成新的包给"f1"

def f1()  <==> def inner()

@outer
def f1():           #  == def inner():
    print("F1")     #        print('log')
                    #        func()

 

被装饰的函数如果有参数呢?

#  装饰函数使用参数
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"

print(f1('test1'))

print(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、嵌套需要注意的是,最外层包裹内层,执行时是从上往下,考虑嵌套后代码执行顺序。

 

迭代器和生成器

1、迭代器

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件

特点:

  1. 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
  2. 不能随机访问集合中的某个值 ,只能从头到尾依次访问
  3. 访问到一半时不能往回退
  4. 便于循环比较大的数据集合,节省内存
>>> a = iter([1,2,3,4,5])
>>> a
<list_iterator object at 0x101402630>
>>> a.__next__()
1
>>> a.__next__()
2
>>> a.__next__()
3
>>> a.__next__()
4
>>> a.__next__()
5
>>> a.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

 

2、生成器

一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator);如果函数中包含yield语法,那这个函数就会变成生成器;

def func():
    yield 1
    yield 2
    yield 3
    yield 4

上述代码中:func是函数称为生成器,当执行此函数func()时会得到一个迭代器。

>>> temp = func()
>>> temp.__next__()
1
>>> temp.__next__()
2
>>> temp.__next__()
3
>>> temp.__next__()
4
>>> temp.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

3、实例

a、利用生成器自定义range

def myrange(arg):
    start = 0
    while True:
        if start > arg:
            return
        yield start
        start += 1

ret = myrange(3)
r1 = ret.__next__()
print(r1)
r2 = ret.__next__()
print(r2)
r3 = ret.__next__()
print(r3)
r4 = ret.__next__()
print(r4)

b、利用迭代器访问range

ret = myrange(3)
for item in ret:
    print(item)

 

递归

在函数体内重复调用自己本身,做到反复运行一个算法达到最终目的然后返回。

def func(n):
    n += 1
    if n >= 4:
        print(n)
        return "end"
    return func(n)

r = func(1)  # 我传递进去个1,经过递归最后给我返回4
print(r)
递归演示(一)
# 1*2*3*4*5*6*7
def func(num):
    if num == 1:
        return 1
    return num * func(num-1)

x = func(7)
print(x)
递归演示(二)

 

其他

1、反射:

利用字符串的形式去对象中(模块)中操作(查找/检测/添加/删除)成员

  • getattr
  • delattr
  • setattr
  • hasattr
# 反射:利用字符串的形式去对象中(模块)中操作(查找/检测/添加/删除)成员
# import commons
# __import__("lib.account")   # 接受字符串得形式导入模块


def run():
    # account/login
    inp = input("请输入访问页面:")
    m, f = inp.split('/')
    obj = __import__('lib.' + m, fromlist=True)
    if hasattr(obj, f):           # 判断这个模块中是否有这个函数
        func = getattr(obj, f)    # 得到这个对象
        func()
    else:
        print("404")


if __name__ == '__main__':
    run()

 

2、python文件中特殊方法

    vars()           # 全部变量
    __builtins__     # 内置所有函数
    __file__         # 当前py文件的路径,有坑,在当前目录下只能得到文件名
    __package__      # 当前在哪个包里
    __cached__       # pyc文件路径
    __doc__          # 这个文件的注视,顶行"""注释内容
    __name__         # 只有执行当前文件时候,当前文件的特殊变量__name__ == mian

 

posted @ 2016-06-03 09:30  梁先森  阅读(221)  评论(0)    收藏  举报