【python3的学习之路九】函数式编程

变量作用域

变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种,分别是:

  • L (Local) 局部作用域
  • E (Enclosing) 闭包函数外的函数中
  • G (Global) 全局作用域
  • B (Built-in) 内建作用域

以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。

x = int(2.9)  # 内建作用域
 
g_count = 0  # 全局作用域
def outer():
    o_count = 1  # 闭包函数外的函数中
    def inner():
        i_count = 2  # 局部作用域

global和nonlocal关键字

当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字。

num = 1
def fun1():
    global num  # 需要使用 global 关键字声明
    print(num) 
    num = 123
    print(num)
fun1()
print(num)

如果要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量nonlocal关键字,如下:

def outer():
    num = 10
    def inner():
        nonlocal num   # nonlocal关键字声明
        num = 100
        print(num)
    inner()
    print(num)
outer()

传入函数

变量也可以指向函数,函数的参数能接受变量,那么一个函数就可以接受另一个函数作为参数,这种函数就称之为高阶函数。

def add(x, y, f):
    print(f(x) + f(y))

add(-5, 6, abs)   # 11
  • map

map()函数接受俩个参数,一个是函数,一个是Iterator,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator。

def f(x):
    return x * x

r = map(f, [1, 2, 3, 4, 5])
print(r)   # [1, 4, 9, 16, 25]
print(list(map(str, [1, 2, 3, 4, 5])))   # ['1', '2', '3', '4', '5']
  • filter

filter()函数用于过滤序列。filter()函数也接受一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True和False决定保留还是丢弃元素。

def is_odd(n):
     return n % 2 == 1

def not_empty(s):
    return s and s.strip()

print(list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])))     # [1, 5, 9, 15]
print(list(filter(not_empty, ['A', '', 'b', ' ', 'c', None]))) #['A', 'b', 'c']

注意:filter()函数转换成list

练习题:取出1~1000中的回数。回数是指从左向右读和从右向左读都是一样的数,例如12321,909。

def get(l):
    s = str(l)
    return s == s[::-1]  # 请回顾切片

print(list(filter(get, range(1,1000))))
  • sorted

sorted()函数可以对list进行排序

sorted([36, 5, -12, 9, -21])  # [-21, -12, 5, 9, 36]
sorted([36, 5, -12, 9, -21], key = abs) #[5, 9, -12, -21, 36]
sorted(['bob', 'about', 'Zoo', 'Credit']) # ['Credit', 'Zoo', 'about', 'bob'] 对字符串排序,是按照ASCII的大小比较的
sorted(['bob', 'about', 'Zoo', 'Credit'], key = str.lower) # ['about', 'bob', 'Credit', 'Zoo']
sorted(['bob', 'about', 'Zoo', 'Credit'], key = str.lower, reverse = True) # ['Zoo', 'Credit', 'bob', 'about']

返回函数

当我们调用c时返回的是求和函数,每次调用都会返回一个新的函数,即使传入相同的参数,所以f1和f2调用的结果互不影响。

def c(*args):
    def sum():
        ax = 0
        for n in args:
           ax = ax + n
        return ax
    return sum

print(c(1, 3, 4, 5, 6)())
f1 = c(1, 3, 5, 7, 9)
f2 = c(1, 3, 5, 7, 9)
print(f1 == f2)   # False

匿名函数

  • 关键字lambda表示匿名函数,冒号前面的x表示函数参数。
  • 匿名函数只能有一个表达式,不用写return,返回值就是该表达式的结果
  • 匿名函数没有名字,不必担心函数名冲突
  • 匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数
list(map(lambda x: x * x, [1, 2, 3, 4, 5]))
# def f(x):
#     return x * x

def build(x, y):
    return lambda: x * x + y * y

装饰器

在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
我们定义一个能打印日志的decorator——log,它能接受一个函数作为参数,并返回一个函数。调用time()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志。

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

@log             # 相当执行语句now = log(now)
def time():
    print('2015-12-09')

如果decorator本身需要传入参数、那么需要编写一个返回decorator的高阶函数。比如,要自定义log的文本。

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('%s %s():' %(text, func.__name__))
            return func(*args, **kwargs)
        return wrapper
    return decorator

@log('execute')           # 相当执行语句now = log('execute')(now)
def time():
    print('2015-12-09')

因为返回的那个wrapper()函数名字就是’wrapper’,所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写 wrapper._ name_ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的。

偏函数

通过设定参数的默认值,可以降低函数调用的难度,而偏函数就可以做到这一点。
假设要转换大量的二进制字符串,每次都传入++int(x, base = 2)++非常麻烦,我们就可以定义一个函数,把默认的base = 2传进去

def int2(x, base = 2)
    return int(x, base)

functools.partial就帮助我们创建一个偏函数,不需要我们自定义int2()

import functools
int2 = functools.partial(int, base = 2)

猜你喜欢

转载自www.cnblogs.com/CSgarcia/p/9706136.html
今日推荐