Python快速而美丽[v1.0.0][迭代器生成器装饰器]

迭代器

迭代器是一个可以记住遍历的位置的对象,它从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

可迭代的对象

可以使用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='[email protected]', *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...")

猜你喜欢

转载自blog.csdn.net/dawei_yang000000/article/details/135217333