zoukankan      html  css  js  c++  java
  • python:python是什么

    转自:https://blog.csdn.net/pr去掉ogram_t汉hink/article/d字etails/7240881#comments_17004541

    正文:

    春节前有 网友留言,提醒Python系列好久没更新了(不知不觉又过了一年多)。俺回复留言说:春节假期补上后一篇。昨天听到鞭炮声才发觉元宵已经到了,赶忙写出本文。
       前一个帖子介绍了Python作为"面向对象编程"(以下简称OOP)语言的特点,今天来聊一聊Python作为"函数式编程"(以下简称FP)语言的特点。考虑到本系列面向的是Python的门外汉或刚入门的新手,故本文仅介绍若干浅显的FP特性。

    ★什么是函数式编程

      说实话,"函数式编程语言"是一个很大的话题。由于篇幅有限,本文不可能对这个话题做全面介绍。俺干脆偷一下懒,只简单说说。
      从字面上看,所谓的函数式编程,就是以"函数"为中心的"编程范式"。估计有同学又会问了,啥是"编程范式"捏?哎呦,这又是一个很大的话题。通俗来讲,"编程范式"就是指编程的套路。比方说大家很熟悉的OOP,就是一种"编程范式"。FP跟OOP一样,都是一种编程的套路。做个简单类比:OOP以"对象/类"作为程序设计的核心,而FP以"函数"作为程序设计的核心。

    ★FP的特点

      既然说到FP,自然要稍微说一下FP的特色。

    ◇函数很牛X

      刚才说了,FP是以函数为中心。既然如此,在支持FP的语言中,函数的功能自然十分牛X。通俗地说,OOP语言中,类/对象能干的事情,FP语言中的函数也能干。下面做一些对比,以加深大伙儿的印象。
    OOP中,对象可以互相赋值;FP中,函数可以互相赋值。
    OOP中,对象可以作为函数的参数/返回值,FP中,函数可以作为函数的参数/返回值。
    某些OOP中,类可以嵌套定义;FP中,函数可以嵌套定义。
    某些OOP中,可以有匿名类;FP中,可以有匿名函数。

    ◇避免副作用

      在FP中,特别强调函数不要有"副作用"(洋文叫" side effect")。没有副作用的函数,又称之为纯函数(pure function)。其输出完全依赖于输入。换句话说,只要输入一样,输出就一样。
      要成为纯函数,函数内部不能读写函数外部变量、不能有设备I/O(比如读写文件)......
      无副作用是FP的重要特性。FP的很多优点来自于此特性。

    ◇避免控制流

      在FP中,尽量避免用控制流语句(循环语句、判断语句)。对于控制流语句,FP有另外的替代方式。比如:常用递归或高阶函数来替代循环。如此一来,FP的代码会显得更简洁,更可读。

    ◇多态

      大部分支持FP的语言,也都支持多态。函数参数支持多态化,便可实现非常灵活的功能。

      说了这么多,不知道大伙儿明白了没?还是没整明白的同学,请看 维基百科的英文词条(中文词条太简单,看不明白滴)。
      洋文实在看不下去吗?那不妨看看IT大牛Joel写的《 你的编程语言能这样做吗?》(中文版在" 这里")。此文以JavaScript来阐述FP的妙处。

    ★FP的优点

      再稍微说一下FP的好处,以强化大伙儿学习的动力。

    ◇模块化

      在FP的思想中,函数最好是"纯"的,而且最好只完成"单一"的任务。在这种指导思想下,函数的模块化程度自然就高。

    ◇可复用性

      模块化程度高,直接的好处就是可复用性好。

    ◇可读性

      刚才说了,FP的思想强调函数又纯又小。这样的函数,代码的可读性自然好,修改起来也方便。

    ◇易于调试

      前面提到了纯函数。如果你的程序中大部分函数都是纯函数,则调试Bug会容易很多。像OOP中,类的多个成员函数都可以修改类的成员变量,有时候会导致调试极其困难。而纯函数没有此问题。
      另外,多线程是调试的一大噩梦。当年俺还专门写过帖子,介绍C++多线程的注意事项(在" 这里")。而纯函数由于没有副作用,不必担心各种"互斥"、"死锁"等问题。

    ◇易于测试

      除了易于调试,纯函数的输出仅仅依赖于输入,这一特点注定了它很容易进行单元测试。

    ◇适合并行

      在FP中,由于纯函数无副作用,很适合编写并行处理的代码。最典型并且在工业界获得巨大成功的例子就是Erlang。

    ◇其它

      当然啦,FP的好处远不止上述这些(比如还有:利于形式化证明)。限于篇幅,俺就不展开了。

    ★Python的函数语法

      Python中,常见的函数定义和函数调用,想必各位都晓得了。下面说几种不太常见的,且跟FP有关的语法。

    ◇函数赋值

    Python可以把函数直接赋值给一个变量。举例如下:

    def square(n) : # 这是一个计算平方的小函数,后面会反复用它举例
        return n ** 2
    f = square # 此处赋值给变量f
    f(10) # 此处返回100。注意:对该变量使用小括号,等同于调用函数

    ◇匿名定义

      Python可以用lambda关键字定义 单行的匿名函数。套用刚才的例子

    square = lambda x : x**2 # 定义一个单参数的匿名函数,并把该函数赋值给变量
    square(10) # 此处返回 100

    ◇嵌套定义

    Python支持函数的嵌套定义(请看如下例子)。这种语法,在"闭包"中经常出现(后面会具体介绍闭包)。

    def outer() : # 外层函数
        s = "hello"
        def inner() : # 内层函数
            print(s) # 此处引用的是外层作用域的变量
    
    inner() # 输出 hello
    s = "world"
    inner() # 输出 world

    ★和FP相关的内置函数

      Python内置了一大坨用于FP的函数,以方便程序猿写出简洁的代码。在接下去聊之前,俺有必要先介绍其中的2个。

    ◇map(func, iter)

    为了省事,俺只介绍2参数的map(正宗的map支持N参数)。
    参数func是个函数,参数iter是个迭代器(也可以理解为集合)
    map()会把iter的每个元素传给func,并把每次调用的结果保存到一个list中,然后返回此list。
    举例:
    挨个计算整数list的平方

    map(square, [1, 2, 3]) # 返回 [1, 4, 9]

    ◇filter(func, iter)

    参数含义同map
    filter()会把iter的每个元素传给func,如果func返回结果为True,就把元素保存在一个list中,最后返回此list。

    举例:
    要过滤出所有奇数

    def odd(n) :
        return (n%2) == 1
    
    filter(odd, [1, 2, 3]) # 返回[1, 3]

    此处可以用上lambda,把代码简化为一行

    filter(lambda n: (n%2)==1, lst)

    ★消除控制流

      为了让大伙儿更深刻体会FP风格同传统风格的差别,俺把刚才两个例子组合一下——要求返回整数list中所有奇数的平方。
    传统的写法(有控制流)

    def func1(lst) :
        new_lst = []
        for n in lst :
            if odd(n) :
            new_lst.append(square(n))
        return new_lst

    FP的写法(无控制流)

    def func2(lst) :
        return map(square, filter(odd, lst))

    怎么样?是不是更简洁?连 for / if 两个关键字都不需要。

    ★List Comprehension

      这个洋文比较难翻译。有人叫做"列表推导",也有人称为"列表展开"或"列表解析"。(俺比较喜欢头一个翻译——不禁让人联想到"推倒":)
      在Python中,这是一个很好吃的语法糖——可以让你写出很简洁、很优雅的代码。
    举例1:
    还拿刚才过滤奇数的例子。

    filter(lambda n: (n%2)==1, lst)

    上述写法可以等价替换为列表推导

    [n for n in lst if (n%2)==1]

    举例2:
    再来一个稍微复杂的例子。假设有两个整数list,分别存储矩形的宽度和高度。现在想把所有的宽度和高度进行两两组合,把大于10的面积打印出来。
    传统的写法(2层循环,4行代码)

    for w in width :
        for h in height :
            if w*h > 10 :
        print(w*h)

    FP的写法(无循环,1行代码,多精致啊)

    print( [w*h for w in width for h in height if w*h > 10] )

      除了列表推导,Python中还有字典推导、集合推导等等。为了省点口水,暂且打住。

    ★闭包

    闭包,洋文叫"closure",解释在" 这里"。它是FP的常见手法。那闭包到底有啥用捏?俺举一个微积分中,函数求导的例子。(不懂微积分或者对高数有心理阴影的同学,别担心,请把注意力集中在代码上)

    def d(f) :
        def calc(x) :
            dx = 0.000001 # 表示无穷小的Δx
            return (f(x+dx) - f(x)) / dx # 计算斜率。注意,此处引用了外层作用域的变量 f
        return calc # 此处用函数作为返回值(也就是函数 f 的导数)    

    现在,假设要计算二次函数 f(x) = x 2 + x + 1 的导数,只需

    f = lambda x : x**2 + x + 1 # 先把二次函数用代码表达出来
        f1 = d(f) # 这个f1 就是 f 的一阶导数啦。注意,导数依然是个函数

    有了一阶导数,就可以很容易地计算该函数在某点的斜率
    比如要计算 x=3 的斜率,只需
    f1(3)

    如果要想得到二阶导数(导数的导数),只需依样画葫芦(瞧这代码写得多优雅)
    f2 = d(f1)

      看到这里,大伙儿不妨设想一下:如果不用FP,改用OOP,上述需求该如何实现?俺觉得吧,用OOP来求导,这代码写起来多半是又丑又臭。

    ★结尾

      今天聊了不少FP的语法特性,可惜还是没聊完。由于俺比较懒,而且怕写得太长没人看,所以一些高级话题(比如:迭代器、生成器、等),今天就不介绍了。假如列位看官对那些玩意儿感兴趣,再抽空单独写一帖。

  • 相关阅读:
    【leetcode】500. Keyboard Row
    【排序算法汇总】5类8种排序算法
    【leetcode】339. Nested List Weight Sum
    ArcCatalog中将SQLServer中的空间数据导入到Oracle库中
    初学ArcGIS API for JavaScript
    ArcGIS API for JavaScript开发环境配置
    shapefile与gdb中不能允许存在的几何错误
    C#中的Dictionary字典类介绍(转载)
    泛型Dictionary<string,string>的用法
    空间数据存储格式wkb和wkt(转载)
  • 原文地址:https://www.cnblogs.com/jinziguang/p/14929692.html
Copyright © 2011-2022 走看看