day5常用内置模块(一)生成器,迭代器,内置函数
模块,用一砣代码实现了某个功能的代码集合。
类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合。而对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个 .py 文件组成的代码集合就称为模块。
如:os 是系统相关的模块;file是文件操作相关的模块
模块分为三种:
- 自定义模块
- 内置标准模块(又称标准库)
- 开源模块
一、time & datetime模块
1 #!/usr/bin/env python 2 # _*_ coding:utf-8 _*_ 3 4 import time 5 6 7 # print(time.clock()) #返回处理器时间,3.3开始已废弃 , 改成了time.process_time()测量处理器运算时间,不包括sleep时间,不稳定,mac上测不出来 8 # print(time.altzone/60/60) #返回与utc时间的时间差,以秒计算\ 9 # print(time.asctime()) #返回时间格式"Fri Aug 19 11:14:16 2016", 10 # print(time.localtime()) #返回本地时间 的struct time对象格式 11 # print(time.gmtime(time.time()-800000)) #返回utc时间的struc时间对象格式 12 # 13 # print(time.asctime(time.localtime())) #返回时间格式"Fri Aug 19 11:14:16 2016", 14 # print(time.ctime()) #返回Fri Aug 19 12:38:29 2016 格式, 同上 15 16 17 18 # 日期字符串 转成 时间戳 19 # string_2_struct = time.strptime("2016/05/22","%Y/%m/%d") #将 日期字符串 转成 struct时间对象格式 20 # print(string_2_struct) 21 # # 22 # struct_2_stamp = time.mktime(string_2_struct) #将struct时间对象转成时间戳 23 # print(struct_2_stamp) 24 25 26 27 #将时间戳转为字符串格式 28 # print(time.gmtime(time.time()-86640)) #将utc时间戳转换成struct_time格式 29 # print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()) ) #将utc struct_time格式转成指定的字符串格式 30 31 32 33 34 35 #时间加减 36 import datetime 37 38 # print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925 39 #print(datetime.date.fromtimestamp(time.time()) ) # 时间戳直接转成日期格式 2016-08-19 40 # print(datetime.datetime.now() ) 41 # print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天 42 # print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天 43 # print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时 44 # print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分 45 46 47 # 48 # c_time = datetime.datetime.now() 49 # 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模块
随机数
1 import random 2 print(random.random()) 3 print(random.randint(1,4)) 4 print(random.randrange(1,10)) 5 ''' 6 运行结果 7 0.6149606660039567 8 2 9 8 10 '''
生成随机验证码
1 import random 2 checkcode = '' 3 for i in range(4): 4 current = random.randrange(0,4) 5 if current != i: 6 temp = chr(random.randint(65,90)) 7 else: 8 temp = random.randint(0,9) 9 checkcode += str(temp) 10 print(checkcode) 11 ''' 12 运行结果 13 DOO1 14 '''
1 import string,random 2 source = string.digits + string.ascii_lowercase 3 print("".join(random.sample(source,6))) 4 ''' 5 运行结果 6 02mcqx 7 '''
三、OS模块
提供对操作系统进行调用的接口
1 # os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 2 # os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd 3 # os.curdir 返回当前目录: ('.') 4 # os.pardir 获取当前目录的父目录字符串名:('..') 5 # os.makedirs('dirname1/dirname2') 可生成多层递归目录 6 # os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 7 # os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname 8 # os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname 9 # os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 10 # os.remove() 删除一个文件 11 # os.rename("oldname","newname") 重命名文件/目录 12 # os.stat('path/filename') 获取文件/目录信息 13 # os.sep 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/" 14 # os.linesep 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n" 15 # os.pathsep 输出用于分割文件路径的字符串 16 # os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' 17 # os.system("bash command") 运行shell命令,直接显示 18 # os.environ 获取系统环境变量 19 # os.path.abspath(path) 返回path规范化的绝对路径 20 # os.path.split(path) 将path分割成目录和文件名二元组返回 21 # os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 22 # os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素 23 # os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False 24 # os.path.isabs(path) 如果path是绝对路径,返回True 25 # os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False 26 # os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False 27 # os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 28 # os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间 29 # os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间
os.path.normcase(path) #在Linux和Mac平台上,该函数会原样返回path,在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为饭斜杠。
1 可以os.path.pardir 等下面的路径名 2 3 4 curdir = '.' 5 pardir = '..' 6 extsep = '.' 7 sep = '\\' 8 pathsep = ';' 9 altsep = '/' 10 defpath = '.;C:\\bin' 11 devnull = 'nul'
四、sys模块
1 sys.argv 命令行参数List,第一个元素是程序本身路径 2 sys.exit(n) 退出程序,正常退出时exit(0) 3 sys.version 获取Python解释程序的版本信息 4 sys.maxint 最大的Int值 5 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 6 sys.platform 返回操作系统平台名称 7 sys.stdout.write('please:') 8 val = sys.stdin.readline()[:-1]
五、json & pickle 模块
用于序列化的两个模块
- json,用于字符串 和 python数据类型间进行转换
- pickle,用于python特有的类型 和 python的数据类型间进行转换
Json模块提供了四个功能:dumps、dump、loads、load
pickle模块提供了四个功能:dumps、dump、loads、load
除了打开文件pickle是以二进制b打开之外。其他用法两个全部一样。
1 import pickle 2 account = { 3 "id":3333, 4 "credit":15000, 5 "balance":8000, 6 "passwd":"abc" 7 } 8 f = open("account_db","wb") 9 pickle.dump(account,f) 10 f.close()
1 import pickle 2 f = open("account_db","rb") 3 account = pickle.load(f) 4 f.close() 5 print(account) 6
1 import json 2 3 info = { 4 'name':'aige', 5 'age':22, 6 } 7 f = open("json.test","w") 8 f.write( json.dumps(info) ) 9 f.close()
1 import json 2 f = open("json.test","r") 3 data = json.loads(f.read()) 4 print(data["age"])
六、日志模块
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug()
, info()
, warning()
, error()
and critical() 5个级别,
下面我们看一下怎么用。
最简单用法
1 import logging 2 3 logging.warning("user [aige] attempted wrong password more than 3 times") 4 logging.critical("server is down") 5 6 ''' 7 运行结果 8 WARNING:root:user [aige] attempted wrong password more than 3 times 9 CRITICAL:root:server is down 10 '''
看一下这几个日志级别分别代表什么意思
Level |
When it’s used |
DEBUG |
Detailed information, typically of interest only when diagnosing problems. |
INFO |
Confirmation that things are working as expected. |
WARNING |
An indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected. |
ERROR |
Due to a more serious problem, the software has not been able to perform some function. |
CRITICAL |
A serious error, indicating that the program itself may be unable to continue running. |
如果想把日志写到文件里,也很简单
1 import logging 2 3 logging.basicConfig(filename='example.log', level=logging.INFO) 4 logging.debug('This message should go to the log file') 5 logging.info('So should this') 6 logging.warning('And this, too')
其中下面这句中的level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里,在这个例子, 第一条日志是不会被纪录的,如果希望纪录debug的日志,那把日志级别改成DEBUG就行了。
1 logging.basicConfig(filename='example.log',level=logging.INFO)
感觉上面的日志格式忘记加上时间啦,日志不知道时间怎么行呢,下面就来加上!
1 import logging 2 3 logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') 4 logging.warning('is when this event was logged.') 5 ''' 6 运行结果 7 02/19/2017 11:37:25 AM is when this event was logged. 8 '''
日志格式
%(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 每天凌晨
1 import logging 2 from logging import handlers 3 4 # create logger 5 logger = logging.getLogger('TEST-LOG') 6 logger.setLevel(logging.ERROR) 7 8 # create console handler and set level to debug 9 ch = logging.StreamHandler() 10 ch.setLevel(logging.INFO) 11 12 # create file handler and set level to warning 13 #fh = handlers.TimedRotatingFileHandler("access.log",when="S",interval=5,backupCount=3) 14 fh = handlers.RotatingFileHandler("access.log",maxBytes=4,backupCount=2) 15 fh.setLevel(logging.WARNING) 16 17 # create formatter 18 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 19 20 # add formatter to ch and fh 21 ch.setFormatter(formatter) 22 fh.setFormatter(formatter) 23 24 # add ch and fh to logger 25 logger.addHandler(ch) 26 logger.addHandler(fh) 27 28 # 'application' code 29 logger.debug('debug message') 30 logger.info('info message') 31 logger.warn('warn message') 32 logger.error('error message') 33 logger.critical('critical message')
1 import logging 2 3 from logging import handlers 4 5 logger = logging.getLogger(__name__) 6 7 log_file = "timelog.log" 8 #fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3) 9 fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3) 10 11 12 formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s') 13 14 fh.setFormatter(formatter) 15 16 logger.addHandler(fh) 17 18 19 logger.warning("test1") 20 logger.warning("test12") 21 logger.warning("test13") 22 logger.warning("test14")
生成器、迭代器、内置函数
一、生成式
列表生成式
孩子,我现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],我要求你把列表里的每个值加1,你怎么实现?你可能会想到2种方式
1 a = [1,3,4,6,7,7,8,9,11] 2 3 for index,i in enumerate(a): 4 a[index] +=1 5 print(a)
1 >>> a 2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 3 >>> a = map(lambda x:x+1, a) 4 >>> a 5 <map object at 0x101d2c630> 6 >>> for i in a:print(i) 7 ... 8 2 9 3 10 4 11 5 12 6 13 7 14 8 15 9 16 10 17 11
1 >>> a = [i+1 for i in range(10)] 2 >>> a 3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
1 >>> L=[x*x for x in range(10)] 2 >>> L 3 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 4 >>> g = (x*x for x in range(10)) 5 >>> g 6 <generator object <genexpr> at 0x000001CED4583BF8> 7 >>> for i in g: 8 ... print(i) 9 ... 10 0 11 1 12 4 13 9 14 16 15 25 16 36 17 49 18 64 19 81
创建L
和g
的区别仅在于最外层的[]
和()
,L
是一个list,而g
是一个generator。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
如果要一个一个打印出来,可以通过next()
函数获得generator的下一个返回值:
1 >>> g = (x*x for x in range(10)) 2 >>> next(g) 3 0 4 >>> next(g) 5 1 6 >>> next(g) 7 4 8 >>> next(g) 9 9 10 >>> next(g) 11 16 12 >>> next(g) 13 25 14 >>> next(g) 15 36 16 >>> next(g) 17 49 18 >>> next(g) 19 64 20 >>> next(g) 21 81 22 >>> next(g) 23 Traceback (most recent call last): 24 File "<stdin>", line 1, in <module> 25 StopIteration 26 >>>
我们讲过,generator保存的是算法,每次调用next(g)
,就计算出g
的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。
当然,上面这种不断调用next(g)
实在是太变态了,正确的方法是使用for
循环,因为generator也是可迭代对象:
1 >>> g = (x * x for x in range(10)) 2 >>> for n in g: 3 ... print(n) 4 ... 5 0 6 1 7 4 8 9 9 16 10 25 11 36 12 49 13 64 14 81
所以,我们创建了一个generator后,基本上永远不会调用next()
,而是通过for
循环来迭代它,并且不需要关心StopIteration
的错误。
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for
循环无法实现的时候,还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:
1 def fib(max): 2 n, a, b = 0, 0, 1 3 while n < max: 4 print(b) 5 a, b = b, a + b 6 n = n + 1 7 return 'done' 8 fib(10)
仔细观察,可以看出,fib
函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
也就是说,上面的函数和generator仅一步之遥。要把fib
函数变成generator,只需要把print(b)
改为yield b
就可以了:
1 def fib(max): 2 n,a,b = 0,0,1 3 while n < max: 4 #print(b) 5 yield b 6 a,b = b,a+b 7 n += 1 8 return 'done'
这就是定义generator的另一种方法。如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator:
1 f = fib(6) 2 print(f) 3 ''' 4 运行结果 5 <generator object fib at 0x000001B959A04AF0> 6 '''
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
1 def fib(max): 2 n,a,b = 0,0,1 3 while n < max: 4 #print(b) 5 yield b 6 a,b = b,a+b 7 n += 1 8 return 'done' 9 data = fib(10) 10 print(data) 11 12 print(data.__next__()) 13 print(data.__next__()) 14 print("干点别的事") 15 print(data.__next__()) 16 print(data.__next__()) 17 print(data.__next__()) 18 print(data.__next__()) 19 print(data.__next__()) 20 ''' 21 运行结果 22 1 23 1 24 干点别的事 25 2 26 3 27 5 28 8 29 13 30 '''
在上面fib
的例子,我们在循环过程中不断调用yield
,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。
同样的,把函数改成generator后,我们基本上从来不会用next()
来获取下一个返回值,而是直接使用for
循环来迭代:
1 def fib(max): 2 n,a,b = 0,0,1 3 while n < max: 4 #print(b) 5 yield b 6 a,b = b,a+b 7 n += 1 8 return 'done' 9 data = fib(10) 10 print(data) 11 for i in data: 12 print(i) 13 ''' 14 运行结果 15 <generator object fib at 0x000001C7CB464AF0> 16 1 17 1 18 2 19 3 20 5 21 8 22 13 23 21 24 34 25 55 26 '''
但是用for
循环调用generator时,发现拿不到generator的return
语句的返回值。如果想要拿到返回值,必须捕获StopIteration
错误,返回值包含在StopIteration
的value
中:
1 def fib(max): 2 n,a,b = 0,0,1 3 while n < max: 4 #print(b) 5 yield b 6 a,b = b,a+b 7 n += 1 8 return 'done' 9 g = fib(6) 10 while True: 11 try: 12 x = next(g) 13 print('g:', x) 14 except StopIteration as e: 15 print('Generator return value:', e.value) 16 break 17 ''' 18 运行结果 19 g: 1 20 g: 1 21 g: 2 22 g: 3 23 g: 5 24 g: 8 25 Generator return value: done 26 '''
还可通过yield实现在单线程的情况下实现并发运算的效果
1 import time 2 def consumer(name): 3 print("%s 准备吃包子啦!" %name) 4 while True: 5 baozi = yield 6 print("包子[%s]来了,被[%s]吃了!" %(baozi,name)) 7 8 9 def producer(name): 10 c = consumer('A') 11 c2 = consumer('B') 12 c.__next__() 13 c2.__next__() 14 print("老子开始准备做包子啦!") 15 for i in range(10): 16 time.sleep(1) 17 print("做了2个包子!") 18 c.send(i) 19 c2.send(i) 20 21 producer("aige")
二、迭代器
我们已经知道,可以直接作用于for
循环的数据类型有以下几种:
一类是集合数据类型,如list
、tuple
、dict
、set
、str
等;
一类是generator
,包括生成器和带yield
的generator function。
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
可以使用isinstance()
判断一个对象是否是Iterable
对象:
1 >>> from collections import Iterable 2 >>> isinstance([], Iterable) #列表 3 True 4 >>> isinstance({}, Iterable) #字典 5 True 6 >>> isinstance("abc", Iterable) #字符串 7 True 8 >>> isinstance((x for x in range(10)), Iterable) #生成器 9 True 10 >>> isinstance(100, Iterable) #纯数字 11 False 12 >>> isinstance((), Iterable) #元组 13 True
而生成器不但可以作用于for
循环,还可以被next()
函数不断调用并返回下一个值,直到最后抛出StopIteration
错误表示无法继续返回下一个值了。
*可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
。
可以使用isinstance()
判断一个对象是否是Iterator
对象:
生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数:
你可能会问,为什么list
、dict
、str
等数据类型不是Iterator
?
这是因为Python的Iterator
对象表示的是一个数据流,Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
小结
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;
集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
Python的for
循环本质上就是通过不断调用next()
函数实现的,例如:
1 for x in [1, 2, 3, 4, 5]: 2 pass
实际上完全等价于:
1 # 首先获得Iterator对象: 2 it = iter([1, 2, 3, 4, 5]) 3 # 循环: 4 while True: 5 try: 6 # 获得下一个值: 7 x = next(it) 8 except StopIteration: 9 # 遇到StopIteration就退出循环 10 break
内置函数
1 #!/usr/bin/env python 2 # _*_ coding:utf-8 _*_ 3 4 # a = abs(-10) #绝对值 5 # print(a) #结果:10 6 # a = [1,2,3,4,5,6,0] 7 # print(all(a)) #结果:False 有0返回False 8 # print(any(a)) #结果:True 有非0的,返回True 9 # a = 'a' 10 # b=ascii(a) 11 # print(b) #不知道返回啥 12 # a=1 13 # print(bin(a)) #结果:0b1 不知道干嘛用的 14 # a=1 15 # b=0 16 # print(bool(a),bool(b)) #结果:True False 判断布尔值 17 # print(bytes(a)) #结果:b'\x00' 18 # def a(): 19 # pass 20 # b=1 21 # print(callable(a),callable(b)) #结果:True False 判断是否可调用 22 23 # a=97 24 # print(chr(a)) #结果:a ascii码对应的字母 25 # f = open("斐波那契.py",encoding="utf-8") 26 # data =compile(f.read(),'','exec') 27 # exec(data) 28 # ''' 29 # 运行结果: 30 # 斐波那契函数的运行结果 31 # ''' 32 # a=1 33 # b=2 34 # print(complex(a)) #结果:(1+0j) 复数,不理会 35 #----------------------------------------------------------------------- 36 # a = [1,2,3] 37 # b = ["a","b","c","d"] 38 # print(dict()) #不懂用。 39 # dir() #结果,该变量或者函数拥有的所有方法 40 # print(divmod(19,2)) #结果:(9, 1) (x//y, x%y) #商和余数 41 # a= [1,2,3,4,5,6,7,77] 42 # for k,v in enumerate(a): 43 # print(k,"-------",v) #结果,下标和对应的value 44 # eval('print("----------------")') #结果:---------------- 把字符串形式的表达式解析并执行 45 # exec() # 把字符串形式的代码解析并执行 46 47 # a=filter(lambda x:x>5,range(10)) #fileter(lammbda x:x>5,range()) 列表满足函数的条件则留下 48 # for i in a: 49 # print(i) 50 # a=1 51 # print(float(a)) #浮点数 52 # format() #字符串格式化 53 # frozenset() #不可变集合 54 # globals() #返回全局变量,以字典的形式 55 # hash() #把一个字符串转换成hash值,当前程序下同一个字符串的hash值保持一致 56 # help() #查看帮助 57 # hex() #16进制 58 # id() #查看内存 59 # int() #整数 60 # iter() #生成器 61 # len() #查看长度 62 # list() #列表 63 # locals() #返回当前作用域的变量,以字典的形式(也就是局部变量) 64 # map() #man(lambda x:x+1,range(10)) 65 # from functools import reduce 66 # reduce() #reduce(lambda x,y:x+y,range(10)) x等于前一次两个数值的和 67 # max() #返回最大值 68 # memoryview() #可以修改内存 69 # min() #取最小值 70 # next() 71 # oct() #八进制 72 # pow() #幂 73 # print() #字符串输出 74 # import time #打印进度条 75 # for i in range(10): 76 # time.sleep(0.5) 77 # print("#",end="",flush=True) 78 # repr() #返回一个对象的字符串形式 79 # reversed() #反转 80 # round() #一个参数四舍五入,第二个参数,表示保留几个小数 81 # slice() #切片 82 # sorted() #排序 83 # str() #字符串 84 # sum() #加法 85 # vars() #没参数和locals()相同,跟参数返回对象的__dict__方法 86 # zip() #两个列表的拉链,长度不一样的话,以短的为准,多余的不保留 87 # __import__() #动态导入模块,用户输入什么模块导入什么模块 __import__("input(>>:)")