本节内容:
- 常用的几个内置函数
- 列表生成式
- 迭代器
- 生成器
- 软件目录结构规范
- 常用的模块
一、几个内置函数
# a= [0,1,2,3,4,5,6,7,8,9] # #取a中大于5的 filter 对值进行操作 # filter(lambda x:x>6,a) # b=filter(lambda x:x>6,a) # for i in b: # print(i) # #map 对操作运算结果进行操作 # c=map(lambda x:x*x,a) # for i in c: # print(i) # #如果是对值进行判断只会返回True or False # c=map(lambda x:x>6,a) # for i in c: # print(i) # from functools import reduce # d = reduce(lambda x,y:x+y,a)#reduce 需要两个参数,第一次循环先把a中的0赋给x,1赋给y然后计算结果, # # 然后把结果再赋给x,在把2赋给y,在计算结果,然后把结果赋给x 再把3 赋给y 依次类推 # print(d) # #eval()把字符串形式的表达式 解析并执行 b = "1+4/2" eval(b) # # exec 把字符串形式的代码,解析并执行 # # compile()把一个代码问价加载进来,按 exec 或eval的方式解析执行 #print() # import time # for i in range(10): # time.sleep(1) # print("#",end="",flush=True)
二、列表生成式
现在有一需求,把列表[0,1,2,3,4,5,6,7,8,9]中的每个值都加1,怎么实现
a= [0,1,3,4,5,6,7,8,9] b = [] for i in a : b.append(i+1) a= b print(a)
for index, i in enumerate(a): a[index]+=1 print(a)
a= [0,1,2,3,4,5,6,7,8,9] a=map(lambda x:x+1,a) for i in a: print(i)
还有一种方法
a= [i+1 for i in range(10)]
b= [i+1 if i >5 else i for i in range(10)] print(a)
这种叫列表生成
三、生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
>>> L = [i*i for i in range(10)] >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> g =(i*i for i in range(10)) >>> g <generator object <genexpr> at 0x02C25780> >>>
创建L
和g
的区别仅在于最外层的[]
和()
,L
是一个list,而g
是一个generator。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
如果要一个一个打印出来,可以通过next()
函数获得generator的下一个返回值:
>>> next(g) 0 >>> next(g) 1 >>> next(g) 4 >>> next(g) 9 >>> next(g) 16 ....
我们讲过,generator保存的是算法,每次调用next(g)
,就计算出g
的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。
当然,上面这种不断调用next(g)
实在是太变态了,正确的方法是使用for
循环,因为generator也是可迭代对象:
>>> g =(i*i for i in range(10)) >>> for i in g:print(i) 0 1 4 9 16 25 36 49 64 81 >>>
所以,我们创建了一个generator后,基本上永远不会调用next()
,而是通过for
循环来迭代它,并且不需要关心StopIteration
的错误。
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for
循环无法实现的时候,还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:
def fib(number): n,a,b = 0,0,1 while n <number: print(b) a,b=b,a+b n+=1 fib(10)
1
1
2
3
5
8
13
21
34
55
可以看出,fib
函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
也就是说,上面的函数和generator仅一步之遥。要把fib
函数变成generator,只需要把print(b)
改为yield b
就可以了:
1 def fib(number): 2 n,a,b = 0,0,1 3 while n <number: 4 # print(b) 5 yield b 6 a,b=b,a+b 7 n+=1
这就是定义generator的另一种方法。如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator:
f = fib(10) print(f) <generator object fib at 0x005AB090>
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,相当于函数中的return ,再次执行时从上次返回的yield
语句处继续执行。生成器yield 保存了函数的中断状态
f = fib(10) print(f) print(f.__next__()) print(f.__next__()) print(f.__next__()) print("干点别的事") print(f.__next__()) print(f.__next__()) print(f.__next__()) #输入的结果: <generator object fib at 0x0063B0C0> 1 1 2 干点别的事 3 5 8
在上面fib
的例子,我们在循环过程中不断调用yield
,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。
同样的,把函数改成generator后,我们基本上从来不会用next()
来获取下一个返回值,而是直接使用for
循环来迭代:
for i in fib(10): print(i) #输出: 1 1 2 3 5 8 13 21 34 55
还可通过yield实现在单线程的情况下实现并发运算的效果:
import time def consumer(name): print("%s 准备吃包子了"%name) while True: baozi=yield #保存函数的中断状态,可以接受传进来的值,(遇到yield说明它就是个生成器,调用next()才能执行) #接收到producer send过来的包子,并赋值给包子变量 print("包子[%s]来了,被[%s]吃了"%(baozi,name)) def producer(name): c = consumer("A") c2= consumer("B") c.__next__() c2.__next__() print("老子开始做包子了") for i in range(10): time.sleep(1) print("做了2个包子") c.send(i) #唤醒生成器并传值(调用next 并传了一个值给yield) c2.send(i) producer("alex")
四、迭代器
我们已经知道,可以直接作用于for
循环的数据类型有以下几种:
一类是集合数据类型,如list
、tuple
、dict
、set
、str
等;
一类是generator
,包括生成器和带yield
的generator function。
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
可以使用isinstance()
判断一个对象是否是Iterable
对象:
1
2
3
4
5
6
7
8
9
10
11
|
>>>
from
collections import Iterable
>>>
isinstance
([], Iterable)
True
>>>
isinstance
({}, Iterable)
True
>>>
isinstance
(
'abc'
, Iterable)
True
>>>
isinstance
((x
for
x in range ( 10 )), Iterable)
True
>>>
isinstance
(
100
, Iterable)
False
|
而生成器不但可以作用于for
循环,还可以被next()
函数不断调用并返回下一个值,直到最后抛出StopIteration
错误表示无法继续返回下一个值了。
*可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
。
可以使用isinstance()
判断一个对象是否是Iterator
对象:
1
2
3
4
5
6
7
8
9
|
>>>
from
collections import Iterator
>>>
isinstance
((x
for
x in range ( 10 )), Iterator)
True
>>>
isinstance
([], Iterator)
False
>>>
isinstance
({}, Iterator)
False
>>>
isinstance
(
'abc'
, Iterator)
False
|
生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。
1
2
3
4
|
>>>
isinstance
(
iter
([]), Iterator)
True
>>>
isinstance
(
iter
(
'abc'
), Iterator)
True
|
你可能会问,为什么list
、dict
、str
等数据类型不是Iterator
?
这是因为Python的Iterator
对象表示的是一个数据流,Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
小结
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;
集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
五、常用的几个模块
模块,用一砣代码实现了某个功能的代码集合。
类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合。而对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个 .py 文件组成的代码集合就称为模块。
如:os 是系统相关的模块;file是文件操作相关的模块
模块分为三种:
- 自定义模块
- 内置标准模块(又称标准库)
- 开源模块
time 和datetime
import time print(time.time())#时间戳 距离计算机系统诞生元年的时间单位s (1970.1.1) # print(time.clock()) #返回处理器时间,3.3开始已废弃 , 改成了time.process_time()测量处理器运算时间,不包括sleep时间,不稳定,mac上测不出来 # print(time.altzone) #返回与utc时间的时间差,以秒计算\ # print(time.asctime()) #返回时间格式"Fri Aug 19 11:14:16 2016", # print(time.localtime()) #返回本地时间 的struct time对象格式 t = time.localtime() print(t.tm_year,t.tm_mon,t.tm_mday) #可以定制的显示时间 # print(time.gmtime(time.time()-800000)) #返回utc时间的struc时间对象格式 # print(time.asctime(time.localtime())) #返回时间格式"Fri Aug 19 11:14:16 2016", #print(time.ctime()) #返回Fri Aug 19 12:38:29 2016 格式, 同上 print(time.strftime("%Y-%m-%d %H:%M:%S"))#自定义时间格式 struct_time = time.localtime(time.time() - 86400) print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time)) #显示前一天的时间 # 日期字符串 转成 时间戳 # string_2_struct = time.strptime("2016/05/22","%Y/%m/%d") #将 日期字符串 转成 struct时间对象格式 # print(string_2_struct) # # # struct_2_stamp = time.mktime(string_2_struct) #将struct时间对象转成时间戳 # print(struct_2_stamp) #将时间戳转为字符串格式 # print(time.gmtime(time.time()-86640)) #将utc时间戳转换成struct_time格式 # print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()) ) #将utc struct_time格式转成指定的字符串格式 #时间加减 import datetime # print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925 #print(datetime.date.fromtimestamp(time.time()) ) # 时间戳直接转成日期格式 2016-08-19 # print(datetime.datetime.now() ) # print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天 # print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天 # print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时 # print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分 # # c_time = datetime.datetime.now() # print(c_time.replace(minute=3,hour=2)) #时间替换
Directive | Meaning | Notes |
---|---|---|
%a | Locale’s abbreviated weekday name. | |
%A | Locale’s full weekday name. | |
%b | Locale’s abbreviated month name. | |
%B | Locale’s full month name. | |
%c | Locale’s appropriate date and time representation. | |
%d | Day of the month as a decimal number [01,31]. | |
%H | Hour (24-hour clock) as a decimal number [00,23]. | |
%I | Hour (12-hour clock) as a decimal number [01,12]. | |
%j | Day of the year as a decimal number [001,366]. | |
%m | Month as a decimal number [01,12]. | |
%M | Minute as a decimal number [00,59]. | |
%p | Locale’s equivalent of either AM or PM. | (1) |
%S | Second as a decimal number [00,61]. | (2) |
%U | Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. | (3) |
%w | Weekday as a decimal number [0(Sunday),6]. | |
%W | Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. | (3) |
%x | Locale’s appropriate date representation. | |
%X | Locale’s appropriate time representation. | |
%y | Year without century as a decimal number [00,99]. | |
%Y | Year with century as a decimal number. | |
%z | Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59]. | |
%Z | Time zone name (no characters if no time zone exists). | |
%% | A literal '%' character. |
random 模块
import random print(random.randint(1,10)) #生成随机数 print(random.randrange(1,10))#同上,只不过生成的随机数不包含10 print(random.randrange(1,20,2))#可以加步长 print(random.sample([1,2,3,4,5],2)) #可以随机取两个数 print(random.sample(range(100),2))
生成随机验证码:
import random chek_code = "" for i in range(4): current=random.randrange(0,4) if current != i: tem = chr(random.randint(65,90)) else: tem = random.randint(0,9) chek_code += str(tem) print(chek_code)
另一种方法:
1 import random,string 2 print(string.ascii_letters) #显示所有大小写字母 3 print(string.ascii_lowercase)#显示所有小写字母 4 print(string.ascii_uppercase)#显示所有大写字母 5 print(string.digits) #显示0-9的所有数字 6 source = string.digits + string.ascii_uppercase 7 print("".join(random.sample(source,6)))
pickle&json模块
pickle模块
import pickle #写到文件时 account = { "id":12324, "credit":15000, "banlance":8000 } f = open("account","wb") # f.write(account) f.write(pickle.dumps(account)) #是以bytes格式写的所以打开问件时要以“wb”的方式 #pickle.dump(account,f)同上 f.close()
import pickle #读到内存时 f= open("account","rb") account = pickle.loads(f.read()) # 等同于account = pickle.load(f) print(account) print(account["id"])
json模块
json的用法和pickle完全相同只是在写到文件时是以str字符串的形式而不是bytes
pickle是python解释器自己可以把内存中所有的数据类型转到硬盘,而json是大多数语言通用的支持的数据类型:srt,int,list,set,dict,tuple
小结:用于序列化的工具:pickle和json同时提供了四个功能:dumps、dump、loads、load
logging模块
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug()
, info()
, warning()
, error()
and critical() 5个级别,
下面我们看一下怎么用。
最简单的用法:
import logging logging.warning("user [alex] attempted wrong password more than 3 times") logging.critical("server is down") #输出 WARNING:root:user [alex] attempted wrong password more than 3 times CRITICAL:root:server is down
如果想把日志写到文件里,也很简单
import logging
logging.basicConfig(filename='example.log', level=logging.INFO) #example,log 日志的文件名 logging.debug('This message should not go to the log file') logging.info('So should this') logging.warning('And this, too') #输出 INFO:root:So should this WARNING:root:And this, too
其中下面这句中的level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里,在这个例子, 第一条日志是不会被纪录的,如果希望纪录debug的日志,那把日志级别改成DEBUG就行了。
日志记录加上日期时间
import logging logging.basicConfig(filename='example.log', level=logging.INFO, format="%(asctime)s %(message)s", datefmt='%m/%d/%Y %I:%M:%S %p' ) logging.debug('This message should not go to the log file') logging.info('So should this') logging.warning('And this, too') ###shuchu 02/23/2017 04:16:57 PM So should this 02/23/2017 04:16:57 PM And this, too
日志格式
%(name)s | Logger的名字 |
%(levelno)s | 数字形式的日志级别 |
%(levelname)s | 文本形式的日志级别 |
%(pathname)s | 调用日志输出函数的模块的完整路径名,可能没有 |
%(filename)s | 调用日志输出函数的模块的文件名 |
%(module)s | 调用日志输出函数的模块名 |
%(funcName)s | 调用日志输出函数的函数名 |
%(lineno)d | 调用日志输出函数的语句所在的代码行 |
%(created)f | 当前时间,用UNIX标准的表示时间的浮 点数表示 |
%(relativeCreated)d | 输出日志信息时的,自Logger创建以 来的毫秒数 |
%(asctime)s | 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 |
%(thread)d | 线程ID。可能没有 |
%(threadName)s | 线程名。可能没有 |
%(process)d | 进程ID。可能没有 |
%(message)s | 用户输出的消息 |
如果想同时把log打印在屏幕和文件日志里,就需要了解一点复杂的知识 了
Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:
logger提供了应用程序可以直接使用的接口;
handler将(logger创建的)日志记录发送到合适的目的输出;
filter提供了细度设备来决定输出哪条日志记录;
formatter决定日志记录的最终输出格式。
logger
每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名,比如聊天工具的图形界面模块可以这样获得它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模块可以这样:
LOG=logging.getLogger(”chat.kernel”)
Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或删除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以设置的日志级别
handler
handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler
Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
Handler.setFormatter():给这个handler选择一个格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象
每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:
1) logging.StreamHandler
使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。它的构造函数是:
StreamHandler([strm])
其中strm参数是一个文件对象。默认是sys.stderr
2) logging.FileHandler
和StreamHandler类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:
FileHandler(filename[,mode])
filename是文件名,必须指定一个文件名。
mode是文件的打开方式。参见Python内置函数open()的用法。默认是’a',即添加到文件末尾。
3) logging.handlers.RotatingFileHandler
这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的构造函数是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode两个参数和FileHandler一样。
maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。
4) logging.handlers.TimedRotatingFileHandler
这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
interval是时间间隔。
when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
S 秒
M 分
H 小时
D 天
W 每星期(interval==0时代表星期一)
midnight 每天凌晨
import logging #create logger logger = logging.getLogger('TEST-LOG') logger.setLevel(logging.DEBUG) # create console handler and set level to debug ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # create file handler and set level to warning fh = logging.FileHandler("access.log") fh.setLevel(logging.WARNING) # create formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # add formatter to ch and fh ch.setFormatter(formatter) fh.setFormatter(formatter) # add ch and fh to logger logger.addHandler(ch) logger.addHandler(fh) # 'application' code logger.debug('debug message') logger.info('info message') logger.warn('warn message') logger.error('error message') logger.critical('critical message')
逻辑关系图:
文件自动截断例子
import logging from logging import handlers logger = logging.getLogger(__name__) log_file = "timelog.log" #fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3) fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3) formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s') fh.setFormatter(formatter) logger.addHandler(fh) logger.warning("test1") logger.warning("test12") logger.warning("test13") logger.warning("test14")