云学python (第4章朝思暮想是循环对象、函数对象、模块对象、异常对象、附录)《vamei-从Python开始学编程》 笔记

4.4意想不到的对象

 

1.循环对象

Python中的许多语法结构都是由对象实现的,循环就可以通过对象实现。循环对象并不是在Python诞生之初就存在的,但它的发展极为迅速,特别是在Python3时代,循环对象正在成为循环的标准形式。

所谓的循环对象包含有一个__next__()方法。这个方法的目的是生成循环的下一个结果。在生成过循环的所有结果之后,该方法将抛出Stoplteration异常。

当一个像for这样的循环语法调用循环对象时,它会在每次循环的时候调用__next__()方法,直到Stoplteration出现。循环接收到这个异常,就会知道循环已经结束,将停止调用__next__()

现用内置函数iter()iterant把一个列表转变为循环对象。这个循环对象将拥有__next__()方法法。多次调用__next__()方法,将不断返回列表的值, 直到出现异常:

>>> example_iter = iter([1,2])
>>> example_iter.__next__()
1
>>> example_iter.__next__()
2
>>> example_iter.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> 

上面重复调用__next__()的过程,就相当于手动进行了循环。

把循环对象包裹在for中循环:

for item in iter([1,2]):
    print(item)

——————————————————————
1
2

在这里,for结构自动调用__next__(),将该方法的返回值赋予给item。可以省去内置函数iter的转换是因为for结构会自动执行这一转换。

相对于序列,循环对象的好处在于:不用在循环还没开始的时候,就生成要使用的元素。所有要使用的元素可以在循环过程中逐渐生成。这样,不仅节省了空间,提高了效率,还会使编程更加灵活。

我们可以借助生成器(generator)来自定义循环对象。生成器的编写方法和函数定义类似,只是在return的地方改为yield生成器中可以有多个yield生成器遇到一个yield时,会暂停运行生成器,返回yield 后面的值。当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个yield。生成器自身又构成一个循环对象,每次循环使用一个yield返回的值。

下面是一个生成器:

def gen():
    a = 100 
    yield a
    a = a*8
    yield a
    yield 1000

for i in gen():
    print(i)
_______________
100
800
1000

再考虑下面一个生成器:

def gen():
    i = 0
    while i < 10000:
        i = i + 1
        yield i

这个生成器能产生10000个元素。如果先创建序列保存这10000个元素,再循环遍历,那么这个序列将占用大量的空间。出于同样的原因,Python中的内置函数range()返回的是一个循环对象,而不是一个序列。

2.函数对象

Python中函数也是一种对象。实际上,任何一个有 __call__()特殊方法的对象都被当作是函数。比如下面的例子:

class SampleMore(object):
    def __call__(self,a):
        return a + 5
add_five = SampleMore()
print(add_five(2))
_________________________
7

add_fiveSampleMore类的一个对象,当被调用时,add_five执行加5的操作。

 3.模块对象

前面说过,Python中的模块对应一个.py文件。模块也是对象。比如标准库中的模块time

import time
print(dir(time))
________________
['CLOCK_MONOTONIC', 'CLOCK_MONOTONIC_RAW', 'CLOCK_PROCESS_CPUTIME_ID', 'CLOCK_REALTIME', 'CLOCK_THREAD_CPUTIME_ID', 'CLOCK_UPTIME_RAW', '_STRUCT_TM_ITEMS', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'altzone', 'asctime', 'clock_getres', 'clock_gettime', 'clock_gettime_ns', 'clock_settime', 'clock_settime_ns', 'ctime', 'daylight', 'get_clock_info', 'gmtime', 'localtime', 'mktime', 'monotonic', 'monotonic_ns', 'perf_counter', 'perf_counter_ns', 'process_time', 'process_time_ns', 'sleep', 'strftime', 'strptime', 'struct_time', 'thread_time', 'thread_time_ns', 'time', 'time_ns', 'timezone', 'tzname', 'tzset']

可以看到,time有很多属性可以调用,例如sleep方法。我们之前用import语句引入其他文件中定义的函数,实际上就是引入模块对象的属性,比如:

from time import sleep

sleep(10)

print("wake up")

________________
10秒后出现wake up

还可以用简单暴力的方法,一次性引入模块的所有属性:

from time import *

sleep(10)

既然知道了 sleep()time的一个方法,也可以利用对象属性的方式来调用它。

import time

time.sleep(10)

调用方法时附带上对象名的好处是可以拓展程序的命名空间,避免同名冲突。例如,如果两个模块中都有sleep()方法可以通过不一样的模块名来区分开来:

my_time.py中写入函数:

def sleep(self):

print("I am sleeping.")

main.py中引入内置模块time和自定义模块my time

import time

import my_time

time.sleep()

my time.sleep()

上面的两次对sleep()方法的调用中,我们通过对象名区分出了不同sleep()。

在引入模块时,我们还可以给模块换个名字:

import time as t

t.sleep(10)

在引入名字较长的模块时,这个换名字的办法能有效地挽救程序员的手指。

可以将功能相似的模块放在同一个文件夹中,构成一个模块包。比如放在this_dir中:

import this_dir.module

引入this dir文件夹中的module模块。

该文件夹中必须包含一个__init__.py的文件,提醒Python,该文件为一个模块包。__init__.py可以是一个空文件。

每个模块对象都有一个__name__属性,用来记录模块的名字,例如:

import time

print(time.__name__)

当一个.py文件作为主程序运行时,比如python foo.py这个文件也会有一个对应的模块对象。但这个模块对象的  _name__属性会是 "__main__"因此在很多.py文件中可以看到下面的语句:

if__name__ == "__main__":

它的意思是说,如果这个文件作为一个主程序运行,那么将执行下面的操作。有的时候,一个.py文件中同时有类和对象的定义以及对它们的调用。通过把调用语句放到上面的if中,就可以在调用时不执行这些调用语句了。

 4.异常对象

程序中加入异常处理的try结构可以捕捉程序中出现的异常。实际上,我们捕捉到的也是一个对象,比如:

try:
    m = 1/0
except ZeroDivisionError as e:
    print("Catch NameError in the sub-function")
    print(type(e))	
    print(dir(e))	#异常对象的属性
    print(e.args)
__________________________________
Catch NameError in the sub-function
<class 'ZeroDivisionError'>
['__cause__', '__class__', '__context__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__suppress_context__', '__traceback__', 'args', 'with_traceback']
('division by zero',)
注意⚠️python3要把书里的print(e.message)改成print(e.args)

利用except... as...的语法,在except结果中用e来代表捕获到的类型对象。关键字except直接跟随的ZeroDivisionError实际上是异常对象的类。正因为如此在举出异常时会创建一个异常对象:

raise ZeroDivisionError()

Python中,循环、函数、模块、异常都是某种对象。当然,我们 可以完全按照面向过程中的方式来调用这些语法,而不必关注它们底层的对象模型。

附录A代码规范

类的命名采用首字母大写的英文单词。如果由多个单词连接而成,则每个单词的首字母都大写。单词之间不出现下画线

对象名、属性名和方法名,全部用小写字母。单词之间用下画线连接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值