廖雪峰 Python教程 笔记 二

来源:互联网 发布:阿里云更改手机绑定 编辑:程序博客网 时间:2024/05/29 08:26

一、函数式编程

函数式编程接近与数学计算,抽象程度很高,相应的和计算机底层结构差异越大,效率不如C语言等高。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

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

1、高阶函数

函数 == 对象
函数是编程中,函数本身像一个对象一样,只是没有内部数据状态而,而函数名和变量名一样,指向某个函数对象的地址。
a、如f = abs 此时f就代表abs函数,能计算绝对值了。而将abs看做一个变量,它也是可变的!! 注意赋值是不带括号和参数的如果带了,就是执行了。
b、如果执行abs = 10,ads将不再能计算绝对值了,它将变成10这个数字。abs函数丢失了!,此时想要重新可用,需要重启Python交互式环境。
c、函数可以作为参数,返回值,容器元素等,被传递,存储等。

1.1 map/reduce
map就是映射,就想数学中的映射函数一样,是现实世界中一类十分常见的操作处理。map就是对一个序列中所有元素做某种变换,比如路径字符串替换前面的盘符。使用方式如下,先定义一个处理的函数f,然后放到map(f, Iterable)中,最后返回一个lterator惰性计算的序列。

>>> def f(x):...     return x * x...>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])>>> list(r)[1, 4, 9, 16, 25, 36, 49, 64, 81]

reduce:
reduce 和map的不同点是函数参数个数必须为2,即f(x1, x2),且操作序列的方式不同,它会将函数前一次计算的结果作为参数x1传入,然后取下一个数做x2传入,所以reduce的Iterable的元素个数至少为2,最后计算出一个结果。即
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

1.2 filter
如名,filter对列表中的元素进行过滤,传入一个返回boolean的函数即可。

1.3 sort
如名,对列表中的元素进行排序,使用方式
sorted([36, 5, -12, 9, -21]) 直接排序

sorted([36, 5, -12, 9, -21], key=abs) 添加排序函数,此排序函数返回一个值,然后根据返回值排序

sorted([36, 5, -12, 9, -21], key=abs, reverse=True) 反向

以上元素能方便的转换成key的,如果有多级参数排序这种不好转换成key的,在Python2里面可以传入cmp函数,但是在Python3里面去掉了,搞了一个很复杂的操作。

2、返回函数
即在函数中创建另一个函数,然后作为返回值返回,此函数并不会立即执行,需要在后面进行显示的的调用之后才会执行。此函数可以访问外部函数的参数和局部变量,本质上是持有它们的引用, 可以访问这个引用,以及改变它所指向的对象内部编变量的值,但是不能改变这个引用所指向的地址及对象,只能外部函数改变,这和Java的函数里面的匿名内部类相似,只是没有想Java里面一样final化,Python里面没有这个机制,所有会有后面一个问题q:,比如

def lazy_sum(*args):    def sum():        ax = 0        for n in args:            ax = ax + n        return ax    return sumf = lazy_sum(1, 3, 5, 7, 9)print(f())

特点:
1、每次调用外部返回的函数都是不一样的。

>>> f1 = lazy_sum(1, 3, 5, 7, 9)>>> f2 = lazy_sum(1, 3, 5, 7, 9)>>> f1==f2False

问题q:返回的函数是持有内部局部变量的引用,并不是直接拷贝一个,所以如果在下次执行之前,局部变量发生改变,函数持有的数据也是改变了的。比如下面这种:

def count():    fs = []    for i in range(1, 4):        def f():             return i*i          fs.append(f)    return fsf1, f2, f3 = count()>>> f1()9>>> f2()9>>> f3()9#因为在循环中定义的函数,等到后面某个时间执行时,内部函数(闭包)指向的变量的值已经改变了,变成了3。所以输出结果都是9

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

def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
再看看结果:

>>> f1, f2, f3 = count()>>> f1()1>>> f2()4>>> f3()9

因为传参时会创建一个新的引用,count函数对i的改变,不可能再影响f的j参数了。缺点是代码较长,可利用lambda函数缩短代码。

2、lambda表达式——匿名函数
lambda表达式的格式:lambda 参数列表:一个表达式
参数列表可以有多种参数,当然,它还可以像一个普通的函数一样,可以赋值传递等。
使用lambda可以简化函数的编写,更加快捷
lambda表达式可用来编写跳转表(jump table),就是行为的列表或字典。例如:

L = [(lambda x: x**2),      (lambda x: x**3),      (lambda x: x**4)]  

* 3、函数装饰器 *
顾名思义,对函数加上一层包装,增加其功能。
例如

def log(func):    @functools.wraps(func)    def wrapper(x):        print('start' + func.__name__)        ret = func(x)        print('end' + func.__name__)        return ret    return wrapper@logdef abs(x):    if x > 0:        return x    else:        return -xprint(abs(-11))

Python里面的函数名可以通过赋值改变其指向,所以其中的@log就相当于依据 abs = log(abs) 此时abs就相当于wapper函数,可在wrapper中接收相同的参数,返回函数值等。
使用装饰器之后,原函数的函数名会改变,Python提供了一个funtools函数进行处理 如上面的@functools.wraps(func)就是把wrapper的函数名称变回去,有些依赖签名的工具需要这个。

如果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

这个3层嵌套的decorator用法如下:

@log('execute')def now():    print('2015-3-25')

执行结果如下:

>>> now()execute now():2015-3-25

和两层嵌套的decorator相比,3层嵌套的效果是这样的:

>>> now = log('execute')(now)
我们来剖析上面的语句,首先执行log(‘execute’),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。这样就能往第三层的函数里面传入相同的参数,并且可以使用第一层的属于Derector的参数了。巧妙的利用内部函数可以访问外部函数参数和局部变量的特性。Python里面函数基本也相当于对象了,外部函数的参数、局部变量被函数对象持有就不会消失。

4、functools提供的一些功能
functools.partial(f, a); 根据已有的函数f,自动传入部分参数,然后以此创建一个新的函数。编码中,通常需要创建某个函数的重载函数,原来函数所有参数都需要调用时传递,重载的只需传递部分的参数,另一部分在重载函数内传递默认值即可。functools.partial就是用来简化这种操作的。

Python的模块、包

1、定义

1、定义:
Python是将一个代码文件称作模块,也即模块就是一个.py文件。
包的定义和其它语言中一样,但是Python的包里面必须放上一个init.py文件,不管里面有无内容,否则不会被当做包处理。

2、模块文件头:

#!/usr/bin/env python3# -*- coding: utf-8 -*-' a test module '__author__ = 'Michael Liao'

第一、二行是标准注释,第一行让此文件可以直接在unix linux的机器上运行,第二行 表示使用标准的UTF-8编码,
第四行当做模块地注释,任何写在py文件开头的字符串都当做文档注释。
第六行的表示作者
这些可以不写,但一般写了更好

2、导入模块

1、导入语句
import module_name #直接导入,系统的或者是同级目录下的
from package_test import module_name #不是同级目录下的

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

还可以手动指定要搜索的模块:
第一种直接修改System.path,运行结束后失效
sys.path.append('/Users/michael/my_py_scripts')
第二种方法是设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量类似。

2、作用域
def private_function_name(xxx) 表示私有函数,_a或者__a私有的变量,这样前面加上两条一条或两条’‘的表示私有的变量,这只是告诉调用者不要访问这个函数或者变量,但是仍然是可以访问的,因为Python没有机制保证这个变量或者方法不能被访问。

3、其他的:

__name__ 在Python中表示特殊变量,可以直接被引用,但是有特殊用途,代码中一般不创建这样的变量。
比如__doc__ 表示模块文档,__author__表示作者,

原创粉丝点击