目录
一.匿名函数 (lambda())
二.内置函数 (map()、filter()、reduce()、zip()、sorted())
三.yield关键字的另外一种用法
四.递归调用
五.正则表达式 re模块
六.模块与包
一.匿名函数(lambda)
1.当传入函数时,有些时候不需要显示的定义函数,直接传入匿名函数更方便
2.匿名函数提供了有限支持,以map()为例,计算f(x)=x²时,除了定义一个f(x),还可以直接传入匿名函数:
>>> map(lambda x: x*x ,[1,2,3,4,5,6,7,8,9]) [1,4,9,16,25,36,49,64,81]
匿名函数lambda x: x*x 实际上就是:(冒号前面的x 表示函数参数)
def f(x):
return x * x
3.匿名函数只能有一个表达式,不用写return,返回值就是表达式的结果
此外,匿名函数也是一个函数对象,也可以把它赋值给一个变量,再利用变量调用该函数:
>>> f = lambda x: x * x >>> f <function <lambda> at 0x10453d7d0> >>> f(5) 25
同样,也可以把它作为返回值返回:
def build(x, y): return lambda: x * x + y * y
二.内置函数
max([1,5,2,9]) # 求最大值
min([9,2,-4,2]) # 求最小值
zip()函数 # 压缩任意多个[0,n]参数 ,形成新的复合元组
a, b = [0, 1], [2, 3] ab = zip(a, b) print(list(ab)) # [(0, 2), (1, 3)]
sorted()函数 # 返回正序的序列
salaries={ 'egon':3000, 'alex':100000000, 'wupeiqi':10000, 'yuanhao':2000 } print(sorted(salaries)) #默认的排序结果是从小到大,这里默认k值的首字母 print(sorted(salaries,key=lambda x:salaries[x])) #默认的排序结果是从小到大,这里按照薪资排序 print(sorted(salaries,key=lambda x:salaries[x],reverse=True)) #默认的排序结果是从小到大,参数是反转内置函数reverse
map()函数
map()是Python的内置函数。它的第一个参数是一个函数对象。
re = map((lambda x: x+3),[1,3,5,6])
这里,map()有两个参数,一个是lambda所定义的函数对象,一个是包含有多个元素的表。map()的功能是将函数对象依次作用于表的每一个元素,每次作用的结果储存于返回的表re中。map通过读入的函数(这里是lambda函数)来操作数据(这里“数据”是表中的每一个元素,“操作”是对每个数据加3)。
在Python 3.X中,map()的返回值是一个循环对象。可以利用list()函数,将该循环对象转换成表。
如果作为参数的函数对象有多个参数,可使用下面的方式,向map()传递函数参数的多个参数:
re = map((lambda x,y: x+y),[1,2,3],[6,7,9])
map()将每次从两个表中分别取出一个元素,带入lambda所定义的函数。
filter()函数
filter函数的第一个参数也是一个函数对象。它也是将作为参数的函数对象作用于多个元素。如果函数对象返回的是True,则该次的元素被储存于返回的表中。filter通过读入的函数来筛选数据。同样,在Python 3.X中,filter返回的不是表,而是循环对象。
filter函数的使用如下例:
def func(a): if a > 100: return True else: return False print filter(func,[10,56,101,500])
l=['alex_SB','wupeiqi_SB','yuanhao_SB','egon'] res=filter(lambda x:x.endswith('SB'),l) print(res) print(list(res))
reduce()函数
reduce函数的第一个参数也是函数,但有一个要求,就是这个函数自身能接收两个参数。reduce可以累进地将函数作用于各个参数。如下例:
print reduce((lambda x,y: x+y),[1,2,5,7,9])
reduce的第一个参数是lambda函数,它接收两个参数x,y, 返回x+y。
reduce将表中的前两个元素(1和2)传递给lambda函数,得到3。该返回值(3)将作为lambda函数的第一个参数,而表中的下一个元素(5)作为lambda函数的第二个参数,进行下一次的对lambda函数的调用,得到8。依次调用lambda函数,每次lambda函数的第一个参数是上一次运算结果,而第二个参数为表中的下一个元素,直到表中没有剩余元素。
上面例子,相当于(((1+2)+5)+7)+9
from functools import reduce L=[1,2,3,4,5] print(reduce(lambda x,y:x+y,L,0))
三.yield关键字的另外一种用法
yield的语句形式:yield 1
yield的表达是形式:x = yield
模拟餐厅吃饭,首先我们需要点餐,然后服务员把一道菜一个个端上来:
def deco(func): # 装饰器 def wrapper(*args,**kwargs): res=func(*args,**kwargs) next(res) # 初始化的作用返回一个food return res return wrapper @deco def eater(name): print('%s ready to eat' %name) food_list=[] # 菜单功能 while True: food=yield food_list # yield控制返回值 food_list.append(food) print('%s start to eat %s' %(name,food)) g=eater('alex') g.send('脚趾头') # send等同于next(g)触发函数运行(初始化) 但send同时又传值的功能 函数内部有yield就是一个生成器,生成器必须先初始化
总结x = yield表达式:
g.send('1111'),先把1111传给yield,由yield赋值给x,然后再往下执行,直到再次碰到yield,然后把yield后的返回值返回
yield表达式应用实例: # grep -rl 'python' /root ( 执行过滤'python'的操作,查找root目录下所有子目录的文件包含python)
执行下面的方法的思维:
第一步:把它定义成函数的形式,找到所有文件夹下的文件
def search(searth_path): # 参数为路径 g=os.walk(searth_path) for par_dir,_,files in g: #返回的三个参数分别为:主目录、子目录、文件 for file in files: file_abs_path=r'%s\%s' %(par_dir,file) # 拼接成所要的格式 # print(file_abs_path) search(r'E:\资料、笔记\python_oldboy\s17day05\day5\a')
第二部:要想找文件需要先建立一个打开函数
第三部:遍历打开文件每一行
第四部:过滤每一行 用户想要的内容
第五步:打印想要的结果
最后一步:调用执行
import os def init(func): # 装饰器 def wrapper(*args,**kwargs): res=func(*args,**kwargs) next(res) return res return wrapper @init def search(target): # target是下面打开函数生成器的名称 while True: search_path=yield g=os.walk(search_path) for par_dir,_,files in g: for file in files: file_abs_path=r'%s\%s' %(par_dir,file) # print(file_abs_path) target.send(file_abs_path) @init def opener(target): while True: file_abs_path=yield # print('opener func==>',file_abs_path) with open(file_abs_path,encoding='utf-8') as f: target.send((file_abs_path,f)) @init def cat(target): # 遍历打开文件的每一行的函数 while True: file_abs_path,f=yield #(file_abs_path,f) # yield传上一个函数给的值 绝对路径和文件名 for line in f: tag=target.send((file_abs_path,line)) if tag: break @init def grep(target,pattern): # 过滤函数 参数是用户想要的过滤内容 tag=False while True: file_abs_path,line=yield tag # yield传上一个函数给的值 tag=False if pattern in line: tag=True target.send(file_abs_path) @init def printer(): # 打印函数 打印想要的结果 while True: file_abs_path=yield print(file_abs_path) x=r'C:\Users\Administrator\PycharmProjects\python17期\day5\a' # 这是想要找的路径 想在这里找到过滤内容 g=search(opener(cat(grep(printer(),'python')))) print(g) # 现在拿到的是生成器对象 g.send(x) # 要想执行结果 需要send() ## 具体文件见17期_day05的代码笔记
四.递归调用
优点:是逻辑简单清晰
缺点:是过深的调用会导致栈溢出。
Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。
尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
例子1:计算5的阶乘
#!/usr/bin/env python def func(n): if n == 0: return 1 else: return n * func(n-1) print func(5)
我们计算fact(5),可以根据函数定义看到计算过程如下: ===> fact(5) ===> 5 * fact(4) ===> 5 * (4 * fact(3)) ===> 5 * (4 * (3 * fact(2))) ===> 5 * (4 * (3 * (2 * fact(1)))) ===> 5 * (4 * (3 * (2 * 1))) ===> 5 * (4 * (3 * 2)) ===> 5 * (4 * 6) ===> 5 * 24 ===> 120
例子2:计算1到100的和
#!/usr/bin/env python def func(n): sum = 0 if n == 0: return 0 else: return n + func(n-1) print func(100)
例子3:用递归的方法打印目录下的所有文件名
#!/usr/bin/env python import os import sys def listdir(n): lsdir = os.listdir(n) for i in lsdir: if os.path.isfile(os.path.join(n,i)): print os.path.join(n,i) else: listdir(os.path.join(n,i)) listdir(sys.argv[1])
#!/usr/bin/env python import os import sys def print_file(path): lsdir = os.listdir(path) files = [ i for i in lsdir if os.path.isfile(os.path.join(path,i)) ] dicts = [ i for i in lsdir if os.path.isdir(os.path.join(path,i)) ] if files: for i in files: print os.path.join(path,i) if dicts: for f in dicts: print_file(os.path.join(path,f)) print_file(sys.argv[1])
用到的模块
注意
五.正则表达式 re模块
正则表达式(regular expression)主要功能是从字符串(string)中通过特定的模式(pattern),搜索想要找到的内容。
语法
之前,我们简介了字符串相关的处理函数。我们可以通过这些函数实现简单的搜索功能,比如说从字符串“I love you”中搜索是否有“you”这一子字符串。但有些时候,我们只是模糊地知道我们想要找什么,而不能具体说出我是在找“you”,比如说,我想找出字符串中包含的数字,这些数字可以是0到9中的任何一个。这些模糊的目标可以作为信息写入正则表达式,传递给Python,从而让Python知道我们想要找的是什么。
在Python中使用正则表达式需要标准库中的一个包re。
import re m = re.search('[0-9]','abcd4ef') print(m.group(0))
re.search()接收两个参数,第一个'[0-9]'就是我们所说的正则表达式,它告诉Python的是,“听着,我从字符串想要找的是从0到9的一个数字字符”。
re.search()如果从第二个参数找到符合要求的子字符串,就返回一个对象m,你可以通过m.group()的方法查看搜索到的结果。如果没有找到符合要求的字符,re.search()会返回None。
如果你熟悉Linux或者Perl, 你应该已经熟悉正则表达式。当我们打开Linux shell的时候,可以用正则表达式去查找或着删除我们想要的文件,比如说:
$rm book[0-9][0-9].txt
这就是要删除类似于book02.txt的文件。book[0-9][0-9].txt所包含的信息是,以book开头,后面跟两个数字字符,之后跟有".txt"的文件名。如果不符合条件的文件名,比如说:
bo12.txt
book1.txt
book99.text
都不会被选中。
Perl中内建有正则表达式的功能,据说是所有正则表达式系统中最强的,这也是Perl成为系统管理员利器的一个原因。
正则表达式的函数
m = re.search(pattern, string) # 搜索整个字符串,直到发现符合的子字符串。 m = re.match(pattern, string) # 从头开始检查字符串是否符合正则表达式。必须从字符串的第一个字符开始就相符。
可以从这两个函数中选择一个进行搜索。上面的例子中,我们如果使用re.match()的话,则会得到None,因为字符串的起始为‘a’, 不符合'[0-9]'的要求。
对于返回的m, 我们使用m.group()来调用结果。(我们会在后面更详细解释m.group())
我们还可以在搜索之后将搜索到的子字符串进行替换:
str = re.sub(pattern, replacement, string)
# 在string中利用正则变换pattern进行搜索,对于搜索到的字符串,用另一字符串replacement替换。返回替换后的字符串。
此外,常用的正则表达式函数还有
re.split() # 根据正则表达式分割字符串, 将分割后的所有子字符串放在一个表(list)中返回
re.findall() # 根据正则表达式搜索字符串,将所有符合的子字符串放在一给表(list)中返回
(在熟悉了上面的函数后,可以看一下re.compile(),以便于提高搜索效率。)
写一个正则表达式
关键在于将信息写成一个正则表达式。我们先看正则表达式的常用语法:
1)单个字符:
. 任意的一个字符
a|b 字符a或字符b
[afg] a或者f或者g的一个字符
[0-4] 0-4范围内的一个字符
[a-f] a-f范围内的一个字符
[^m] 不是m的一个字符
\s 一个空格
\S 一个非空格
\d [0-9]
\D [^0-9]
\w [0-9a-zA-Z]
\W [^0-9a-zA-Z]
2)重复
紧跟在单个字符之后,表示多个这样类似的字符
* 重复 >=0 次
+ 重复 >=1 次
? 重复 0或者1 次
{m} 重复m次。比如说 a{4}相当于aaaa,再比如说[1-3]{2}相当于[1-3][1-3]
{m, n} 重复m到n次。比如说a{2, 5}表示a重复2到5次。小于m次的重复,或者大于n次的重复都不符合条件。
正则表达 相符的字符串举例
[0-9]{3,5} 9678
a?b b
a+b aaaaab
3) 位置
^ 字符串的起始位置
$ 字符串的结尾位置
正则表达 相符的字符串举例 不相符字符串
^ab.*c$ abeec cabeec (如果用re.search(), 将无法找到。)
4)返回控制
我们有可能对搜索的结果进行进一步精简信息。比如下面一个正则表达式:
output_(\d{4})
该正则表达式用括号()包围了一个小的正则表达式,\d{4}。 这个小的正则表达式被用于从结果中筛选想要的信息(在这里是四位数字)。这样被括号圈起来的正则表达式的一部分,称为群(group)。
我们可以m.group(number)的方法来查询群。group(0)是整个正则表达的搜索结果,group(1)是第一个群……
import re m = re.search("output_(\d{4})", "output_1986.txt") print(m.group(1))
我们还可以将群命名,以便更好地使用m.group查询:
import re m = re.search("output_(?P<year>\d{4})", "output_1986.txt") #(?P<name>...) 为group命名 print(m.group("year"))
练习题:
# 练习 # 有一个文件,文件名为output_1981.10.21.txt 。 # 下面使用Python: 读取文件名中的日期时间信息,并找出这一天是周几。 # 将文件改名为output_YYYY-MM-DD-W.txt (YYYY:四位的年,MM:两位的月份,DD:两位的日,W:一位的周几,并假设周一为一周第一天) import re import time import datetime filename = "output_1981.10.21.txt" m = re.search("output_(\d{4}.\d{2}.\d{2})", filename) searchResult = m.group(1) print("matcht result: %s" % searchResult) dates = searchResult.split('.') for date in dates: print(date) year = dates[0] month = dates[1] day = dates[2] xingqi = datetime.datetime(int(year), int(month), int(day)).strftime("%w") # replace to new filename theReplacePart = '%s-%s-%s-%s' % (year, month, day, xingqi) print('the new filename is: %s' % theReplacePart) newfileName = re.sub("\d{4}.\d{2}.\d{2}", theReplacePart, filename) print(newfileName)
六.模块与包
我们之前看到了函数和对象。从本质上来说,它们都是为了更好的组织已经有的程序,以方便重复利用。
模块(module)也是为了同样的目的。在Python中,一个.py文件就构成一个模块。通过模块,你可以调用其它文件中的程序。
引入模块
我们先写一个first.py文件,内容如下:
def laugh(): print 'HaHaHaHa'
再写一个second.py,并引入first中的程序:
import first for i in range(10): first.laugh()
在second.py中,我们使用了first.py中定义的laugh()函数。
引入模块后,可以通过模块.对象的方式来调用引入模块中的某个对象。上面例子中,first为引入的模块,laugh()是我们所引入的对象。
Python中还有其它的引入方式:
import a as b # 引入模块a,并将模块a重命名为b
from a import function1 # 从模块a中引入function1对象。调用a中对象时,我们不用再说明模块,即直接使用function1,而不是a.function1。
from a import * # 从模块a中引入所有对象。调用a中对象时,我们不用再说明模块,即直接使用对象,而不是a.对象。
也可以在模块a里写,__all__=['调用对象名'] == from a import *
这些引用方式,可以方便后面的程序书写。
__name__ == '__main__'的作用
有句话经典的概括了这段代码的意义:
“Make a script both importable and executable”
意思就是说让你写的脚本模块既可以导入到别的模块中用,另外该模块自己也可执行。
这句话,可能一开始听的还不是很懂。下面举例说明:
先写一个模块:
这个函数定义了一个main函数,我们执行一下该py文件发现结果是打印出”we are in __main__“,说明我们的if语句中的内容被执行了,调用了main():
但是如果我们从另我一个模块导入该模块,并调用一次main()函数会是怎样的结果呢?
#anothermodle.py from module import main main()
其执行的结果是:we are in module
但是没有显示”we are in __main__“,也就是说模块__name__ = '__main__' 下面的函数没有执行。
这样既可以让“模块”文件运行,也可以被其他模块引入,而且不会执行函数2次。这才是关键。
总结一下:
如果我们是直接执行某个.py文件的时候,该文件中那么”__name__ == '__main__'“是True,但是我们如果从另外一个.py文件通过import导入该文件的时候,这时__name__的值就是我们这个py文件的名字而不是__main__。
这个功能还有一个用处:调试代码的时候,在”if __name__ == '__main__'“中加入一些我们的调试代码,我们可以让外部模块调用的时候不执行我们的调试代码,但是如果我们想排查问题的时候,直接执行该模块文件,调试代码能够正常运行!
搜索路径
Python会在以下路径中搜索模块顺序:
- 内存 只在第一次导入执行后开辟内存空间 多次导入不生效
- 内置
- sys.path
如果你有自定义的模块,或者下载的模块,可以根据情况放在相应的路径,以便Python可以找到。
包(package)
可以将功能相似的模块放在同一个文件夹(比如说this_dir)中,构成一个模块包。通过
import this_dir.module
引入this_dir文件夹中的module模块。
注 意 : .点左边必须是一个包 .点的右边一定是文件名
通常包总是一个目录,可以使用import导入包,或者from + import来导入包中的部分模块。包目录下为首的一个文件便是 __init__.py。然后是一些模块文件和子目录,假如子目录中也有 __init__.py 那么它就是这个包的子包了。
在创建许许多多模块后,我们可能希望将某些功能相近的文件组织在同一文件夹下,这里就需要运用包的概念了。包对应于文件夹,使用包的方式跟模块也类似,唯一需要注意的是,当文件夹当作包使用时,文件夹需要包含__init__.py文件,主要是为了避免将文件夹名当作普通的字符串。__init__.py的内容可以为空,一般用来进行包的某些初始化工作或者设置__all__值,__all__是在from package-name import *这语句使用的,全部导出定义过的模块。
可以从包中导入单独的模块。
1). import PackageA.SubPackageA.ModuleA,使用时必须用全路径名
2). 变种: from PackageA.SubPackageA import ModuleA, 可以直接使用模块名而不用加上包前缀。
3). 也可以直接导入模块中的函数或变量:from PackageA.SubPackageA.ModuleA import functionA
import语句语法:
1. 当使用from package import item时,item可以是package的子模块或子包,或是其他的定义在包中的名字(比如一个函数、类或变量)
首先检查item是否定义在包中,不过没找到,就认为item是一个模块并尝试加载它,失败时会抛出一个ImportError异常。
2. 当使用import item.subitem.subsubitem语法时,最后一个item之前的item必须是包,最后一个item可以是一个模块或包,但不能是类、函数和变量
3. from pacakge import *
如果包的__init__.py定义了一个名为__all__的列表变量,它包含的模块名字的列表将作为被导入的模块列表。
如果没有定义__all__, 这条语句不会导入所有的package的子模块,它只保证包package被导入,然后导入定义在包中的所有名字。
python包是:
包是一个有层次的文件目录结构,它定义了由n个模块或n个子包组成的python应用程序执行环境。
通俗一点:包是一个包含__init__.py 文件的目录,该目录下一定得有这个__init__.py文件和其它模块或子包。
常见问题:
-
引入某一特定路径下的模块
- 使用
sys.path.append(yourmodulepath)
- 使用
-
将一个路径加入到python系统路径下,避免每次通过代码指定路径
- 利用系统环境变量
export PYTHONPATH=$PYTHONPATH:yourmodulepath
, - 直接将这个路径链接到类似
/Library/Python/2.7/site-packages
目录下
- 利用系统环境变量
-
好的建议
-
经常使用
if __name__ == '__main__'
,保证你写包既可以import又可以独立运行,用于test。 -
多次import不会多次执行模块,只会执行一次。可以使用
reload
来强制运行模块,但不提倡。
-
为了组织好模块,将多个模块分为一个包。包是python模块文件所在的目录,且该目录下必须存在__init__.py
文件。
常见的包结构如下:
package_a
├── __init__.py
├── module_a1.py
└── module_a2.py
package_b
├── __init__.py
├── module_b1.py
└── module_b2.py
main.py
- 如果
main.py
想要引用packagea
中的模块module
a1
,可以使用:
- 如果
packagea
中的module
a1
需要引用packageb
,那么默认情况下,python是找不到package
b
。我们可以使用sys.path.append('../')
,可以在packagea
中的__init__.py
添加这句话,然后该包下得所有module都添加*import __init
_
即可。