声明:本文整理于虫师的博客,记载一些知识点。
迭代器
这里只讨论一个特殊方法—- iter ,这个方法是迭代器规则的基础。
迭代器规则
迭代的意思是重复做一些事很多次—就像在循环中做的那样。iter 方法返回一个迭代器,所谓迭代器就是具有next方法的对象,在调用next方法时,迭代器会返回它的下一个值。如果next方法被调用,但迭代器没有值可以返回,就会引发一个StopIteration异常。
这里是一个婓波那契数例,使用迭代器如下:
class Fibs:
def __init__(self):
self.a = 0
self.b = 1
def next(self):
self.a , self.b = self.b , self.a + self.b
return self.a
def __iter__(self):
return self
>>> fibs = Fibs()
>>> for f in fibs:
if f > 1000:
print f
break #因为设置了break ,所以循环在这里停止。
内建函数iter可以从可迭代的对象中获得迭代器。
>>> it = iter([1,2,3])
>>> it.next()
1
>>> it.next()
2
从迭代器得到序列
除了在迭代器和可迭代对象上进行迭代外,还能把它们转换为序列。在大部分能使用序列的情况下,能使用迭代器替换。
class TestIterator:
value = 0
def next(self):
self.value += 1
if self.value > 10: raise StopIteration
return self.value
def __iter__(self):
return self
>>> ti = TestIterator()
>>> list(ti)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
生成器
生成器也叫 简单生成器,生成器可以帮助读者写出非常优雅的代码,当然,编写任何程序时不使用生成器也是可以的。
创建生成器
创建一个生成器就像创建函数一样简单。
>>> def flatten(nested):
for sublist in nested:
for element in sublist:
yield element
>>> nested = [[1,2],[3,4],[5]]
#使用for循环
>>> for num in flatten(nested):
print num
1
2
3
4
5
#或使用list函数
>>> list(flatten(nested))
[1, 2, 3, 4, 5]
递归生成器
上面创建的生成器只能处理两层嵌套,为了处理嵌套使用了两个for循环,如果要处理任意层的嵌套呢?例如,可以每层嵌套需要增加一个for循环,但不知道有几层嵌套,所以必须把解决方案变得更灵活,现在可以用递归来解决。
>>> def fla(aa):
try:
for bb in aa:
for cc in fla(bb):
yield cc
except TypeError:
yield aa
>>> list(fla([[[1],2],3,4,[5,[6,7]],8])) #注意括号层次比较多
[1, 2, 3, 4, 5, 6, 7, 8]
当fla被调用时有两种情况:基本情况和需要递归的情况
在基本的情况中,函数被告知展开一个元素,这种情部下,for循环会引发一个TypeError 异常,生成会产生一个元素。
如果展开的是一个列表,那么就需要特殊情况处理。程序必须遍历所有的子列表,并对它们调用fla。
上面的做法有一个问题:如果aa 是一个类似于字符串的对象(字符串、Unicode、UserString等),那么它就是一个序列,不会引发TypeError,但是你不想对这样的对象进行迭代。
为了处理这种情况,则必须在生成器的开始处添加一个检查语句。试着将传入的对象和一个字符串拼接,看看会不会出现TypeError,这是检查一个对象是不是类似于字符串最简单快速的方法。
>>> def flatten(nested):
try:
#不要迭代类似字符串的对象
try:nested + ''
except TypeError: pass
else: raise TypeError
for sublist in nested:
for element in flatten(sublist):
yield element
except TypeError:
yield nested
>>> list(flatten(['foo',['bar',['baz']]]))
['foo', 'bar', 'baz']
如果nested+’’ 引发了一个TypError ,它就会被忽略。如果没有引发TypeError,那么内层try语句就会引发一个它自己的TypeError异常。
生成器方法
生成器新属性是在开始运行后为生成器提供值的能力。表现为生成器和“外部世界”进行交流的渠道:
* 外部作用域访问生成器的send方法,就像访问next 方法一样,只不过前者使用一个参数(发送的“消息”—任意对象)
* 在内部则挂起生成器,yield现在作为表达式而不是语句使用,换句话说,当生成器重新运行的时候,yield方法返回一个值,也就是外部通过send方法发送的值。如果next 方法被使用,那么yield方法返回None.
下面简单的方例子来说明这种机制:
>>> def repeater(value):
while True:
new =(yield value)
if new is not None:value = new
>>> r = repeater(42)
>>> r.next()
>>> r.send("hello, world!")
'hello, world!'
生成器的另两个方法:
* throw方法(使用异常类型调用,还有可选的值以及回溯对象)用于在生成器内引发一个异常(在yield表达式中)
* close 方法(调用时不用参数)用于停止生成器。
模块
模块是程序:任何python程序都可以作为模块导入。假设写如下程序,并且将它保存为以C:\python\hello.py
#hello.py
print "hello,world!"
下面通过python解释器调用:
>>> import sys
>>> sys.path.append('c:/python')
>>> import hello
hello,world!
再来一次:
>>> import hello
>>>
怎么这次没结果?因为导入模块并不意味着在导入进执行某些操作。它们主要用于定义,比如变量、函数和类等。此外,因为只需要定义这些东西一次,导入模块多次和导入一次的效果是一样的。
模块用于定义
1、在模块中定义函数
假设我们编写了一个类似代码的模块,将其保存为hello2.py 文件。
#hello2.py
def hello():
print "hello, world !"
保存后,可以像下面这样导入:
>>> import hello2
模块会被执行,这意味着hello函数在模块的作用被定义了。因此可以通过以下方式来访问函数:
>>> hello2.hello()
hello.world!
2、在模块中增加测试代码
模块用来定义函数、类和其他内容,有时候在模块中添加一些检查模块本身是否正常工作的测试代码是非常有用的。
#hello3.py
def hello():
print "hello.world!"
def test():
hello()
if __name__ == '__main__': test()
f name == ‘main’ 解释:python文件的后缀为.py ,.py文件可以用来直接运行,就像一个独立的小程序;也可以用来作为模块被其它程序调用。name是模块的内置属性,如果等于’main’ 侧表示直接被使用,那么将执行方法test()方法;如果是被调用则不执行 if 判断后面的test()方法。
执行结果:
>>> import hello3 #表示hello3模块被调用,不执行test方法,所以没有输出
>>> hello3.hello() #表示程序执行,调用test方法
hello.world!
>>> hello3.hello() #这里是不是可以反复调用test方法
hello.world!
让模块可用
前面的例子中,我们改变了sys.path,其中包含了一个目录列表,解释器在该列表中查找模块。在理想情况下,一开始sys.path本身就应该包含正确的目录,有两方法可以做到这一点: 一是将模块放置在合适的位置,别外一种是告诉解释器去哪里查找需要的模块。
1、将模块放置在正确的位置
来看看python解释器会从哪里查找模块:
>>> import sys,pprint >>> pprint.pprint(sys.path) ['', 'D:\\SoftWare\\Python27\\lib\\site-packages\\feedparser-5.2.1-py2.7.egg', 'D:\\SoftWare\\Python27\\lib\\site-packages\\pip-8.1.2-py2.7.egg', 'D:\\SoftWare\\Python27\\lib\\site-packages\\pydelicious-0.6-py2.7.egg', 'C:\\WINDOWS\\SYSTEM32\\python27.zip', 'D:\\SoftWare\\Python27\\DLLs', 'D:\\SoftWare\\Python27\\lib', 'D:\\SoftWare\\Python27\\lib\\plat-win', 'D:\\SoftWare\\Python27\\lib\\lib-tk', 'D:\\SoftWare\\Python27', 'D:\\SoftWare\\Python27\\lib\\site-packages']
尽管这些目录下都可以被找到,但site-packages 目录是最佳选择。
2、告诉编译器去哪里找
以下情况是告诉编译器去哪儿找的原因:
** 不希望将自己的模块填满python解释器的目录
** 没有在python解释器目录中存储文件的权限
** 想将模块放到其它位置
编辑sys.path ,前面就已经使用了
>>> import sys
>>> sys.path.append('c:/python')
但更优雅的做法是配置pythonpath环境变量,方法和配置java 环境变量类似。
文档
模块信息的自然来源是文档,除了通过python书籍或标准python文档来查看某个函数的含义,也可以通过下面方式:
>>> print range.__doc__
range([start,] stop[, step]) -> list of integers
Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3]. The end point is omitted!
These are exactly the valid indices for a list of 4 elements.
这样就获得了关于range函数的精确描述。
time 模块
time模块所包括的函数能够实现 以下功能:获得当前时间,操作时间和日期,从字符串读取时间以及格式化时间为字符串。
time模块中重要的函数:
asctime([tuple]):将时间元组转换为字符串
localtime([secs]):将秒数转换为日期元组,以本地时间为准
mktime(tuple):将时间元组转换为本地时间
sleep(secs):休眠secs秒
strptime(string[,format]):将字符串解析为时间元组
time():当前时间
>>> time.time() 1483343631.946 >>> time.asctime() 'Mon Jan 02 15:54:08 2017'
time.asctime将当前时间格式化为字符串
random模块
random模块包括返回随机的函数,可以用于模拟或者用于任何产生随机输出的程序。
random模块中的一些重要函数:
random():返回0到1之间的随机实数
getrandbits(n):以长整数形式返回n个随机位
uniform(a,b):返回a到b之间的随机实数
randrange([start],stop,[step]):返回range(start,stop,step)中的随机数
choice(seq):从序列seq中返回任意元素
shuffle(seq[,random]):原地指定序列seq
sample(seq,n):从序列seq中选择n个随机且独立的元素
下面介绍使用random模块的例子,还需要用到time模块中的函数。
例:首先获得代表时间间隔(2013年)限制的实数,这可以通过时间元组的方式来表示日期(使用 -1表示一周的某天,一年中某天和夏令时,以例让python自己计算),并且对这些元组调用mktime :
from random import *
from time import *
data1 = (2013 ,1,1,0,0,0,-1,-1,-1)
time1 = mktime(data1)
data2 = (2014 ,1,1,0,0,0,-1,-1,-1)
time2 = mktime(data2)
#然后在这个范围内生成随机数
>>> random_time = uniform(time1,time2)
# 可以将数字转换成易读的日期形式
>>> print asctime(localtime(random_time))
Fri Jan 18 18:23:16 2013