Python学习一(面向对象和函数式编程)
学习了一周的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 == || n == ) return ;
else return ( feb(n - ) + feb(n - ));
}
让我惊奇的是居然可以使用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的时候结果都是[]
好啦,今天先到这里吧,这种函数编程的方式其实可读性并不好,写的时候也容易出错,也许是我理解的有些偏了,不过确实有种黑魔法的感觉,自己玩玩还是很不错的,不知道在实际项目中这么用会怎么样.
Python学习一(面向对象和函数式编程)的更多相关文章
- Python学习笔记二:函数式编程
1:Python中,内置函数名相当于一个变量,指向内置函数.所以可以通过函数名调用相应函数,也可以给函数名赋值,改变它的内容,如:可以把另一个函数变量赋值给它,那它就指向了所赋值的函数了. 2:高级函 ...
- Python学习总结之五 -- 入门函数式编程
函数式编程 最近对Python的学习有些怠慢,最近的学习态度和学习效率确实很不好,目前这种病况正在好转. 今天,我把之前学过的Python中函数式编程简单总结一下,分享给大家,也欢迎并感谢大家提出意见 ...
- python学习第十一天 -- 函数式编程
在介绍函数式编程之前,先介绍几个概念性的东西. 什么是函数式编程? 函数式编程的特点: 1.把计算视为函数而非指令; 2.纯函数式编程:不需要变量,没有副作用,测试简单; 3.支持高阶函数,代码简洁. ...
- Python学习札记(二十) 函数式编程1 介绍 高阶函数介绍
参考: 函数式编程 高阶函数 Note A.函数式编程(Functional Programming)介绍 1.函数是Python内建支持的一种封装,我们通过一层一层的函数调用把复杂任务分解成简单的任 ...
- Python学习札记(二十三) 函数式编程4 sorted
参考:sorted NOTE 1.sorted,快速排序,时间复杂度O(nlogn)渐进最优. #!/usr/bin/env python3 L = [] for i in range(10): L. ...
- Python学习札记(二十一) 函数式编程2 map/reduce
参考:map/reduce Note 1.map():map()函数接收两个参数,一个是函数,一个是Iterable.map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回. ...
- Python学习札记(二十七) 函数式编程8 偏函数
偏函数 NOTE 1.int()函数提供额外的base参数,默认值为10.如果传入base参数,就可以做N进制的转换: #!/usr/bin/env python3 import functools ...
- python学习,day3:函数式编程,递归和高阶函数
# coding=utf-8 # Author: RyAn Bi def calc(n): #递归 print(n) if int(n/2) > 0: #设置条件,否则会循环999 次,报错, ...
- python学习,day3:函数式编程,局部变量和全局变量
# coding=utf-8 # Author: RyAn Bi school = 'THU' #全局变量 def change_name(name): global age #在函数中,用globa ...
随机推荐
- 写自己的ASP.NET MVC框架(上)
http://www.cnblogs.com/fish-li/archive/2012/02/12/2348395.html 阅读目录 开始 ASP.NET程序的几种开发方式 介绍我的MVC框架 我的 ...
- consul 安装
1. linux 下consul 安装 首先查看机器信息: uname -a Linux centos-linux.shared 3.10.0-327.el7.x86_64 #1 SMP Thu No ...
- [原] wmic: Invalid XSL format (or) file name错误解决方法
之前运行wmic命令正确,今天在服务器上出现Invalid XSL format (or) file name的提示,搜索了一下,在这里找到了答案: http://www.ctkn.net/2011/ ...
- linux学习笔记--vi与vim编辑器
vi编辑器全名为Visual Interface,即为可视化接口,类似于Windows中的记事本 vim相当于是vi的一个升级版本,包含vi的一切操作命令,vim相对于vi做了哪些提升: 1.vim支 ...
- 如何提高Linux操作系统的安全性 转自https://yq.aliyun.com/articles/24251?spm=5176.100239.blogcont24250.7.CfBYE9
摘要: Linux系统不论在功能上.价格上或性能上都有很多优点,但作为开放式操作系统,它不可避免地存在一些安全隐患.关于如何解决这些隐患,为应用提供一个安全的操作平台,本文会告诉你一些最基本.最常用, ...
- 浅入浅出EmguCv(三)EmguCv打开指定视频
打开视频的思路跟打开图片的思路是一样的,只不过视频是由一帧帧图片组成,因此,打开视频的处理程序有一个连续的获取图片并逐帧显示的处理过程.GUI同<浅入浅出EmguCv(二)EmguCv打开指定图 ...
- [Gradle]填坑记录
1.初次打开Gradle工程特别慢,一直提示下载更新Gradle 解决办法:打开Gradle工程子目录:"\gradle\wrapper" 下的 "gradle-wrap ...
- APP跳到系统设置
//定位服务设置界面 NSURL *url = [NSURL URLWithString:@"prefs:root=LOCATION_SERVICES"]; if ([[UIApp ...
- MS SQL执行大脚本文件时,提示“内存不足”的解决办法()
问题描述: 当客户服务器不允许直接备份时,往往通过导出数据库脚本的方式来部署-还原数据库, 但是当数据库导出脚本很大,用Microsoft SQL Server Management Studio执行 ...
- cPage分页源码,分享给大家,可作参考
cPage是asp.net分页控件,也可以叫做分页组件,更确切的应该叫做分页模块,也或者叫做分页通用代码. cPage,版本3.2,源码如下: using System; namespace cPag ...