python入门--04函数式编程(代码片段)

快乐崇拜234 快乐崇拜234     2022-10-21     740

关键词:

正文在下面,先打个广告:

概念

编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如Lisp语言。

函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。

高阶函数

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

print(abs(-1))

a = -10
b = 5
f = abs # 将变量f指向abs()函数的引用
def fuc(x, y, fuction):
    return fuction(x) + fuction(y)

print(fuc(a, b, f))

结果;

1
15

map/reduce

如果你读过Google的那篇大名鼎鼎的论文“MapReduce: Simplified Data Processing on Large Clusters”,你就能大概明白map/reduce的概念。

  • map()函数接收两个参数,一个是函数,一个是Iterablemap将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
def f (x):
    return x*x

a = [1,2,3,4,5,6,7,8,9,10]
print(list(map(f, a)))  # 结果:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  • reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
from functools import reduce

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

print(reduce(add, a)) # 结果:55

示例,字符串转数字:

def str2int(str):
    def chr2num(x):
        return "1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"0":0[x]
    def fuc(x, y):
        return x*10+y
    return reduce(fuc, map(chr2num, str))

print(str2int('123215432') + 1)  # 结果: 123215433

filter

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

def a(x):
    if x % 2 == 0:
        return True
    else :
        return False

print(list(filter(a, [1,2,3,4,5,6,7,8,9,10])))  # 结果:[2, 4, 6, 8, 10]

排序sorted

print(sorted([1,2,87,3,-23]))
print(sorted([1,2,87,3,-23], key=abs))

结果:

[-23, 1, 2, 3, 87]
[1, 2, 3, -23, 87]
  • 字符串排序
print(sorted(['bob', 'about', 'Zoo', 'Credit'])) #默认情况下,对字符串排序,是按照ASCII的大小比较的,由于`'Z' < 'a'`,结果,大写字母`Z`会排在小写字母`a`的前面。
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)) # 忽略大小写
print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)) # 倒序排序

结果:

['Credit', 'Zoo', 'about', 'bob']
['about', 'bob', 'Credit', 'Zoo']
['Zoo', 'Credit', 'bob', 'about']

将函数作为返回值

def fuc(x):
    def doubleFuc():
        return x * 2
    return doubleFuc

print(fuc(3)())  # 结果   6

我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行

看下面这个例子:

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

f1 = count()
for i in f1:
    print(i())

输出结果:

9
9
9

全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

def f(i):
    def g():
        return i*i
    return g

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

f1 = count()
for i in f1:
    print(i())

输出结果:

1
4
9

匿名函数(lambda表达式)

在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数:

def f(x):
    return x*x

print(list(map(f, [1, 2, 3, 4])))
print(list(map(lambda x: x * x, [1, 2, 3, 4])))

结果:

[1, 4, 9, 16]
[1, 4, 9, 16]

通过对比可以看出,匿名函数lambda x: x * x实际上就是:

def f(x):
    return x * x

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

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

用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:

f = lambda x: x * x;
print(f)  # 结果:  <function <lambda> at 0x00000000007120D0>
print(f(3))  # 结果: 9

同样,也可以把匿名函数作为返回值返回,比如:

def g1(x):
    return lambda: x*x
print(g1(2)())

def g2(x):
    return lambda x: x*x
print(g2(2)(9))  # 这里g2函数的入参x其实是没有用的

结果:

4
81

装饰器(decorator)

def log(f):
    def wapper(*args, **kw):
        print('call %s(args= %s) begin' % (f.__name__, args))
        result = f(*args, **kw)
        print('call %s (result = %s) end' % (f.__name__, result))
        return result;
    return wapper;

@log
def ff(x, y):
    return x * y

print(ff(2, 3))

结果:


call ff(args= (2, 3)) begin

call ff (result = 6) end
6

@log放到now()函数的定义处,相当于执行了语句:now = log(now)

如果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("aaaaaaaa")
def ff(x, y):
    return x * y

结果:

aaaaaaaa ff():
6

偏函数

Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。要注意,这里的偏函数和数学意义上的偏函数不一样。

在介绍函数参数的时候,我们讲到,通过设定参数的默认值,可以降低函数调用的难度。而偏函数也可以做到这一点。举例如下:

int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换:
int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换;
假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去.
functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2

print(int('123'))
print(int('10', 8))
print(int('b', 16))

import functools

#创建一个偏函数,默认base值是2
int2 = functools.partial(int, base= 2)
print(int2('11'))
print(int2('abc', base=16)) # 虽然定义了偏函数,但是还是可以传入base参数

结果

123
8
11
3
2748

所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

模块

相当于java中的

Python本身就内置了很多非常有用的模块,只要安装完毕,这些模块就可以立刻使用。

我们以内建的sys模块为例,编写一个hello的模块:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

' a test module '

__author__ = 'Michael Liao'

import sys

def test():
    args = sys.argv
    if len(args)==1:
            print('Hello, world!')
    elif len(args)==2:
        print('Hello, %s!' % args[1])
    else:
        print('Too many arguments!')

# 通常用于测试
if __name__=='__main__':
    test()

第1行和第2行是标准注释,第1行注释可以让这个hello.py文件直接在Unix/Linux/Mac上运行,第2行注释表示.py文件本身使用标准UTF-8编码;

第4行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;

第6行使用__author__变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;

以上就是Python模块的标准文件模板,当然也可以全部删掉不写,但是,按标准办事肯定没错。

后面开始就是真正的代码部分。

你可能注意到了,使用sys模块的第一步,就是导入该模块:

import sys

导入sys模块后,我们就有了变量sys指向该模块,利用sys这个变量,就可以访问sys模块的所有功能。

sys模块有一个argv变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称,例如:

运行python3 hello.py获得的sys.argv就是['hello.py']

运行python3 hello.py Michael获得的sys.argv就是['hello.py', 'Michael]

最后,注意到这两行代码:

if __name__=='__main__':
    test()

当我们在命令行运行hello模块文件时,Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该hello模块时,if判断将失败,因此,这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。

我们可以用命令行运行hello.py看看效果:

$ python3 hello.py
Hello, world!
$ python hello.py Michael
Hello, Michael!

如果启动Python交互环境,再导入hello模块:

$ python3
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 23 2015, 02:52:03) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>>

导入时,没有打印Hello, word!,因为没有执行test()函数。

调用hello.test()时,才能打印出Hello, word!

>>> hello.test()
Hello, world!

作用域

在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过_前缀来实现的。

正常的函数和变量名是公开的(public),可以被直接引用,比如:abcx123PI等;

类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author____name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问,我们自己的变量一般不要用这种变量名;

类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc__abc等;

之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。

private函数或变量不应该被别人引用,那它们有什么用呢?请看例子:

def _private_1(name):
    return 'Hello, %s' % name

def _private_2(name):
    return 'Hi, %s' % name

def greeting(name):
    if len(name) > 3:
        return _private_1(name)
    else:
        return _private_2(name)

我们在模块里公开greeting()函数,而把内部逻辑用private函数隐藏起来了,这样,调用greeting()函数不用关心内部的private函数细节,这也是一种非常有用的代码封装和抽象的方法,即:

外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。

安装第三方模块

在Python中,安装第三方模块,是通过包管理工具pip完成的。

如果你正在使用Mac或Linux,安装pip本身这个步骤就可以跳过了。

如果你正在使用Windows,请参考安装Python一节的内容,确保安装时勾选了pipAdd python.exe to Path

在命令提示符窗口下尝试运行pip,如果Windows提示未找到命令,可以重新运行安装程序添加pip

注意:Mac或Linux上有可能并存Python 3.x和Python 2.x,因此对应的pip命令是pip3

现在,让我们来安装一个第三方库——Python Imaging Library,这是Python下非常强大的处理图像的工具库。不过,PIL目前只支持到Python 2.7,并且有年头没有更新了,因此,基于PIL的Pillow项目开发非常活跃,并且支持最新的Python 3。

一般来说,第三方库都会在Python官方的pypi.python.org网站注册,要安装一个第三方库,必须先知道该库的名称,可以在官网或者pypi上搜索,比如Pillow的名称叫Pillow,因此,安装Pillow的命令就是:

pip install Pillow

耐心等待下载并安装后,就可以使用Pillow了。

有了Pillow,处理图片易如反掌。随便找个图片生成缩略图:

>>> from PIL import Image
>>> im = Image.open('test.png')
>>> print(im.format, im.size, im.mode)
PNG (400, 300) RGB
>>> im.thumbnail((200, 100))
>>> im.save('thumb.jpg', 'JPEG')

其他常用的第三方库还有MySQL的驱动:mysql-connector-python,用于科学计算的NumPy库:numpy,用于生成文本的模板工具Jinja2,等等。

模块搜索路径

当我们试图加载一个模块时,Python会在指定的路径下搜索对应的.py文件,如果找不到,就会报错:

>>> import mymodule
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named mymodule

默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:

>>> import sys
>>> sys.path
['', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python34.zip', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages']

如果我们要添加自己的搜索目录,有两种方法:

一是直接修改sys.path,添加要搜索的目录:

>>> import sys
>>> sys.path.append('/Users/michael/my_py_scripts')

这种方法是在运行时修改,运行结束后失效。

第二种方法是设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量类似。注意只需要添加你自己的搜索路径,Python自己本身的搜索路径不受影响。

python入门到精通python函数式编程与应用详解(代码片段)

...1f680;欢迎小伙伴们点赞👍、收藏⭐、留言💬目录python函数式编程lambda表达式的用法及其使用场景什么是匿名函数?ambda表达式的基本格式lambda表达式的使用场景Python中的高阶函数之map函数中带两个参数的map 查看详情

函数式编程简介-附入门方法(代码片段)

WHAT?什么是函数式编程?函数式编程是一种编程范式。编程范式又是什么?编程范式是一种解决问题的思路。我们熟悉的命令式编程把程序看作一系列改变状态的指令;而函数式编程把程序看作一系列数学函数映射的组合。编程... 查看详情

rxjs入门之函数响应式编程(代码片段)

一.函数式编程1.声明式(Declarativ)和声明式相对应的编程?式叫做命令式编程(ImperativeProgramming),命令式编程也是最常见的?种编程?式。//命令式编程:functiondouble(arr)constresults=[]for(leti=0;i<arr.length;i++)results.push(arr[i]*2)returnresult... 查看详情

前端开发函数式编程入门(代码片段)

前端开发函数式编程入门函数式编程是一门古老的技术,从上个世纪60年代Lisp语言诞生开始,各种方言层出不穷。各种方言带来欣欣向荣的生态的同时,也给兼容性带来很大麻烦。于是更种标准化工作也在不断根据现... 查看详情

编程范式——函数式编程入门(代码片段)

该系列会有3篇文章,分别介绍什么是函数式编程、剖析函数式编程库、以及函数式编程在React中的应用,欢迎关注我的blog命令式编程和声明式编程拿泡茶这个事例进行区分命令式编程和声明式编程命令式编程1.烧开水(为第一人... 查看详情

python入门到精通python函数式编程与应用详解(代码片段)

...1f680;欢迎小伙伴们点赞👍、收藏⭐、留言💬目录python函数式编程lambda表达式的用法及其使用场景什么是匿名函数?ambda表达式的基本格式lambda表达式的使用场景Python中的高阶函数之map函数中带两个参数的map函数格式Py... 查看详情

python基础--函数式编程(代码片段)

#面向过程#函数式编程:函数式=编程语言定义的函数+数学意义上的函数#面向对象#高阶函数:1.函数接受的参数是一个函数名2.返回值中包含函数#deffoo(n):#n=bar#print(n)##defbar(name):#print(‘mynameis%s‘%name)###foo(bar)#把函数当做参数传给另... 查看详情

python函数式编程以及高阶函数(代码片段)

...ng.com)一、什么是函数式编程首先要知道的是,函数是python内建的一种封装方法,可以通过把指定段落的代码拆解成函数,通过一层一 查看详情

scalaidea安装配置入门helloworld(代码片段)

ScalaIDEA安装入门ScalaScala(发音为/?skɑ?l?,?ske?l?/)是一门多范式的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。Scala是一门以java虚拟机(JVM)为目标运行环境并将面向对象和函数式编程的最佳特性结合在... 查看详情

函数式编程(代码片段)

《Python从小白到大牛》第10章函数式编程定义函数函数参数使用关键字参数调用函数参数默认值可变参数函数返回值无返回值函数多返回值函数函数变量作用域生成器嵌套函数函数式编程基础函数类型上述代码第①行重构了calcula... 查看详情

什么是函数式编程(代码片段)

...建议先阅读一下这几篇博客:函数式编程初探函数式编程入门教程图解Monad什么是函数式编程函数式编程中的函数指的并不是编程语言中的函数(或方法),它指的是数学意义上的函数,即映射关系(如:y=f(x)),就是y和x的对... 查看详情

函数式编程(练习一)(代码片段)

作业现要求你写一个简单的员工信息增删改查程序,需求如下:当然此表你在文件存储时可以这样表示1,AlexLi,22,13651054608,IT,2013-04-012,JackWang,28,13451024608,HR,2015-01-073,RainWang,21,13451054608,IT,2017-04-014,MackQiao,44,15653354208,Sales,2016-02- 查看详情

python-函数与函数式编程(代码片段)

一、函数定义1、函数的定义格式在Python中,函数的定义使用def语句、函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值可以使用return进行返回。例子:  defmy_abs(x):  print(x)  return0 说... 查看详情

函数式编程入门教程(转)

初涉函数式编程语言python,转载了一篇文章,先简单来了解一下函数式编程的概念.文章原址:http://www.ruanyifeng.com/blog/2017/02/fp-tutorial.html函数式编程入门教程 作者: 阮一峰日期: 2017年2月22日你可能听说过函数式编程... 查看详情

函数式编程(代码片段)

...的函数+数学意义上的函数(先想到一个数学模型,再用python上的功能实现这个逻辑) y=x+1defcat():returnx+1  特性:1.不用变量保存状态,意思就是不赋值,而且不修改变量2.第一类对象:函数即变量高阶函数1。函数接收... 查看详情

python程序设计实验6:函数式编程(代码片段)

实验6:函数式编程1.函数的参数传递2.Lambda2.1(lambdaval:val**2)(5)2.2(lambdax,y:x*y)(3,8)2.3(lambdas:s.strip().lower()[:2])('PyTHon')3.Map3.1['12','-2','0']-->[12,-2,0]3.2[&# 查看详情

python中函数与函数式编程(代码片段)

...表示参数是可选的"optionaldocumentationstring"function_suitePython中实用的内建函数函数说明 查看详情

笔记效率之门——python中的函数式编程技巧(代码片段)

文章目录Python函数式编程1.数据2.推导式3.函数式编程3.1.Lambda函数3.2.python内置函数3.3.高阶函数4.函数式编程的应用Python函数式编程我的AIStudio项目:【笔记】LearnDL第三课:Python高级编程——抽象与封装-飞桨AIStudio(baidu.com)p... 查看详情