首页 > 代码库 > PYTHON_DAY_05

PYTHON_DAY_05

目录

一.匿名函数 (lambda())

二.内置函数 (map()、filter()、reduce()、zip()、sorted())

三.yield关键字的另外一种用法

四.递归调用

五.正则表达式 re模块

六.模块与包


 

一.匿名函数(lambda)

1.当传入函数时,有些时候不需要显示的定义函数,直接传入匿名函数更方便

 

2.匿名函数提供了有限支持,以map()为例,计算f(x)=x2时,除了定义一个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])

  

 

用到的模块

  os.pass.isdir()
    - 判断后面的文件是否是目录,是的话返回True(不能判断目录是否存在,不存在也会返回false)
  os.path.isfile()
    - 判断后面的文件是否是文件,是的话返回True(不能判断文件是否存在,不存在也会返回false)
  os.path.join()
    - 连接括号中的路径(文件)
    os.path.join(‘/etc/‘,‘passwd‘,‘abc‘) ----> /etc/passwd/abc
 

注意

  Python3默认递归的深度不能超过100层



五.正则表达式 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”

意思就是说让你写的脚本模块既可以导入到别的模块中用,另外该模块自己也可执行

这句话,可能一开始听的还不是很懂。下面举例说明:

先写一个模块:

#module.py

def main():
  print "we are in %s"%__name__
if __name__ == ‘__main__‘:
  main()

  

这个函数定义了一个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会在以下路径中搜索模块顺序:

  1. 内存   只在第一次导入执行后开辟内存空间  多次导入不生效    
  2. 内置
  3. 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 中的模块 modulea1,可以使用:
    from package_a import module_a1 import package_a.module_a1
  • 如果 packagea 中的 modulea1需要引用 packageb,那么默认情况下,python是找不到 packageb。我们可以使用 sys.path.append(‘../‘),可以在packagea中的__init__.py添加这句话,然后该包下得所有module都添加* import __init_即可。

PYTHON_DAY_05