what's the python之模块

Posted ゛竹先森゜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了what's the python之模块相关的知识,希望对你有一定的参考价值。

 

正则表达式

  首先,我们引入了正则表达式的知识。所谓正则表达式,就是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

  正则表达式本身和python没有什么关系,就是匹配字符串内容的一种规则。这里给了一个非常好用的在线测试工具http://tool.chinaz.com/regex/

  谈到正则,就只和字符串相关了。着眼于正则的时候,输入的每一个字都是一个字符串。如果在一个位置的一个值,不会出现什么变化,那么是不需要规则的,直接就可以匹配上。在之后我们更多要考虑的是在同一个位置上可以出现的字符的范围。

  字符组 : 形式为——[字符组]

  在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[ ]表示字符分为很多类,比如数字、字母、标点等等。假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一。可以用[0123456789]表达,也可以用[0-9],后者只能从小指到大,即不可以用[9-0]的形式。

字符组
待匹配字符
匹配
结果
说明
[0123456789]
8
True
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符
和"待匹配字符"相同都视为可以匹配
[0123456789]
a
False
由于字符组中没有"a"字符,所以不能匹配
 
[0-9]
 
7
True
[0-9]和[0123456789]是一个意思
 
[a-z]
 
s
 
True
 
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示
 
[A-Z]
 
B
 
True
 
[A-Z]就表示所有的大写字母
 
[0-9][a-f][A-F]
 
e
 
True
 
可以匹配数字,大小写形式的a~f,用来验证十六进制字符

 

以下是正则表达式中的所有字符及其用法:

元字符 匹配内容
. 匹配除换行符以外的任意字符
\\w 匹配字母或数字或下划线或汉字
\\s 匹配任意的空白符
\\d 匹配数字
\\n 匹配一个换行符
\\t 匹配一个制表符
\\b 匹配一个单词的结尾
^ 匹配字符串的开始
$ 匹配字符串的结尾
\\W
匹配非字母或数字或下划线或汉字,即除\\w能匹配的类型以外的所有类型
\\D
匹配非数字,即除\\d能匹配的类型以外的所有类型
\\S
匹配非空白符,即除\\s能匹配的类型以外的所有类型
a|b
匹配字符a或字符b
()
匹配括号内的表达式,也表示一个组
[...]
匹配字符组中的字符
[^...]
匹配除了字符组中字符的所有字符,即与[]相反

 

量词
用法说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

. ^ $的用法简介

正则 待匹配字符 匹配
结果
说明
海. 海燕海东西海娇

海燕

海东

海娇

  匹配所有"海."的字符
^海. 海燕海东西海娇 海燕 只从开头匹配"海."
  海.$   海燕海东西海娇 海娇 只匹配结尾的"海.$"

 

* + ? { }的用法简介

正则 待匹配字符 匹配
结果
说明
李.? 李杰和李莲英和李二棍子

李杰
李莲
李二

 
?表示重复零次或一次,即只匹配"李"后面一个任意字符,贪婪匹配
 
李.* 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子
*表示重复零次或多次,即匹配"李"后面0或多个任意字符,贪婪匹配
李.+ 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子
+表示重复一次或多次,即只匹配"李"后面1个或多个任意字符,贪婪匹配
李.{1,2} 李杰和李莲英和李二棍子

李杰和
李莲英
李二棍

{1,2}匹配1到2次任意字符,贪婪匹配

 注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配

正则 待匹配字符 匹配
结果
说明
李.*? 李杰和李莲英和李二棍子 李杰
李莲
李二
惰性匹配

 

字符集[][^]的用法简介

正则 待匹配字符 匹配
结果
说明
李[杰莲英二棍子]* 李杰和李莲英和李二棍子

李杰
李莲英
李二棍子

 
表示匹配"李"字后面[杰莲英二棍子]的字符任意次
 
李[^和]* 李杰和李莲英和李二棍子

李杰
李莲英
李二棍子

表示匹配一个不是"和"的字符任意次
[\\d] 456bdha3

4
5
6
3

表示匹配任意一个数字,匹配到4个结果
[\\d]+ 456bdha3

456
3

表示匹配任意个数字,匹配到2个结果

 

分组 ()与 或 |[^]的用法简介

 身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部由数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:

正则 待匹配字符 匹配
结果
说明
^[1-9]\\d{13,16}[0-9x]$ 110101198001017032

110101198001017032

   表示可以匹配一个正确的身份证号
^[1-9]\\d{13,16}[0-9x]$ 1101011980010170

1101011980010170

表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字
^[1-9]\\d{14}(\\d{2}[0-9x])?$ 1101011980010170

False

现在不会匹配错误的身份证号了
()表示分组,将\\d{2}[0-9x]分成一组,就可以整体约束他们出现的次数为0-1次
^([1-9]\\d{16}[0-9x]|[1-9]\\d{14})$ 110105199812067023

110105199812067023

表示先匹配[1-9]\\d{16}[0-9x]如果没有匹配上就匹配[1-9]\\d{14}

 

转义符 \\的用法简介

在正则表达式中,有很多有特殊意义的是元字符,比如\\d和\\s等,如果要在正则中匹配正常的"\\d"而不是"数字"就需要对"\\"进行转义,变成\'\\\\\'。

在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\\也有特殊的含义,本身还需要转义。所以如果匹配一次"\\d",字符串中要写成\'\\\\d\',那么正则里就要写成"\\\\\\\\d",这样就太麻烦了。这个时候我们就用到了r\'\\d\'这个概念,此时的正则是r\'\\\\d\'就可以了。

正则 待匹配字符 匹配
结果
说明
\\d \\d  False
因为在正则表达式中\\是有特殊意义的字符,所以要匹配\\d本身,用表达式\\d无法匹配
\\\\d \\d  True
转义\\之后变成\\\\,即可匹配
"\\\\\\\\d" \'\\\\d\'  True
如果在python中,字符串中的\'\\\'也需要转义,所以每一个字符串\'\\\'又需要转义一次
r\'\\\\d\' r\'\\d\'  True
在字符串之前加r,让整个字符串不转义

 

贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配

正则 待匹配字符 匹配
结果
说明
<.*>

<script>...<script>

<script>...<script>
默认为贪婪匹配模式,会匹配尽量长的字符串
<.*?> r\'\\d\'  

<script>
<script>

加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串

注:在后面加上?后即转为非贪婪匹配模式,所有匹配长度都取最短

附:非贪婪模式的一个用法——  .*?x     就是取前面任意长度的字符,直到一个x出现

 

 

re模块下的常用方法

import re

ret = re.findall(\'a\', \'eva egon yuan\')  # 返回所有满足匹配条件的结果,放在列表里
print(ret) #结果 : [\'a\', \'a\']

ret = re.search(\'a\', \'eva egon yuan\').group()
print(ret) #结果 : \'a\'
# 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
# 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。

ret = re.match(\'a\', \'abc\').group()  # 同search,不过仅在字符串开始处进行匹配,如果开始没有匹配到就报错
print(ret)
#结果 : \'a\'

ret = re.split(\'[ab]\', \'abcd\')  # 先按\'a\'分割得到\'\'和\'bcd\',在对\'\'和\'bcd\'分别按\'b\'分割
print(ret)  # [\'\', \'\', \'cd\']

ret = re.sub(\'\\d\', \'H\', \'eva3egon4yuan4\', 1)#将数字替换成\'H\',参数1表示只替换1个
print(ret) #evaHegon4yuan4

ret = re.subn(\'\\d\', \'H\', \'eva3egon4yuan4\')#将数字替换成\'H\',返回元组(替换的结果,替换了多少次)
print(ret)#(\'evaHegonHyuanH\', 3)

obj = re.compile(\'\\d{3}\')  #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
ret = obj.search(\'abc123eeee\') #正则表达式对象调用search,参数为待匹配的字符串
print(ret.group())  #结果 : 123

import re
ret = re.finditer(\'\\d\', \'ds3sy4784a\')   #finditer返回一个存放匹配结果的迭代器
print(ret)  # <callable_iterator object at 0x10195f940>
print(next(ret).group())  #查看第一个结果即3
print(next(ret).group())  #查看第二个结果即4
print([i.group() for i in ret])  #查看剩余的结果,以列表的形式打印,即[\'7\', \'8\', \'4\']

 

注意:findall和split的优先级查询:

#1 findall的优先级查询:
import re

ret = re.findall(\'www.(sogo|baidu).com\', \'www.baidu.com\')
print(ret)  # [\'baidu\']     这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可

ret = re.findall(\'www.(?:sogo|baidu).com\', \'www.baidu.com\')
print(ret)  # [\'www.baidu.com\']





#2 split的优先级查询
ret=re.split("\\d+","eva3egon4yuan")
print(ret) #结果 : [\'eva\', \'egon\', \'yuan\']

ret=re.split("(\\d+)","eva3egon4yuan")
print(ret) #结果 : [\'eva\', \'3\', \'egon\', \'4\', \'yuan\']

#在匹配部分加上()之后所切出的结果是不同的,
#没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
#这个在某些需要保留匹配部分的使用过程是非常重要的。

 

 

下面举几个练习的栗子,需要了解,为了变得更牛逼出去更好的装逼最好还是掌握:

1、匹配标签

import re


ret = re.search("<(?P<tag_name>\\w+)>\\w+</(?P=tag_name)>","<h1>hello</h1>")
#还可以在分组中利用?<name>的形式给分组起名字
#获取的匹配结果可以直接用group(\'名字\')拿到对应的值
print(ret.group(\'tag_name\'))  #结果 :h1
print(ret.group())  #结果 :<h1>hello</h1>

ret = re.search(r"<(\\w+)>\\w+</\\1>","<h1>hello</h1>")
#如果不给组起名字,也可以用\\序号来找到对应的组,表示要找的内容和前面的组内容一致
#获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1))
print(ret.group())  #结果 :<h1>hello</h1>
View Code

2、匹配整数

import re

ret=re.findall(r"\\d+","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) #[\'1\', \'2\', \'60\', \'40\', \'35\', \'5\', \'4\', \'3\']

ret=re.findall(r"-?\\d+\\.\\d*|(-?\\d+)","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) #[\'1\', \'-2\', \'60\', \'\', \'5\', \'-4\', \'3\']

ret.remove("")
print(ret) #[\'1\', \'-2\', \'60\', \'5\', \'-4\', \'3\']

ret=re.findall(r\'(-?\\d+\\.\\d*)|-?\\d+\',\'1-2*(60+(-40.35/5)-(-4*3))\')
print(ret)#[\'\', \'\', \'\', \'-40.35\', \'\', \'\', \'\']

ret=re.findall(r\'-?\\d+\\.\\d*|-?\\d+\',\'1-2*(60+(-40.35/5)-(-4*3))\')
print(ret)#[\'1\', \'-2\', \'60\', \'-40.35\', \'5\', \'-4\', \'3\']
View Code

3、数字匹配

1、 匹配一段文本中的每行的邮箱
      http://blog.csdn.net/make164492212/article/details/51656638

2、 匹配一段文本中的每行的时间字符串,比如:‘1990-07-12’;

   分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、
   一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$

3、 匹配qq号。(腾讯QQ号从10000开始)  [1,9][0,9]{4,}

4、 匹配一个浮点数。       ^(-?\\d+)(\\.\\d+)?$   或者  -?\\d+\\.?\\d*

5、 匹配汉字。             ^[\\u4e00-\\u9fa5]{0,}$ 

6、 匹配出所有整数
View Code

4、爬虫练习

import requests

import re
import json

def getPage(url):

    response=requests.get(url)
    return response.text

def parsePage(s):
    
    com=re.compile(\'<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\\d+).*?<span class="title">(?P<title>.*?)</span>\'
                   \'.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>\',re.S)

    ret=com.finditer(s)
    for i in ret:
        yield {
            "id":i.group("id"),
            "title":i.group("title"),
            "rating_num":i.group("rating_num"),
            "comment_num":i.group("comment_num"),
        }

def main(num):

    url=\'https://movie.douban.com/top250?start=%s&filter=\'%num
    response_html=getPage(url)
    ret=parsePage(response_html)
    print(ret)
    f=open("move_info7","a",encoding="utf8")

    for obj in ret:
        print(obj)
        data=json.dumps(obj,ensure_ascii=False)
        f.write(data+"\\n")

if __name__ == \'__main__\':
    count=0
    for i in range(10):
        main(count)
        count+=25
View Code

 

关于正则的知识目前就到这里,下面要说有关模块的知识

 


 

what\'s the 是模块

  一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。

但其实import加载的模块分为四个通用类别: 

  1 使用python编写的代码(.py文件)

  2 已被编译为共享库或DLL的C或C++扩展

  3 包好一组模块的包

  4 使用C编写并链接到python解释器的内置模块

为何要使用模块?

   如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。

    随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用。

  模块的导入应该在程序的起始位置。格式为import +模块名

 

 

  首先我们学了re模块,re模块与正则表达式息息相关,关于re模块的使用,在上文正则表达式中就有提及,即findallsearchmatch的使用方法,这里不做赘述。

  然后我们来看看关于collection模块

collection模块

  在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等

    1.namedtuple: 生成可以使用名字来访问元素内容的tuple

    2.deque: 双端队列,可以快速的从另外一侧追加和推出对象

    3.Counter: 计数器,主要用来计数

    4.OrderedDict: 有序字典

    5.defaultdict: 带有默认值的字典

下面我们来详细介绍一下这5种数据类型:

namedtuple:主要用在坐标上表示,如表示一个点或者一个圆

from collections import namedtuple
Point = namedtuple(\'Point\', [\'x\', \'y\'])
p = Point(1, 2)
p.x
p.y


#表示圆
#namedtuple(\'名称\', [属性list]):
Circle = namedtuple(\'Circle\', [\'x\', \'y\', \'r\'])

 

deque:

  因为list是线性存储,数据量大的时候,插入和删除效率很低。deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈:

from collections import deque
q = deque([\'a\', \'b\', \'c\'])
q.append(\'x\')
q.appendleft(\'y\')
print(q)#[\'y\', \'a\', \'b\', \'c\', \'x\']

注:deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部添加或删除元素。

 

OrderedDict:使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。如果要保持Key的顺序,可以用OrderedDict

from collections import OrderedDict
d = dict([(\'a\', 1), (\'b\', 2), (\'c\', 3)])# dict的Key是无序的
print(d)#{\'a\': 1, \'c\': 3, \'b\': 2}

od = OrderedDict([(\'a\', 1), (\'b\', 2), (\'c\', 3)])# OrderedDict的Key是有序的
print(od)#OrderedDict([(\'a\', 1), (\'b\', 2), (\'c\', 3)])

注:OrderedDict的Key会按照插入的顺序排列,不是按照Key本身排序

 

defaultdict:使dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict

from collections import defaultdict
dd=defaultdict(lambda :\'N/A\')#即key不存在时返回设置的默认值
dd[\'k1\']=\'abc\'
print(dd[\'k1\'])#abc
print(dd[\'k2])#N/A

拿defaultdict举个小栗子:

#有如下值集合 [11,22,33,44,55,66,77,88,99,90...],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中。
#即: {\'k1\': 大于66 , \'k2\': 小于66}

#利用字典的解决方式:
l= [11, 22, 33,44,55,66,77,88,99,90]
my_dict = {}
for value in  l:
    if value>66:
        if my_dict.has_key(\'k1\'):
            my_dict[\'k1\'].append(value)
        else:
            my_dict[\'k1\'] = [value]
    else:
        if my_dict.has_key(\'k2\'):
            my_dict[\'k2\'].append(value)
        else:
            my_dict[\'k2\'] = [value]



#利用defaultdict的解决方法:
from collections import defaultdict

l= [11, 22, 33,44,55,66,77,88,99,90]

my_dict = defaultdict(list)

for value in l:
    if value>66:
        my_dict[\'k1\'].append(value)
    else:
        my_dict[\'k2\'].append(value)
View Code

 

Counter:Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。

c = Counter(\'abcdeabcdabcaba\')
print c
#Counter({\'a\': 5, \'b\': 4, \'c\': 3, \'d\': 2, \'e\': 1})

 

时间模块

  在Python中,通常有这三种方式来表示时间:时间戳(timestamp)、元组(struct_time)、格式化的时间字符串(Format String) 

  (1)时间戳(timestamp) :通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。

  (2)格式化的时间字符串(Format String):表现形式为 ‘1999-12-06’,下文会详细介绍

  (3)元组(struct_time) :struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天和是否是夏令时)

注:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的

索引(Index)属性(Attribute)值(Values)
0 tm_year(年) 比如2011
1 tm_mon(月) 1 - 12
2 tm_mday(日) 1 - 31
3 tm_hour(时) 0 - 23
4 tm_min(分) 0 - 59
5 tm_sec(秒) 0 - 61
6 tm_wday(weekday) 0 - 6(0表示周日)
7 tm_yday(一年中的第几天) 1 - 366
8 tm_isdst(是否是夏令时) 默认为-1
import time
#时间戳
print(time.time())#1502179789.9325476
#时间字符串,%都有对应的意思
print(time.strftime(\'%Y-%m-%d %X\'))#2017-08-08 16:09:49
#时间元祖
print(time.localtime())#time.struct_time(tm_year=2017, tm_mon=8, tm_mday=8, tm_hour=16, tm_min=9, tm_sec=49, tm_wday=1, tm_yday=220, tm_isdst=0)

 

有关于时间字符串中的格式化符号,具体如下:

%y 两位数的年份表示(00-99%Y 四位数的年份表示(000-9999%m 月份(01-12%d 月内中的一天(0-31%H 24小时制小时数(0-23%I 12小时制小时数(01-12%M 分钟数(00=59%S 秒(00-59以上是关于what's the python之模块的主要内容,如果未能解决你的问题,请参考以下文章

what's the python之内置函数

what's the python之异常处理

what's the python之函数及装饰器

what's the python之面向对象

what' the python之面向对象(进阶)

what's the python之基本运算符及字符串列表元祖集合字典的内置方法