pthon之函数式编程


函数式编程是一种抽象计算的编程范式。

不同语言的抽象层次不同:计算机硬件->汇编语言->C语言->Python语言

                                  指令        ->           ->函数  ->函数式

                                  计算机————————————>计算

函数式编程的特点:把计算视为函数不是指令

纯函数式编程:不需要变量,没有副作用,测试简单。

支持高阶函数,代码简单。

python作为一种支持函数式编程的语言的特点:

1.不是纯的函数式编程,允许有变量。

2.支持高阶函数,函数也可以作为变量输入

3.支持闭包,有了闭包之后就能够返回函数。

4.有限度的支持匿名函数

高阶函数的特点:

1.变量可以指向函数

>>> abs(-10)
10
>>> abs
<built-in function abs>
>>> f = abs
>>> f(-20)
20

2.函数名其实就是指向函数的一个变量

>>> abs = len
>>> abs([1,2,3])
3

这里面我们将len这个函数名所直接指向的函数赋给了abs,这个时候,abs就能够计算list的长度了。

高阶函数:能够接收函数做参数的函数

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

>>> add(1,-2,abs)
3
>>> add(1,-2,abs())
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    add(1,-2,abs())
TypeError: abs() takes exactly one argument (0 given)

在这里就展示了将函数作为参数,这里面我们的函数只能是传入一个变量名。

map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回。

>>> def f(x):
    return x*x

>>> print (map(f,[1,2,3,4,5,6,7,8,9]))
<map object at 0x0404C9D0>
>>> print (list(map(f,[1,2,3,4,5,6,7,8,9])))
[1, 4, 9, 16, 25, 36, 49, 64, 81]

这里是python3.0的版本,而3.0的版本在生成list的时候我们就需要加一个list,如果是2.0的版本我们就不要添加一个list了

def f(x):
    return x*x
print map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 10, 25, 36, 49, 64, 81]

reduce()函数也是Python内置的一个高阶函数。reduce()函数接收的参数和 map()类似,一个函数 f,一个list,但行为和 map()不同,reduce()传入的函数 f 必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值。

reduce()还可以接收第3个可选参数,作为计算的初始值。

def f(x, y):
    return x + y
reduce(f, [1, 3, 5, 7, 9], 100)
125

但是在python3.0版本当中就不包含reduce函数了。如果需要使用的话呢就需要使用from functools import reduce.

>>> def f(x,y):
    return x + y

>>> from functools import reduce
>>> reduce(f,[1,3,5,7,9])
25
>>> reduce(f,[1,3,5,7,9],100)
125

filter()函数是 Python 内置的另一个有用的高阶函数,filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。

>>> def is_odd(x):
    return x % 2 == 1

>>> list(filter(is_odd,[1,4,6,7,9,12,17]))
[1, 7, 9, 17]

利用filter(),可以完成很多有用的功能,例如,删除 None 或者空字符串

>>> def is_not_empty(s):
    return s and len(s.strip()) > 0

>>> list(filter(is_not_empty,['test',None,'','str',' ','END']))
['test', 'str', 'END']

s.strip(rm) 删除 s 字符串中开头、结尾处的 rm 序列的字符

rm为空时,默认删除空白符(包括'\n', '\r', '\t', ' ')

 sorted()也是一个高阶函数,它可以接收一个比较函数来实现自定义排序,比较函数的定义是,传入两个待比较的元素 x, y,如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。

 

>>> sorted([36,5,12,9,21],reverse=True)
[36, 21, 12, 9, 5]

在python3.0中我们进行了改动,只要我们将这个reverse改为True就可以进行倒序的排法,而不需要自己定义新的倒序排法。

由于python3.0和2.0的版本不同,sorted进行了很大的改动。

在Python3.0当中呢我们的比较函数只能有一个函数

>>> def L_upper(x):
    return x.lower()

>>> sorted(['adam','paul','dean'],key=L_upper,reverse=True)
['paul', 'dean', 'adam']

就如这样,这个key就是前面的list在排序时侯的关键字

Python的函数不但可以返回int、str、list、dict等数据类型,还可以返回函数

>>> def f():
    print('call f()...')
    def g():
        print("call g()...")
    return g

>>> x=f()
call f()...
>>> x
<function f.<locals>.g at 0x03FB9FA8>
>>> x()
call g()...

在这里面x便是一个函数,也即是那个函数g()

而在打出x()便是调用函数

这种返回函数的操作可以延缓函数的执行。

>>> def calc_sum(lst):
    def lazy_sum():
        return sum(lst)
    return lazy_sum

>>> f=calc_sum([1,2,3,4])
>>> f
<function calc_sum.<locals>.lazy_sum at 0x04232078>
>>> f()
10

而如果没有这种反回函数的功能的话呢

>>> def cal_sum(lst):
    return sum(lst)

>>> f=cal_sum([1,2,3,4])
>>> f
10

便会直接的进行输出

>>> def calc_sum(lst):
    def lazy_sum():
        return sum(lst)
    return lazy_sum

上面的代码便是闭包的简单的函数

内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)

闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变

>>> def count():
    fs = []
    for i in range(1,4):
        def f():
            return i*i
        fs.append(f)
    return fs

>>> f1,f2,f3 =count()
>>> f1()
9
>>> f2()
9
>>> f3()
9

这里因为i是一个可变的函数所以我们的函数调用三次就会出现变成3

>>> def count():
    fs=[]
    for i in range(1,4):
        def f(m=i):
            return m*m
        fs.append(f)
    return fs

>>> f1,f2,f3=count()
>>> f1()
1
>>> f2()
4
>>> f3()
9

我们只要将变量保存在m里面就好了

高阶函数可以接收函数做参数,有些时候,我们不需要显式地定义函数,直接传入匿名函数更方便。

>>> list(map(lambda x: x*x,[1,2,3]))
[1, 4, 9]

关键字lambda 表示匿名函数,冒号前面的 x 表示函数参数

使用匿名函数,可以不必定义函数名,直接创建一个函数对象,很多时候可以简化代码

匿名函数有个限制,就是只能有一个表达式不写return,返回值就是该表达式的结果。

在有些时候返回函数的时候也可以返回匿名函数。

装饰器是为了简化代码的书写而发明的一种语法,在这里我们的装饰器还有一种很简单的调用方法就是@

>>> def log(f):
    def fn(x):
        print ("call"+f.__name__+'()...')
        return f(x)
    return fn

>>> from functools import reduce
>>> @log
def factorial(n):
    return reduce(lambda x,y:x*y,range(1,n+1))

>>> print (factorial(10))
callfactorial()...
3628800

在这里我们调用的只能是一个只包含有一个参数的函数。因为我们在定义的时候说的就是一个包含只有一个参数的函数。

>>> @log
def add(x,y):
    return x+y

>>> print(add(1,2))
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    print(add(1,2))
TypeError: fn() takes 1 positional argument but 2 were given
 
 

  

 

如上所示我们调用的是一个用有两个参数的add()胡散户,所以系统会报错,这里面报错的理由是fn()拥有的是一个参数,而add()用有的是两个参数,这里就报错了。

要让 @log 自适应任何参数定义的函数,可以利用Python的 *args 和 **kw,保证任意个数的参数总是能正常调用

>>> def log(f):
    def fn(*args,**kw):
        print('call '+f.__name__+"()...")
        return f(*args,**kw)
    return fn

>>> @log
def factorial(n):
    return reduce(lambda x,y:x*y,range(1,n+1))

>>> print (factorial(10))
call factorial()...
3628800
>>> @log
def add(x,y):
    return x+y

>>> print(add(1,2))
call add()...
3

装饰器也是可以带参数的。比如:在上面输出的时候我们需要简单的表明这些函数是否重要

>>> def log(prefix):#prefix指的是前缀的意思,在这里是随便的命名
    def log_decorator(f):
        def wrapper(*args,**kw):
            print('[%s]%s()...'%(prefix,f.__name__))
            return f(*args,**kw)
        return wrapper
    return log_decorator

>>> @log('DEBUG')
def test():
    pass

>>> print(test())
[DEBUG]test()...
None

由于我们需要添加一个另外的参数所以我们需要在定义的时候就需要添加一个参数,如果这样的话我们就像需要多添加一个循环。

这个三层循环运用了闭包的知识,如果哦我们拆开它就会出现错误,因为最里面的函数调用了外面的参数,所以才会出现错误,如果我们没有装饰器的话呢,我们在其他的编程语言下,就需要使用更多的代码。这也是装饰器的作用。我们学习Python是喜欢它的简洁性、易懂性以及它那超过了两千个库所带来的强大的功能。

而装饰器正是实现这一途径最好的方法。

>>> def log(prefix):
    def log_decorator(f):
        def wrapper(*args,**kw):
            print('[%s]%s()...'%(prefix,f.__name__))
            return f(*args,**kw)
        return wrapper
    return log_decorator

>>> @log('DEBUG')
def test():
    pass

>>> print(test.__name__)
wrapper

在这里面我们会发现我们的函数的名字已经变化了。这种情况就会导致如果我们编写一些需要依赖名字的函数的话我们就会出错。(decorator还改变了函数的__doc__等其它属性。)

如果要让调用者看不出一个函数经过了@decorator的“改造”,就需要把原函数的一些属性复制到新函数中

所以Python内置的functools可以用来自动化完成这个“复制”的任务:

>>> import functools
>>> def log(f):
    @functools.wraps(f)
    def wrapper(*args,**kw):
        print("call...")
        return f(*args,**kw)
    return wrapper

>>> @log
def test():
    pass

>>> print(test.__name__)
test

但是我们还需要注意一点,就是我们的函数里面的那个参数名有的时候会发生改变的,因为我们的参数虽然是进行了复制,但是参数名是可以进行变化的,因此,即使是只有一个参数的简单函数我们也无法只能简答的保存函数参数最开赋给的那个参数名,而不是后来传入的那个参数名。

int()是我们函数自带的一个转换函数,我们在使用int()进行转换的时候我们会发现int()不止有一个参数,它还有一个参数,这个参数是用来指定,函数的转换进制的。

>>> int ('123')
123
>>> int ('123',8)
83

其实,int这个函数的第二个函数已经被python给默认是10了,这是为了能够简便我们的输入。这种函数我们称之为偏函数

functools.partial就是帮助我们创建一个偏函数的。

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

functools.partial还可以把参数多的函数变为参数少的函数。

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com