python入门笔记(二)

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.433))

运行结果,也是两数之和,

$ 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(12)

运行结果,

 $ 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元,谢谢~~

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值