## 迭代器
迭代器是一个可以记住遍历的位置的对象,它从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
### 可迭代的对象
可以使用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__()方法(Python2里是 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 StopIterationmyclass = 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)...0149162536496481
### 生成器进阶
有序的数列我们可以使用列表生成器来生成每一个元素,然后用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)11235813213455>>>
#### 解析
定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。
fibonacci函数定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,然而它还不是个生成器,距离生成器仅一步之遥,只需要将只需要把print(c)改为yield(c)即可
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)...11235813213455
## 装饰器
### 定义一个函数
>>> 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() functioncurrently, you are inside the say_hello() functioncurrently, you are inside the say_goodbye() functioncurrently, 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 wrapTheFunctiondef 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_decoratordef 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 wrapsdef 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_decoratordef 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' @logitdef myfunc(): passmyfunc()# 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 loggingfrom selenium import webdriverfrom time import sleepclass 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....")@DecoratorDemodef test_access_baidu(): print("Baidu website is opened...") sleep(5)if __name__ == '__main__': test_access_baidu()
# -*- coding: utf-8 -*-import loggingimport unittestfrom functools import wrapsfrom 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_browserdef 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_browserdef 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_browserclass TestDecorator(unittest.TestCase): @starting_browser_with_simple_parameter def test_access_baidu(self): print("Baidu website is opened...")
# -*- coding: utf-8 -*-import loggingimport unittestfrom functools import wrapsfrom 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_parameterclass TestDecorator(unittest.TestCase): @starting_browser_with_parameter(browser_name='firefox') def test_access_baidu(self): print("Baidu website is opened...")