迭代器是一个可以记住遍历的位置的对象,它从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
可以使用For循环的数据类型,其中一种是集合数据类型,如list、tuple、dict、set、str等;另一种则是生成器,可以直接作用于for循环的对象统称为可迭代的对象(Iterable),判断是否是可迭代对象:
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
返回True则表明该对象是可迭代的。
与普通的集合数据类型不同,生成器不但可以作用于for循环,还可以被__next__()
函数不断调用并返回下一个值,直到最后抛出StopIteration错误为止,那么,可以被__next__()
函数调用并不断返回下一个值的对象称为迭代器(Iterator),判断是否是迭代器对象:
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
生成器都是Iterator对象,但list、dict、str虽然是可迭代对象,但不是迭代器,把list、dict、str等可迭代对象变成迭代器可以使用iter()
函数
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()
与 __next__()
,__iter__()
方法返回一个特殊的迭代器对象, 这个迭代器对象实现了__next__()
方法并通过 StopIteration 异常标识迭代的完成__next__()
方法(Python 2 里是 next())会返回下一个迭代器对象。
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> G = (x * x for x in range(10))
>>> G
<generator object <genexpr> at 0x000000D25E141B88>
>>> G.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'generator' object has no attribute 'next'
>>> G.__next__()
0
>>> G.__next__()
1
>>> G.__next__()
4
>>> G.__next__()
9
>>> G.__next__()
16
>>> G.__next__()
25
>>> G.__next__()
36
>>> G.__next__()
49
>>> G.__next__()
64
>>> G.__next__()
81
>>> G.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
创建Generator,有很多种方法,第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个Generator
Generator保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误
在代码中我们调用next()函数报错,因为在Python3之后next()函数改为了__next__()
这样使用__next__()函数来让生成器生成数据的方式太麻烦了,虽然生成器不是列表,但我们仍然可以使用for循环
>>> G = (x * x for x in range(10))
>>> for e in G:
... print(e)
...
0
1
4
9
16
25
36
49
64
81
有序的数列我们可以使用列表生成器来生成每一个元素,然后用for循环来迭代它,如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现
>>> def fibonacci(end):
... a, b, c = 0, 0, 1
... while a < end:
... print(c)
... b, c = c, b+c
... a = a+1
...
>>> fibonacci(10)
1
1
2
3
5
8
13
21
34
55
>>>
定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。
fibonacci函数定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,然而它还不是个生成器,距离生成器仅一步之遥,只需要将只需要把print?改为yield?即可
generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行
>>> def fibonacci(end):
... a, b, c = 0,0,1
... while a < end:
... yield(c)
... b,c = c, b+c
... a=a+1
...
>>> fibonacci(10)
<generator object fibonacci at 0x000000D25E2792A0>
>>> num = fibonacci(10)
>>> num.__next__()
1
>>> num.__next__()
1
>>> num.__next__()
2
>>> num.__next__()
3
>>> num.__next__()
5
>>> for i in fibonacci(10):
... print(i)
...
1
1
2
3
5
8
13
21
34
55
>>> def print_name(name='davieyang'):
... return name
...
>>> print(print_name)
<function print_name at 0x0000000CB357C268>
>>> print(print_name())
davieyang
>>> print(print_name.__name__)
print_name
>>>
>>> print_func = print_name
>>> print_func_f = print_name()
>>> print(print_func)
<function print_name at 0x0000000CB357C268>
>>> print(print_func_f)
davieyang
>>> print(print_func())
davieyang
>>> del print_name
>>> print(print_name)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'print_name' is not defined
>>> print(print_name())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'print_name' is not defined
>>> print(print_func)
<function print_name at 0x0000000CB357C268>
>>> print(print_func())
davieyang
>>> print(print_func_f())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
>>> print(print_func_f)
davieyang
>>> def print_name(name='davieyang'):
... print("currently, you are inside the print_name() function")
... def say_hello():
... return "currently, you are inside the say_hello() function"
... def say_goodbye():
... return "currently, you are inside the say_goodbye() function"
... print(say_hello())
... print(say_goodbay())
... print("currently, you are back in the print_name() function")
...
>>> print_name()
currently, you are inside the print_name() function
currently, you are inside the say_hello() function
currently, you are inside the say_goodbye() function
currently, you are back in the print_name() function
>>> say_hello()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'say_hello' is not defined
>>>
>>> def print_name(name='davieyang'):
... print("currently, you are inside the print_name() function")
... def say_hello():
... return "currently, you are inside the say_hello() function"
... def say_goodbye():
... return "currently, you are inside the say_goodbye() function"
... if name == "davieyang":
... return say_hello
... else:
... return say_goodbye
... print(say_hello())
... print(say_goodbay())
... print("currently, you are back in the print_name() function")
...
>>> a = print_name()
>>> print(a)
<function say_hello at 0x7f2143c01500>
这说明已经指向了say_hello函数
>>> def print_name(name='davieyang'):
... print("currently, you are inside the print_name() function")
...
>>> def do_something_before_print_name(func):
... print("for doing something before executing print_name()")
... print(func())
...
>>> do_something_before_print_name(print_name)
for doing something before executing print_name()
currently, you are inside the print_name() function
本质上装饰器就是一个返回函数的高阶函数,常常将装饰器用于扩展或者增强函数
def a_new_decorator(a_func):
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")
a_func()
print("I am doing some boring work after executing a_func()")
return wrapTheFunction
def a_function_requiring_decoration():
print("I am the function which needs some decoration to remove my foul smell")
a_function_requiring_decoration()
#outputs: "I am the function which needs some decoration to remove my foul smell"
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()
a_function_requiring_decoration()
#outputs:I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()
@a_new_decorator
def a_function_requiring_decoration():
"""Hey you! Decorate me!"""
print("I am the function which needs some decoration to "
"remove my foul smell")
a_function_requiring_decoration()
#outputs: I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()
@a_new_decorator代替了a_function_requiring_decoration=a_new_decorator(a_function_requiring_decoration)
然而这里存在一个问题,加了装饰器后的函数a_function_requiring_decoration的__name__属性被装饰器修改了,这并不是我们期望的
>>> print(a_function_requiring_decoration.__name__)
wrapTheFunction
Python提供了一个简单的方法functools.wraps
解决装饰器修改属性的问题
from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")
a_func()
print("I am doing some boring work after executing a_func()")
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
"""Hey yo! Decorate me!"""
print("I am the function which needs some decoration to "
"remove my foul smell")
>>> print(a_function_requiring_decoration.__name__)
a_function_requiring_decoration
class logit:
_logfile = 'out.log'
def __init__(self, func):
self.func = func
def __call__(self, *args):
log_string = self.func.__name__ + " was called"
print(log_string)
# Open the logfile and append
with open(self._logfile, 'a') as opened_file:
# Now we log to the specified logfile
opened_file.write(log_string + '\n')
# Now, send a notification
self.notify()
# return base func
return self.func(*args)
def notify(self):
# logit only logs, no more
pass
使用该类级装饰器
logit._logfile = 'out2.log'
@logit
def myfunc():
pass
myfunc()
# Output: myfunc1 was called
class email_logit(logit):
'''
Let’s subclass logit to add email functionality, from here, @email_logit works just like @logit but sends an email to the admin in addition to logging.
A logit implementation for sending emails to admins
when the function is called.
'''
def __init__(self, email='admin@myproject.com', *args, **kwargs):
self.email = email
super(email_logit, self).__init__(*args, **kwargs)
def notify(self):
# Send an email to self.email
# Will not be implemented here
pass
import logging
from selenium import webdriver
from time import sleep
class DecoratorDemo:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
driver = webdriver.Chrome()
driver.get(url="http://www.baidu.com")
logging.warning("%s is running" % self.func.__name__)
self.notify()
return self.func(*args, **kwargs)
def notify(self):
print(self.notify.__name__ + " " + "was called....")
@DecoratorDemo
def test_access_baidu():
print("Baidu website is opened...")
sleep(5)
if __name__ == '__main__':
test_access_baidu()
# -*- coding: utf-8 -*-
import logging
import unittest
from functools import wraps
from selenium import webdriver
"""
被装饰的函数有可能是不带参数的,有可能是带参数的,装饰器应如何编写
"""
def starting_browser_none_parameter(func):
"""
starting_browser_none_parameter是一个装饰器,返回函数wrap_starting_browser
函数的进入和退出时,被称为一个横切面,这种编程方式被称为面向切面编程
"""
@wraps(func)
def wrap_starting_browser():
driver = webdriver.Chrome()
driver.get(url="http://www.baidu.com")
logging.warning("%s is running" % func.__name__)
return func()
return wrap_starting_browser
def starting_browser_with_simple_parameter(func):
"""
starting_browser是一个装饰器,返回一个函数
"""
@wraps(func)
def wrap_starting_browser(self):
"""
函数的参数定义是(*args, **kwargs),因此,wrap_starting_browser()函数可以接受任意参数的调用。
"""
driver = webdriver.Chrome()
driver.get(url="http://www.baidu.com")
logging.warning("%s is running" % func.__name__)
return func(self)
return wrap_starting_browser
def starting_browser_with_sophisticated_parameter(func):
@wraps(func)
def wrap_starting_browser(*args, **kwargs):
"""
函数的参数定义是(*args, **kwargs),因此,wrap_starting_browser()函数可以接受任意参数的调用。
*args是一个数组,**kwargs是一个字典
"""
driver = webdriver.Chrome()
driver.get(url="http://www.baidu.com")
logging.warning("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrap_starting_browser
class TestDecorator(unittest.TestCase):
@starting_browser_with_simple_parameter
def test_access_baidu(self):
print("Baidu website is opened...")
# -*- coding: utf-8 -*-
import logging
import unittest
from functools import wraps
from selenium import webdriver
"""
装饰器还有更大的灵活性,例如带参数的装饰器,在上面的装饰器调用中,该装饰器接收唯一的参数就是执行业务的函数
装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(parameter)。这样,就为装饰器的编写和使用提供了更大的灵活性
比如,我们可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的
它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包
当我 们使用@starting_browser_with_parameter(browser_name='firefox')调用的时候
Python 能够发现这一层的封装,并把参数传递到装饰器的环境中
"""
def starting_browser_with_parameter(browser_name):
def decorator_with_parameter(func):
@wraps(func)
def wrap_starting_browser(*args, **kwargs):
if browser_name.lower() == 'chrome':
driver = webdriver.Chrome()
driver.get(url="http://www.baidu.com")
logging.warning("%s is running" % func.__name__)
elif browser_name.lower() == 'firefox':
driver = webdriver.Firefox()
driver.get(url="http://www.baidu.com")
logging.warning("%s is running" % func.__name__)
else:
driver = webdriver.Ie()
driver.get(url="http://www.baidu.com")
logging.warning("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrap_starting_browser
return decorator_with_parameter
class TestDecorator(unittest.TestCase):
@starting_browser_with_parameter(browser_name='firefox')
def test_access_baidu(self):
print("Baidu website is opened...")