robocky

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

学习了一周的Python,虽然一本书还没看完但是也收获颇多,作为一个老码农竟然想起了曾经荒废好久的园子,写点东西当做是学习笔记吧

对Python的语法看的七七八八了,比较让我关注的还是他编程的思想,那种和C语言不同的感觉

首先是面向对象的概念:

在Python中一切皆对象,因此包括类,类型,以至于函数都是对象,每个对象都有自己的一块空间用于存放他的东西,这个概念是之前的C语言编程中一直没有的概念.因此类生成对象的过程并不是类的实现,而是一个可以生成对象的对象用他方法(也可以说是构造函数)生成了一个和他具有一定关系的对象.而类这个对象本身也是由元类(MetaClass)来生成的,元类可以控制类的生成方法甚至他的一些特性和特征,但一般用到的非常少,所以暂时也没有深入去研究

而函数本身也是对象,由于Python支持动态的修改对象,因此我们可以给函数增加一些特征和属性,类似下面这样:

def foo(i):
    foo.n += i
    return foo.n
c = foo
c.n = 0

上面这个实际上是实现了一个累加器,重复调用这个函数里面的计数值就会不断增加,而累加器中就包含了一个变量n,这个变量存储在foo这个函数对象中,而c实际上只是对foo的引用,两个对象实际指向一个空间,那么如果我需要再生成一个累加器的话 就不能重用foo了,那么就需要一个用于生成累加器的一个函数方法

def func(n = 0):
    def foo(i):
        foo.n += i
        return foo.n
    foo.n = n
    return foo

c = func()
d = func()

这样实际上我们就得到了一个可以用于生成函数对象的函数对象,就和用类来生成对象是相同的.c和d是两个不同的累加器,有各自的scope,因此能够各自工作不受影响,就和两个类对象相似.

弄到这里我又关注了一下函数编程的概念,虽然了解并不深,但其中的很多思想很有意思,Python应该是可以实现部分函数编程的,包括他引入lambda表达式,当然可能跟那些本身就是函数编程的语言,如Lisp或者Haskell之类的有一定距离吧

函数编程中关注函数和表达式,而弱化命令编程中的命令和流程,因此条件和循环都会被表达式或者函数递归所替代,比如下面这个例子

feb = lambda n: (n == 0 or n == 1) and 1 or feb(n - 1) + feb(n - 2)

这段代码等同于下面这段C代码,实际上就是一个通过递归实现菲波那切数列的简单例子

int feb(n)
{
    if ( n == 0 || n == 1) return 1;
    else return ( feb(n - 1) + feb(n - 2));
}

让我惊奇的是居然可以使用and和or来替代if,else的作用,这个源于and和or的短路运算,也就是A and B时如果A为True则执行B,否则不执行B,而A or B中如果A为True则不执行B,反之执行B.

但是这个代码也存在问题,如果在A and B or C中B为False也会导致再执行C,这个问题可以通过将代码改为A and (B or True) or C更为稳妥

那么相应的if...then...elif...then...else也可以写成A and (B or True) or C and (D or True) or E的形式

但是目前为止能够用在表达式中的只能是表达式或者函数,如果企图插入语句就会失效了,好在函数编程中应该是不存在语句的,连最基本的赋值语句都是要舍弃的,号称变量不变性,但是就具体来说目前我的理解还有限

那么如何直接生成一个斐波那契数列而不是单个元素呢,可以使用下面的方法: 

febo = lambda n: list(map(feb, range(n)))

这个方法使用了Python在函数编程中常用的map函数,在Python3中map函数得到的是迭代器,因此想要获得列表必须用list提取一下

计算结果如下:

febo(10)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

不过这个方法在执行效率上是存在问题的,因为算每个斐波那契数的时候都要把之前的算一遍,更加理想的方式是在列表生成的过程中直接使用列表中的数值进行计算

为了便于理解,我把这个过程分为三个函数,不过即使这样理解起来仍然需要一定的逻辑思维能力.

首先是一个数列处理函数: 

febon = lambda lst: len(lst) < 2 and (lst.append(1) or True) or lst.append(lst[-1] + lst[-2])

这里面首先判断长度是否小于2,如果是则给列表添加1,否则添加列表的最后两项之和.其中由于append函数返回值为None,因此必须在之后强制变为True,使其无法执行后面的or代码

然后是一个递归函数用于生成序列 

feb = lambda n, lst:n == 0 and lst or (febon(lst) or True) and feb(n-1, lst)

 

这里面需要注意的是在Python中逻辑运算的结果并不一定是逻辑值,但当其用于逻辑判断是则会根据其值的类型自动判断其为True还是False,但是表达式的值仍然是实际的值,这点跟C语言有很大区别

比如1 and 2这个表达式的真值为True,但是表达式的值为2,因为1为True,因此表达式又判断了2,2也为True,则最终的值就定格在2了

而1 or 2的表达式结果就是1,因为1为True之后就不在计算or后面的表达式了

同样可以知道None, 0, False, [], (), {}都代表False,那么[] and {}其结果为[],而[] or{}的结果就为{}

在本例中可以看到,n的作用仅限于设定递归次数,当n减到0则直接输出列表,否则进行一次列表运算后进行递归运算,当计算n次之后终止并返回列表

但是这个代码中存在一个问题,等下再说

最后这个只是用于将递归函数中的列表不作为输入,仅作为输出 

febo = lambda n: feb(n, [])

 

事实上也可以把列表作为函数对象的一个属性,而不是参数在递归中调用,不过我觉得这种方式更加直观并且不容易犯错误

最后的执行结果就是可以返回一个数列,而且每个元素都只进行一次计算和设置,结果和上面那个一样,就不贴了

刚才我提到这里面有个问题,就是说如果输入的参数n为0的时候lst为[],就是false,这样导致or后面的代码也被执行了,实际测试中就会引发异常.为了保证能够正确输出值,将feb函数的定义改了一下

feb = lambda n, lst:n > 0 and (febon(lst) or True) and (feb(n-1, lst) or True) or lst

 实际就是把条件判断的顺序改了一下,最后返回lst,这样无论是否为False都会被返回了,经过测试当n<=0的时候结果都是[]

好啦,今天先到这里吧,这种函数编程的方式其实可读性并不好,写的时候也容易出错,也许是我理解的有些偏了,不过确实有种黑魔法的感觉,自己玩玩还是很不错的,不知道在实际项目中这么用会怎么样.

posted on 2016-04-22 13:44  robocky  阅读(399)  评论(0编辑  收藏  举报