美文网首页
Python 函数基础

Python 函数基础

作者: MrTrying | 来源:发表于2018-09-19 09:17 被阅读23次

函数式编程其中的一个特点就是允许把函数本身作为参数传递给另一个函数,也允许返回一个函数,Python支持函数式编程的这一特点。由于Python允许使用变量,因此Python不是纯函数式编程语言。

先了解函数作为变量是什么意思,以abs函数为例,执行print(abs(-10))的输出结果是10,这里输出的是abs(-10)的返回值;如果执行print(abs)输出结果则是<built-in function abs>,这里输出的则是abs函数。其实abs这个函数名也是变量,同样可以进行传递

f = abs
print(f(-10))

输出结果同样是10,和直接调用其实是一样的,如果这里直接print(f)的输出结果同样也是<built-in function abs>,这说明f函数已经指向了abs函数,这就是函数的传递。这样子,函数就可以作为参数传递

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

sum = addAbs(-10,2,abs)
print(sum)

#输出结果为:12

函数可以作为变量传递,如果函数名在代码中使用了,并且赋了其他的值,这时候去调用这个变量将使用的是后面赋予的值,而不是原本的函数。实际上一般也不允许这么写

map

map函数接收两个参数,一个函数,一个Iterablemap将对传入的Iterable中的元素执行传入的函数,最后返回新的Iterable

def f(x):
    return x + 10

r = map(f,[1,2,3,4,5,6,7,8,9])
print(list(r))
#输出结果为11~19

将函数f传入map迭代生成listmap作为高阶函数,将函数的规则运算抽象,不仅仅可以计算x+10还可以计算更加复杂的函数。

reduce

reduce函数同样也是接收函数和一个Iterablereduce函数的作用就是将传入函数的结果与Iterable的下一个元素累积计算,效果上感觉有点类似递归。

from functools import reduce

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

print(reduce(add, [1, 2, 3, 4, 5]))
#输出结果为15

可以看出来是1~5的累加,这么看reduce函数并没有什么特别的地方,但是给出了一个mapreduce结合使用的例子,将str转换成int,很简单,但是很好地使用了map函数和reduce函数

from functools import reduce

DIGTIS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(c):
        return DIGTIS[c]
    return reduce(fn, map(char2num, s))

或者使用lambda表达式

from functools import reduce

DIGTIS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def char2num(c):
    return DIGTIS[c]

def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))

有时候觉得大神的想法真多,一个简单地东西,还能思考多种写法。反思一下,其实学习的过程中很多东西看懂了,却没有比较深入的使用,需要适当的做一些扩展的思考来练习知识的使用,不然知识始终只是知识而已。

对于mapreduce两个方法,Google很久以前写过一篇论文MapReduce: Simplified Data Processing on Large Clusters,看了能更好地理解map/reduce

filter

filter函数,用于过滤,与map相似接收一个函数和一个Iterablefilter函数将传入的函数以此作用于传入的序列,函数返回TrueFalse表示该元素是否保留。

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

l = list(filter(check_empty, ['X', '', 'Y', None, 'Z', '   ']))
print(l)
#输出结果:['X', 'Y', 'Z']

sorted

Python内置的sorted函数,可以对list进行排序,不仅仅是int,字符串类型也可以进行排序,字符串排序则是按照ASCII的大小进行比较的。

l = [86, 16, -64, 0, -3]

print(sorted(l))
#输出结果:[-64, -3, 0, 16, 86]

这是对int正常大小的排序,sorted还可以接收一个key函数来实现自定义排序,比如:按绝对值排序

l = [86, 16, -64, 0, -3]

print(sorted(l, key=abs))
#输出结果:[0, -3, 16, -64, 86]

接下来看看字符串的排序

l = ['MrTrying', 'C.C.', 'nothing', 'bruce', 'jack wharton']

print(sorted(l))
#输出结果:['C.C.', 'MrTrying', 'bruce', 'jack wharton', 'nothing']

ASCII中,大写字符的的ASCII值是小于小写字符的,所以默认情况下,大写字母开头的会排在前面;当然通过key可以忽略大小写的比较

l = ['MrTrying', 'C.C.', 'nothing', 'bruce', 'jack wharton']

print(sorted(l, key=str.lower))
#输出结果:['bruce', 'C.C.', 'jack wharton', 'MrTrying', 'nothing']

sorted函数还有第三个参数reversereverse=True可以将排序反序

l = ['MrTrying', 'C.C.', 'nothing', 'bruce', 'jack wharton']

print(sorted(l, key=str.lower, reverse=True))
#输出结果:['nothing', 'MrTrying', 'jack wharton', 'C.C.', 'bruce']

基本可以看出sorted函数可以将排序算法抽象的功能,提升了排序的扩展性,同时代码还是相当简洁的。sorted函数对list进行排序,就可以对dictkey或者value进行排序。

返回(一个)函数

Pyhton支持函数式编程,函数可以作为参数传递,也可以作为返回值使用。

这是一个求和函数

def calc_sum(*args):
    x = 0
    for n in args:
        x = x + n
    return x

可以将这个函数作为返回值return出去,被赋值的函数使用方式是一样的。

def lazy_sum():
    def calc_sum(*args):
        x = 0
        for n in args:
            x = x + n
        return x
    return calc_sum

f = lazy_sum()
print(f)
#输出结果:<function lazy_sum.<locals>.calc_sum at 0x1047a02f0>

print(f(1,2,3,4,5))
#输出结果:15

每次调用lazy_sum()都返回一个新函数,即使传参是一样的

闭包

闭包,简单一点理解就是:能够读取其他函数内部变量的函数。

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

f1, f2, f3 = count()

上面的count函数返回一个函数的list,函数是循环生成的,其中f()使用了count()中的局部变量ii的引用并保存在返回的函数,这种形式称之为闭包

可能觉得返回值应该是149,但是分别调用f1()f2()f3()输出结果全部都是9。原因在于引用了ii在循环中递增,随着f1()f2()f3()三个函数的生成i变成了3,导致最后输出都是9所以,使用返回闭包时,最好不要引用可变变量。

使用java会知道,java不支持函数式编程,所以传递函数的替代方式是传递一个实现的接口,而在实例化这个接口的代码块中,如果接口的方法想要使用自身作用于意外的变量,这个变量就必须使用final修饰。

上面的代码稍作修改,使用g()返回f()f()所使用的n在调用g()时传入不会在发生改变,进而f()所保存的n不会发生改变,再调用f1()f2()f3()就可以正常输出149

def count():
    def g(n):
        def f():
            return n * n
        return f
    fs = []
    for i in range(1, 4):
        fs.append(g(i))
    return fs
    
f1, f2, f3 = count()

lambda表达式

函数传递时,可以使用匿名函数,简化代码

l = list(map(lambda x: x * x, [1, 2, 3, 4, 5]))
print(l)
#输出结果:[1, 4, 9, 16, 25]

lambda后跟的就是函数的参数,:后是函数返回值得表达式。

上面代码就可以看到,使用lambda x: x * x代替了

def f(x):
    return x * x

同样,lambda表达式也可以作为函数返回

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

Decorator(装饰器)

Decorator是高阶函数,可以在不修改原有还是的基础上,动态的增加函数功能。

先定义一个打印日志的Decorator,可以看到log函数传入一个函数,返回在内部定义的wapper函数。wapper函数调用log传入的函数func,并在调用func函数前打印了函数名

import functools

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

log中返回的wrapper函数的参数(*args,**kw)可以接受任意参数的调用。

Decorator可以借助pyhton@语法放在定义函数的代码处。

@log
def test():
    print('MrtTying')

test()
#输出结果:
#call test():
#MrtTying

这里看代码的写法,decorator相当于执行了test = log(test),只是在原有的test函数本身做了一个代理。如果需要自定义日志部分的分本呢?可以给之前的log函数在套一层,log函数传入自定义文本,decorator传入func参数,具体如下:

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

@log('call')
def test():
    print('MrtTying')

而函数的实际调用相当于test = log('call')(test)

到这里已经有两种decorator写法了,而wrapper函数都有一个@functools.wraps(func)decorator。这里分析一波,log最终返回的是wrapper函数,虽然wrapper返回了传入函数的返回值,当外部调用test函数时,实际上使用的是wrapper函数,只是test函数重新指向了wrapper函数。

这里就会出现一个问题,函数是一个对象,拥有__name__等属性,而wrapper函数的属性与传入的func并不相同,所以我们需要将原有函数func的属性传递给wrapper函数,否则有些依赖函数签名的代码会出错。

Pyhton的内置函数functools.wraps可以帮助我们完成函数属性的复制。用法如上两段代码

偏函数

偏函數是对可以传入默认值的函数的一种扩展,可以重新创建一个函数设定这个传入函数的默认值,同样也是可以重新设置的。

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

print(int2('01000000'))
#输出结果:64

这是我们自己的实现方式,重新定义了int2函数来转化2进制数。pythonfunctools.partial可以帮助我们创建一个偏函数

import functools

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

print(int2('01000000'))
#输出结果:64

上述代码中的int2函数,同样可以添加base参数,例如:int2('01000000',base=10)得到的结果将是'01000000'的10进制结果,也就是1000000

创建偏函数时,实际可以接收函数对象、*args和***kwint中只是固定了关键字参数是base

偏函数也就通过固定原函数的部分参数来简化函数调用。

相关文章

  • python基础笔记

    Python基础 函数:

  • Python入门

    Python3教程 安装Python 第一个Python程序 Python基础 函数 高级特性 函数式编程 模块 ...

  • python入门套路

    Python基础 基础数据类型 bool string list tuple dictionary 基础函数 he...

  • python基础-07-函数

    python基础-函数 1.函数的定义 例子: 2.python中函数的参数详解 必备参数demo: 默认参数de...

  • Python基础-函数

    Python基础-函数 1.函数定义 2.参数传值 3.内置函数

  • 理解Python中的闭包

    Python基础 在Python中,函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。...

  • Python基础 - 函数基础

    知识回顾 列表,字典,元组,集合 列表(list):[];可变,有序;元素是任何类型的数据增:append,ins...

  • Python从入门到精通

    Python语法的三个阶段 Python基础语法函数是编程 Python进阶语法面向对象编程 Python高级语法...

  • #python基础入门#目录

    python基础入门_01 保留字常用函数turtle库基本数据类型time库 python基础入门_02 程序的...

  • 2018-07-25Python(7)

    python基础语法(7) 函数下 lambda函数lambda函数有自己的作用域。代码示例:def makeAc...

网友评论

      本文标题:Python 函数基础

      本文链接:https://www.haomeiwen.com/subject/mpybnftx.html