4、迭代器
4.1、文件迭代器
先来回顾一下上面讲过的文件的读取,我们先在程序根目录保存一个文本文件“wf.txt”,里面存的内容如下,
然后,我们写代码将其一行一行地读取出来并打印,
>>> fd = open('wf.txt', 'r')
>>> fd.readline()
'wei\n'
>>> fd.readline()
'fang'
>>> fd.readline()
''
>>>
>>> fd.close()
>>>
现在,文件有一个名为 __next__的方法,效果跟readline函数相同,每次返回文件中的一行,唯一的区别是,达到文件末尾时,__next__会引发内置的StopIteration异常,而不是返回空字符串。代码如下,
>>> fd = open('wf.txt', 'r')
>>> fd.__next__()
'wei\n'
>>> fd.__next__()
'fang'
>>> fd.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
忘了说了,python3才有这个属性,可以用dir分别打印出python2和python3的文件属性。
python2:
>>> fd = open('wf.txt', 'r')
>>> dir(fd)
['__class__', '__delattr__', '__doc__', '__enter__', '__exit__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'closed', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'mode', 'name', 'newlines', 'next', 'read', 'readinto', 'readline', 'readlines', 'seek', 'softspace', 'tell', 'truncate', 'write', 'writelines', 'xreadlines']
>>>
python3:
>>> fd = open('wf.txt', 'r')
>>> dir(fd)
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines']
>>>
既然,readline函数就能做的事,还要__next__干嘛?
for循环可以用于python中任何序列类型,包括列表、元组、字符串等。实际上,for循环还可以用于任何可迭代的对象。不止for循环,python中所有会从左到右扫描对象的迭代工具都是如此,包括for循环、列表解析、in成员关系测试,map内置函数等。
还是以文件读取为例,原始方式,可以使用for循环加上readlines方法,将文件内容加载到内存,并做成行字符串的列表,再读取,代码如下,
for line in open('wf.txt', 'r').readlines():
print(line, end='')
print()
运行结果,
$ python3 demo1.py
wei
fang
可以将open('wf.txt', 'r').readlines()
返回的结果打印出来看看,
print(open('wf.txt', 'r').readlines())
运行结果,
$ python3 demo1.py
['wei\n', 'fang']
可以发现,readlines方法一次性将文件的内容都读到内存中,如果文件很大,这样就很耗内存,甚至导致计算机内存不足。而如果用迭代器,一次读一行,就避免了这种情况,代码如下,
for line in open('wf.txt', 'r'):
print(line, end='')
print()
运行结果,
$ python3 demo1.py
wei
fang
这个就是python的迭代协议:有__next__方法的对象会前进到下一个结果,而在一系列结果的末尾时,会引发StopIteration异常。任何这类对象都是可迭代的,当这类对象以for循环等迭代工具遍历时,所有迭代工具内部都在那次迭代中调用__next__,并在捕捉到StopIteration异常时退出。
跟readline函数对比,迭代器的运行速度会更快一些,因为迭代器在python中是以c语言的速度运行的。
4.2、手动迭代:iter和next
为了支持手动迭代代码,python3.0提供了一个内置函数next,它自动调用一个对象的__next__方法,next(a)等同于a.__next__(),代码如下,
fd = open('wf.txt', 'r')
print(fd.__next__(), end='')
print(fd.__next__())
fd.close()
fd = open('wf.txt', 'r')
print(next(fd), end='')
print(next(fd))
fd.close()
运行结果,
$ python3 demo1.py
wei
fang
wei
fang
迭代协议还有一个需要注意的是,for循环开始时,会通过它传给iter内置函数,以便从可迭代对象中获得一个迭代器,返回的对象含有next方法,下面代码说明这点,
l = [1, 2, 3]
L = iter(l)
print(next(L))
print(next(L))
print(next(L))
运行结果,
$ python3 demo1.py
1
2
3
如果去掉L = iter(l),则运行结果如下,
$ python3 demo1.py
Traceback (most recent call last):
File "demo1.py", line 72, in <module>
print(next(l))
TypeError: 'list' object is not an iterator
因为列表不是自身的迭代器,对与这样的对象,必须调用iter来启动迭代。
4.3、其他迭代器
上面说过,在对象中从左到右扫描的每一种工具都使用了迭代协议。所以,现在我们来讲其他的迭代器。
- in:
[print(line, end='') for line in open('wf.txt', 'r')]
print()
运行结果,
$ python3 demo1.py
wei
fang
- map
print(list(map(str, open('wf.txt', 'r'))))
print('fang' in open('wf.txt', 'r'))
print('wei\n' in open('wf.txt', 'r'))
运行结果,
$ python3 demo1.py
['wei\n', 'fang']
True
True
- zip
print(list(zip(open('wf.txt', 'r'), open('wf.txt', 'r'))))
运行结果,
$ python3 demo1.py
[('wei\n', 'wei\n'), ('fang', 'fang')]
还有一些迭代器就不一一列举了,没耐心了,用到再说。
5、函数
使用def关键字可以创建函数,格式如下,
def <fun_name>(agr1,arg2...argn):
...
return <value>
其中,参数个数可以为0,return是可选的。
5.1、函数的定义和调用
下面定义一个名为add的函数,这个函数返回两个参数的和,然后再调用这个函数,代码如下,
def add(x, y):
return x + y
print(add(6, 3))
运行结果,
$ python demo1.py
9
如果将参数传入浮点型,看看结果怎样,
print(add(1.43, 3))
运行结果,也是两数之和,
$ python demo1.py
4.43
如果传入字符串呢?
print(add('wei', 'fang'))
运行结果,
$ python demo1.py
weifang
这次,函数的返回值是两个字符串的连接。可以看出,函数add的作用取决与传递给它的值,这种依赖类型的行为称为多态。这是python这门语言的简易性和灵活性的一个表现。
5.2、作用域
当我们在一个程序中使用变量名时,python创建、改变或查找变量名都是在所谓的命名空间中进行的。也就是说,在代码中,变量名被赋值的位置决定了这个变量名能被访问到的范围。
函数为程序增加了一个额外的命名空间层,默认情况下,一个函数的所有变量名都是与函数的命名空间相关联的。
还是用代码说明吧,
#encoding:utf-8
x = 88
def func():
x = 99
print('我在函数里面:%d' % x)
func()
print('我在函数外面:%d' % x)
运行结果,
$ python demo1.py
我在函数里面:99
我在函数外面:88
如果我们想在上面代码中,在func函数内修改函数外的x的值,怎么办呢?这就需要用到global关键字了。global关键字是对命名空间的生命,它能将变量名的作用域变成整个模块内部作用域。上代码,
#encoding:utf-8
x = 88
def func():
global x
x = 99
print('我在函数里面:%d' % x)
func()
print('我在函数外面:%d' % x)
运行结果,
$ python demo1.py
我在函数里面:99
我在函数外面:99
6、模块
python中,每一个文件都是一个模块,并且,模块导入其他模块后就可以使用导入模块定义的变量名、函数等。模块可以由两个语句和一个重要的内置函数处理。如下,
import
以一个整体获取一个模块
from
从一个模块文件中获取特定的变量名、函数等
imp.reload
在不中止python程序的情况下,提供一中重载模块文件代码的方法
6.1、导入
在文件demo1.py中写入代码,
#encoding:utf-8
def func():
print('我是demo1的')
然后,在同一目录下,新建文件demo2.py,代码如下,
#encoding:utf-8
import demo1
def func():
print('我是demo2的')
demo1.func()
然后运行,
$ python demo2.py
我是demo1的
这样就可以在demo2.py中调用demo1.py中定义的函数了。
如果想用from导入模块,代码如下,
#encoding:utf-8
from demo1 import func as wffunc
def func():
print('我是demo2的')
wffunc()
运行结果,
$ python demo2.py
我是demo1的
因为我们要导入的demo1.py的函数名和demo2.py定义的函数名重复了,所以我们在导入的时候在用as将导入的demo1的func函数名重命名,如果不这么做,就会优先调用demo2的func,代码如下,
#encoding:utf-8
from demo1 import func
def func():
print('我是demo2的')
func()
运行结果,
$ python demo2.py
我是demo2的
那reload又有什么用呢?
模块程序代码默认只对每个过程执行一次,如果要强制是模块代码重新载入并重新运行,就得调用reload内置函数了。reload函数与import和from不同,reload是python内置函数,传给reload的是已经存在的模块对象,而不是变量名。reload在python3.0中位于模块之中,并且必须导入自己。
那么,我们为什么要去重载模块呢?reload函数可以修改程序的一部分,而无需停止整个程序。还是以代码说明吧,
在demo1.py中,写下如下代码,
# #encoding:utf-8
message = '韦访'
def func():
print(message)
然后,打开命令窗口,输入python,回车,再输入下面代码,并得到运行结果,
$ python
Python 2.7.12 (default, Nov 19 2016, 06:48:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import demo1
>>> demo1.func()
韦访
>>>
不要关闭解释器,修改demo1.py中的代码,
# #encoding:utf-8
message = 'weifang'
def func():
print(message)
回到python窗口,再运行demo1.func(),
>>> demo1.func()
韦访
>>>
我们发现,现在打印的结果并不是我们最新改的“weifang”,现在,输入reload(demo1),然后再运行demo1.func()看看,
>>> reload(demo1)
<module 'demo1' from 'demo1.py'>
>>> demo1.func()
weifang
>>>
这次才是我们想要的结果。这就是模块重载的作用。
6.2、模块包
除了模块名以外,导入也可以指定目录路径,这个目录就称为包,这类导入就称为包导入,包导入就是把计算机上的目录变成另一个python命名空间,而属性则对应于目录中所包含的子目录和模块文件。
还是用例子说明吧,新建文件夹,
$ mkdir -p dir1/dir2
然后在dir2文件夹里,创建文件mod.py,代码如下,
x = 'weifang'
def func():
print(x)
这样就可以了吗?我们试试,再新建文件demo1.py,跟dir1文件夹同目录,我们可以在demo1.py里导入mod模块了,代码如下,
import dir1.dir2.mod
dir1.dir2.mod.func()
运行看看,
$ python demo1.py
Traceback (most recent call last):
File "demo1.py", line 105, in <module>
import dir1.dir2.mod
ImportError: No module named dir1.dir2.mod
显然是不行的,python中如果选择使用包导入,就必须遵循一条约束:包导入语句的路径中的每个目录内都必须有__init__.py文件,否则导入包会失败,这个文件里面可以是空的,但是必须存在。
接着用命令行创建这个两个文件吧(当然你也可以手动创建,linux中使用命令更快捷而已)
$ touch dir1/__init__.py
$ touch dir1/dir2/__init__.py
然后再运行看看,
$ python demo1.py
weifang
ok,就这么简单,可是上面的代码中,每次使用mod模块,都得写完整的包,很麻烦,我们可以使用import/as关键字将其简化,代码如下,
import dir1.dir2.mod as mod
mod.func()
当然还可以用from来导入,我们只导入变量x,代码如下,
from dir1.dir2.mod import x
print(x)
运行结果,
$ python demo1.py
weifang
我们上面的例子中,__init__.py里面是空的,什么都没有,那么,这个__init__.py还有什么用呢?
首先,根据后缀,至少知道它也是个python文件,我们在里面写个打印代码试试,
#dir1/__init__.py 内容如下,
print('dir1 init')
#dir1/dir2/__init__.py 内容如下,
print('dir2 init')
然后,再运行上面的demo1.py,结果如下,
$ python demo1.py
dir1 init
dir2 init
weifang
我们发现,这个__init__.py文件是可以写python代码的,而且是优先执行上层目录的__init__.py,__init__.py都执行完了以后,才执行我们要运行的模块。
但是一般不建议在__init__.py里写代码,要尽量保证__init__.py足够轻。
除了这个作用,__init__.py还有一个作用就是定义包中的__all__,用于模糊导入。还是以例子说明,
我们在dir2目录下,新建文件mod1.py、mod2.py、mod3.py,内容分别如下,
#mod1.py
x = 'weifang mod1'
def func():
print(x)
#mod2.py
x = 'weifang mod2'
def func():
print(x)
#mod3.py
x = 'weifang mod3'
def func():
print(x)
dir2/__init__.py内容如下,
__all__ = ['mod', 'mod1', 'mod2']
然后,demo1.py内容如下,
from dir1.dir2 import *
mod.func()
mod1.func()
mod2.func()
mod3.func()
然后,运行demo1.py咯,运行结果如下,
$ python demo1.py
weifang mod
weifang mod1
weifang mod2
Traceback (most recent call last):
File "demo1.py", line 116, in <module>
mod3.func()
NameError: name 'mod3' is not defined
哎,mod3模块为定义?因为我们未将其加入__init__.py的__all__列表中。
6.3、__name__和__main__
每个模块都有一个名为__name__的内置属性,python会自动设置该属性:
l 如果文件是以顶层程序文件执行,在启动时,__name__会设置为字符串”__main__”。
l 如果文件被导入,__name__会变成客户端所了解的模块名。
还是以代码说明吧,修改上面的代码,
#mod1.py
x = 'weifang mod1'
def func():
print(x)
print('aaaaa1:' + __name__)
#mod2.py
x = 'weifang mod2'
def func():
print(x)
print('aaaaa2:' + __name__)
#mod3.py
x = 'weifang mod3'
def func():
print(x)
print('aaaaa3:' + __name__)
#demo1.py
from dir1.dir2 import *
print('demo1:' + __name__)
运行结果为,
$ python demo1.py
aaaaa1:dir1.dir2.mod1
aaaaa2:dir1.dir2.mod2
aaaaa3:dir1.dir2.mod3
demo1:__main__
这样模块就可以根据检测自己的__name__来确定它是在执行还是被导入。
上面代码中,我们只是通过from导入了dir2下的所有包,然后每个模块都执行了自己的print打印自己的__name__,那么,我们现在可以根据__name__这个属性,让模块在只有是在执行状态时才打印,为方便,只改mod1模块,代码如下,
#mod1.py
x = 'weifang mod1'
def func():
print(x)
if __name__ == '__main__':
print('aaaaa1:' + __name__)
运行结果,
$ python demo1.py
aaaaa2:dir1.dir2.mod2
aaaaa3:dir1.dir2.mod3
demo1:__main__
可以看到,mod1的不打印了。直接运行mod1看看,
$ python dir1/dir2/mod1.py
aaaaa1:__main__
这时mod1的__name__就变成了”__main__”。这样的一个好处就是方便进行单元测试。
6.4、__name__命令行参数
python中,sys.argv列表包含了命令行参数,下面就结合__name__使用。上例子,还是demo1.py文件,代码如下,
import sys
if __name__ == '__main__':
print(sys.argv)
print(len(sys.argv))
运行结果,
$ python demo1.py 1 2 3
['demo1.py', '1', '2', '3']
4
7、类
7.1、类的创建和调用
虽然《python入门笔记(一)》中已经提过类,这里我们再深入的讲一下类。类的优点就不说了。还是举例说明,定义一个类DemoClass
class DemoClass:
def setData(self, value):
self.value = value
def show(self):
print(self.value)
python不用像其他语言一样先得定义属性才能用,python的所有属性都含在self里,准确的说,应该是函数的第一个参数。用的时候直接用,这样就很灵活,但很难说哪种方式更好。python类中的函数,至少得有一个参数,我们可以来测试一下,在DemoClass类中,创建一个add函数,不带任何参数,代码如下,
class DemoClass:
def setData(self, value):
self.value = value
def show(self):
print(self.value)
def add():
print('add')
d1 = DemoClass()
d1.add()
运行结果,
$ python demo1.py
Traceback (most recent call last):
File "demo1.py", line 130, in <module>
d1.add()
TypeError: add() takes no arguments (1 given)
而且,第一个参数,可以任意命名,我们将其命名为x,效果也是一样的。代码如下,
class DemoClass:
def setData(self, value):
self.value = value
def show(self):
print(self.value)
def add(x, y, z):
print(x.value)
print('%d + %d = %d' % (y, z, (y + z)))
d1 = DemoClass()
d1.setData('weifang')
d1.add(1, 2)
运行结果,
$ python demo1.py
weifang
1 + 2 = 3
调用的时候,第一个参数是不需要传入的。
类还有构造函数__init__,还有析构函数__del__。构造函数是实例化类的时候会执行,只会执行一次,一般用作初始化。析构函数是当对象在某个作用域调用完毕,在跳出其作用域时会被调用一次,可以用来释放内存空间等操作。还是以例子说明,
# #encoding:utf-8
class DemoClass:
def __init__(self, value):
print('我是构造函数!')
self.value = value
def setData(self, value):
self.value = value
def show(self):
print(self.value)
def __del__(self):
print('我是析构函数!')
d1 = DemoClass('weifang')
d1.show()
运行结果,
$ python demo1.py
我是构造函数!
weifang
我是析构函数!
7.2、类的继承
类是可以被继承的,还是直接上代码吧,
# #encoding:utf-8
class DemoClass:
def __init__(self, value):
print('我是你爹的构造函数!')
self.value = value
def setData(self, value):
self.value = value
def show(self):
print(self.value)
def __del__(self):
print('我是你爹的析构函数!')
class FatherClass(DemoClass):
def __init__(self, value):
self.value = value
print('我是构造函数!')
def __del__(self):
print('我是析构函数!')
d1 = FatherClass('weifang')
d1.show()
运行结果,
$ python demo1.py
我是构造函数!
weifang
我是析构函数!
可以看到,我们虽然没有为类FatherClass定义show函数,但是,它从DemoClass类继承了这个函数,所以,它也就拥有了show函数。如果子类也定义了构造函数或析构函数,则父类的构造函数和析构函数就不会执行,可以将子类的构造函数去掉看看。运行结果如下,
$ python demo1.py
我是你爹的构造函数!
weifang
我是析构函数!
可以看到,如果子类不定义构造或析构函数,就会执行父类的,当然,前提是,父类定义了。
7.3、
这个内容我不知道用什么标题才好,直接举例子吧,python还可以建一个“空”类,如,
class wf:pass
这个类有什么用呢?还是先上代码吧,
class wf:pass
wf.name = 'weifang'
wf.age = 26
print(wf.name)
print(wf.age)
运行结果,
$ python demo1.py
weifang
26
我们可以通过直接赋值变量名给这个类增加属性,就可以将它们取出来直接使用了,作用有点像c语言的struct。如果将这个类实例化呢?
class wf:pass
wf.name = 'weifang'
wf.age = 26
print(wf.name)
print(wf.age)
x = wf()
print(x.name)
y = wf()
print(y.age)
运行结果,
$ python demo1.py
weifang
26
weifang
26
发现没有,上面例子中,不管实例化几个对象,他们的“name”和”age”都是一样的。
想看类中有多少个这样的变量,可以使用下面代码,
print(wf.__dict__.keys())
运行结果,
$ python demo1.py
['age', '__module__', '__doc__', 'name']
总结:
python还有很多知识点,比如异常、网络等等,这里不想写了。写这个只是为了过一次python的最基本的语法和用法,我们的目的还是学习tensorflow,所以不要花费太多时间。
如果您感觉本篇博客对您有帮助,请打开支付宝,领个红包支持一下,祝您扫到99元,谢谢~~