1. 函数式编程(函数是一等公民)functional programming

函数式编程是指用一系列函数解决问题

1、函数本身可以赋值给变量,赋值后变量绑定的是函数
2、允许将函数本身作为参数传入另一个函数
3、允许函数返回一个函数
  • 函数式编程的好处:
1. 每一个函数完成细小的功能,一系列函数的任意组合可以解决大问题
2. 函数仅接受输入并产生输出,不包含任何可能影响输出的内部状态
  • 函数的可重入性:
当一个函数,调用时参数一定时,结果一定的函数称为可重入函数

说明:可重入函数一定不能访问除局部变量外的变量
y = 2
def mypow(x):  # 此函数为不可重入函数
    return x**y

print(mypow(5))
y = 3
print(mypow(5))

def mypow1(x, y):
    return x**y

print(mypow1(5, 2))
25
125
25

2. 闭包 closure

将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象称为闭包

  • 说明:
如果一个内嵌函数访问的外部嵌套函数作用域的变量,则这个函数就是闭包
  • 闭包必须满足以下三个条件:
1. 必须有一个内嵌函数
2. 内嵌函数必须引用外部函数中的变量
3. 外部函数返回值必须是内嵌函数
def make_power(y):
    print("y的值是:", y)
    def f(x):
        return x ** y  # <<<---注意此处的y为外部嵌套函数内的y
    return f

pow2 = make_power(2)
print(pow2(5))
y的值是: 2
25
  • 应用场景:
1.使用闭包代替全局变量
2.函数外或在其他函数中访问某一函数内部的参数
3.在函数执行之前为要执行的函数提供具体参数
4.在函数执行之前为函数提供只有在函数执行或引用时才能知道的具体参数
6.暂停执行
7.包装相关功能

3. 高阶函数:High Order function

  • 什么是高阶函数,满足下面两个条件之一的就为高阶函数:
  1. 函数接受一个或多个函数作为参数传入
def fn():
    print("hello world!")

def f2(f):
    f()  # f绑定了fn,调用f 相当于调用了fn
    print("hello everbody!")

f2(fn)  # 调用f2函数,传入fn函数
hello world!
hello everbody!
def myf(iterable, f):
    m = f(iterable)
    return m

L = [1, 3, 5, 7, 9]
print("L的和是:", myf(L, sum))
print("L的最大值是:", myf(L, max))
print("L的最小值是:", myf(L, min))
L的和是: 25
L的最大值是: 9
L的最小值是: 1
  1. 函数返回一个函数
def get_op():
    s = input("输入您要做的操作:")
    if s == "求最大":
        return max
    elif s == "求最小":
        return min
    elif s == "求和":
        return sum

L = [2,4,6,8,10]
print(L)

f = get_op()
print(f(L))
[2, 4, 6, 8, 10]
输入您要做的操作:求和
30

4. 内置函数(8) 高阶函数

  1. reduce(func, lst),其中func必须有两个参数。每次func计算的结果继续和序列的下一个元素做累积计算。

在 Python3 中,reduce() 函数已经被从全局名字空间里移除了,它现在被放置在 fucntools 模块里,如果想要使用它,则需要通过引入 functools 模块来调用 reduce() 函数

from functools import reduce

def add(x,y):
    return x + y

print (reduce(add, range(1, 101)))
5050
  1. map(func, *iterable), 用函数对可迭代对象中的每一个元素作为参数, 计算出新的可迭代对象,当最短的一个迭代器完成迭代后结束
def power(x):
    return x**2

L = []
mit = map(power, range(1, 10)) #返回一个新的可迭代对象,此可迭代对象可以生成一系列的平方
for x in mit:
    L.append(x)
print(L)

# 等价于以上5行
L = [x for x in map(power, range(1, 10))]
print(L)

def mypow(x, y):
    return x ** y

# 生成一个可迭代对象,此可迭代对象可以生成1**4,2**3,3**2, 4**1
L = [x for x in map(mypow, range(1, 5), range(4, 0, -1))]
print(L)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
[1, 4, 9, 16, 25, 36, 49, 64, 81]
[1, 8, 9, 4]

练习:求1**2 + 2**2 + 3**2 + .......+9**2的和

def fun(x):
    return x**2

print(sum(map(fun, range(1, 10))))
285

练习: 求1**3 + 2**3 + 3**3 + .......+9**3的和

print(sum(map(lambda x:x**3, range(1, 10))))
2025

练习: 求1**9 + 2**8 + 3**7 + .......+9**1的和

print(sum(map((lambda x, y: x**y), range(1, 10), range(9,0,-1))))
11377

练习: 有一个字符串,s = "ABCDEFG" 和数字 L = [1, 2, 3, 4, 5, 6, 7] 生产一个列表:L2 = ['A1', 'B2', 'C3',.......]

s = "ABCDEFG"
L = [1, 2, 3, 4, 5, 6, 7]

def fun(x, y):
    return x+str(y)

L2 = []
for x in map(fun, s, L):
    L2.append(x)
print(L2)
print([x for x in map(fun, s, L)])
['A1', 'B2', 'C3', 'D4', 'E5', 'F6', 'G7']
['A1', 'B2', 'C3', 'D4', 'E5', 'F6', 'G7']
  • filter(function or None, iterable)
用 iterable 中函数 function 返回真的那些元素,构建一个新的迭代器。iterable 可以是一个序列,一个支持迭代的容器,或一个迭代器。如果 function 是 None ,则会假设它是一个身份函数,即 iterable 中所有返回假的元素会被移除。

请注意, filter(function, iterable) 相当于一个生成器表达式,当 function 不是 None 的时候为 (item for item in iterable if function(item));function 是 None 的时候为 (item for item in iterable if item) 。

练习:生成1-20之间所有的偶数的列表

def isodd(x):
    return x % 2 == 1

#生产奇数的列表
print([x for x in filter(isodd, range(21))])
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

练习: 1-100以内所有素数的列表,L = [2, 3, 5, 7, 11, ....., 97]

def prime(x):
    for i in range(2, int(x**0.5 + 1)):
        if x % i == 0:
            return False
    else:
        return True

primes = [x for x in filter(prime, range(2, 100))]
print(primes)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
  • sorted(iterable, *, key=None, reverse=False)
根据 iterable 中的项返回一个新的已排序列表。

key 指定带有单个参数的函数,用于从 iterable 的每个元素中提取用于比较的键 (例如 key=str.lower)。默认值为 None (直接比较元素)。
reverse 为一个布尔值。 如果设为 True,则每个列表元素将按反向顺序比较进行排序。
L = [5, -2, -4, 0, 3, 1]
L2 = sorted(L)
print(L2)

L3 = sorted(L, key=abs)
print(L3)

names = ['Tom', 'Jerry', 'Spike', 'Tyke']
L4 = sorted(names)
print(L4)
L5 = sorted(names, key = len)
print(L5)

def fun(k): # 此函数内一定只有一个参数,此函数用于绑定元素,并返回此元素的排序依据
    return k[::-1]

L6 = sorted(names, key=fun)
print(L6)
[-4, -2, 0, 1, 3, 5]
[0, 1, -2, 3, -4, 5]
['Jerry', 'Spike', 'Tom', 'Tyke']
['Tom', 'Tyke', 'Jerry', 'Spike']
['Spike', 'Tyke', 'Tom', 'Jerry']

练习:L列表中,存储书名,价钱和页数

L = [{"name":Python基础教程, "price":58, "pages":866},
     {"name":C语言, "price":98, "pages":982},
     {"name":C++语言, "price":56, "pages":1024},
     {"name":Java, "price":79, "pages":691}]

按价钱排序,然后输出顺序(升序), 按页数排序,然后输出顺序(升序)

L = [{"name":"Python", "price":58, "pages":866},
     {"name":"C", "price":98, "pages":982},
     {"name":"C++", "price":56, "pages":1024},
     {"name":"Java", "price":79, "pages":691}]

def fun(x):
    return x['price']

def fun1(x):
    return x['pages']

L1 = sorted(L, key=fun)
L2 = sorted(L, key=fun1)

def show_books(lst):
    for x in lst:
        print("%10s %3d %5d" % (x['name'],x['price'],x['pages']))

show_books(L1)
show_books(L2)
C++  56  1024
    Python  58   866
      Java  79   691
         C  98   982
      Java  79   691
    Python  58   866
         C  98   982
       C++  56  1024

5. 递归函数:(recursion function)

函数直接或间接的调用自身(效率不高,递归有深度限制,所以慎用递归), 编写递归函数时,必须告诉它什么时候停止递归。正因如此,每个递归函数都有两个部分:基线条件和递归条件。递归条件指的是函数调用自己,而基线条件指的是函数不再调用自己,从而避免形成无限循环。

  • 说明
递归的实现方法:先假设函数已经实现

递归一定要控制递归的层数(深度),当符合某一条件时要终止递归调用,当进入更深层递归时问题规模都要有所减少,几乎所有的递归都能用while循环来代替.
  • 以下两种形式的调用时递归,并无法返回
# 直接调用
def f():
    f() # 调用自己

f() # 调用此函数

# 间接调用
def fa():
    fb()

def fb()
    fa()

fa()
import time
def f(n):
    if n <= 0:  # 当n条件满足时终止调用终止自己,来终止递归调用
        return
    time.sleep(0.5)
    print("进入函数时:", n)
    f(n-1)
    print("退出函数前:", n)

f(3)  # 调用函数
进入函数时: 3
进入函数时: 2
进入函数时: 1
退出函数前: 1
退出函数前: 2
退出函数前: 3

练习:写一个函数,用递归方式打印1-10的数字

import time

def f(n):
    print(n)
    time.sleep(1)
    if n >= 10:  # 设置返回条件
        return
    f(n+1)

f(1)
1
2
3
4
5
6
7
8
9
10

练习:写一个函数sum_n,用递归来实现 n+(n-1)+(n-2)+....+3+2+1 的和

def sum_n(n):
    if n == 1 :
        return 1
    return n + sum_n(n - 1)

print(sum_n(10))
55

练习:编写程序,用递归实现阶乘myfac(x)返回x的阶乘

def fact(x):
    if x == 1:
        return 1
    else:
        return x * fact(x - 1)

print(fact(5))
120

练习: 写程序,算出1-20的阶乘的和: 1!+ 2!+ 3!+4!+ 5!+…………+20!

def fun(x):
    if x == 1:
        return 1
    return x * fun(x-1)

print(sum(map(fun, range(1, 21))))
2561327494111820313

6. 装饰器 decorator

  • 什么是装饰器:
1、装饰器是一个函数,主要是用来包装另一个函数或类
2、装饰的目的是在不改变原函数或类的源代码和调用方式的情况下,改变被包装对象的行为
3、函数装饰器是指一个装饰器函数传入参数的是一个函数,返回也是一个函数
  • 实现装饰器知识储备:
1、函数即“变量”
2、高阶函数
3、嵌套函数
def now():
    print('2015-3-25')

# 函数即“变量”
f = now
print(f.__name__, now.__name__)
now now

假设要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。本质上,decorator就是一个返回函数的高阶函数。所以要定义一个能打印日志的decorator,可以定义如下:

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。要借助Python的@语法,把decorator置于函数的定义处:

@log
def now():
    print('2015-3-25')

# 等价于 now = log(now)
now()
call now():
2015-3-25

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
@log('execute')
def now():
    print('2015-3-25')

# 等价于: now = log('execute')(now)
now()
execute now():
2015-3-25

剖析上面的语句,首先执行log(‘execute’),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。以上两种decorator的定义都没有问题,但还差最后一步。因为函数也是对象,它有__name__等属性,但去看经过decorator装饰之后的函数,它们的__name__已经从原来的’now’变成了’wrapper’:

now.__name__
'wrapper'

因为返回的那个wrapper()函数名字就是’wrapper’,所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。不需要编写wrapper.name = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
import functools

# 带有参数的
def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

练习: 用装饰器函数名替换被装饰函数

import functools

def mydeco(fn):  # 此函数为装饰器函数,可以用来装饰带有一个参数的函数
    @functools.wraps(fn)
    def wrapper(*args, **kw):
        print("hello:", args[0])
        return fn(*args, **kw)
    return wrapper

@mydeco
def say_hello(name):
    print("你好!", name)

say_hello("小张")
hello: 小张
你好! 小张

练习: 实现用闭包来包裹原被装饰函数,可以在执行前和执行后加入自定义代码

import functools

def mydeco(fn):
    @functools.wraps(fn)
    def f2(name):  # 此时f2为闭包,因为f2调用了fn 参数
        print("调用函数前。。。。")
        fn(name)
        print("调用函数后。。。。")
    return f2

@mydeco
def say_hello(name):
    print("你好!", name)

say_hello("小张")
调用函数前。。。。
你好! 小张
调用函数后。。。。

练习: 在函数调用时,打印函数最终执行的时间(用装饰器测试函数执行时间案例)

import functools
import time


def f2(f1):
    @functools.wraps(f1)
    def warpper(*args, **kwarge):  # 通用的装饰器的形参列表为(*args, **kwarge) 可以装饰任意参数的函数
        start_time = time.time()
        f1()
        print("the f1 run time is %s" % (time.time() - start_time))
    return warpper


@f2
def f1():
    time.sleep(3)
    print("f1 .......")

f1()
f1 .......
the f1 run time is 3.0043368339538574

练习: 给不同的函数加上同一个装饰器,赋予不同的功能

import functools

user, passwd = "aaa", "123"

def auth(auth_type):
    print("auth func:", auth_type)

    def outer_wrapper(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if auth_type == "local":
                username = input("username: ").strip()
                password = input("password: ").strip()

                if user == username and passwd == password:
                    print("\033[32;ImUser has passed authentication\033]Om")
                    res = func(*args, **kwargs)  # from home
                    print("_______after________")
                    return res
                else:
                    exit("\033[32;ImInvalid username or password\033]Om")
            elif auth_type == "ldap":
                print("其他操作。。。。。。。")
        return wrapper
    return outer_wrapper

def index():
    print("index page.......")

@auth(auth_type="local")
def home():
    print("home page.......")
    return "*****from home*******"

@auth(auth_type="ldap")
def bbs():
    print("bbs page........")


index()
print(home())
bbs()
auth func: local
auth func: ldap
index page.......
username: aaa
password: 123
[32;ImUser has passed authentication]Om
home page.......
_______after________
*****from home*******
其他操作。。。。。。。
  • 类装饰器会在之后的面向对象编程中介绍