イテレーター
イテレータはトラバーサルの位置を記憶できるオブジェクトで、コレクションの最初の要素からすべての要素にアクセスされるまでアクセスを開始します。イテレータは前方にのみ進むことができ、後方には進むことができません。
反復可能なオブジェクト
For ループのデータ型を使用できます。そのうちの 1 つは、list、tuple、dict、set、str などのコレクション データ型であり、もう 1 つはジェネレーターと、そのデータ型に直接作用できるオブジェクトです。 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 は反復可能オブジェクトですが、反復子ではありません。関数を使用して、iter()
list 、dict、str などの反復可能オブジェクトを反復子に変えることができます。
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
イテレータクラス
クラスをイテレータとして使用するには、クラスに 2 つのメソッドを実装する必要があります__iter__()
。__next__()
この__iter__()
メソッドは特別なイテレータ オブジェクトを返します。このイテレータ オブジェクトは__next__()
メソッドを実装し、__next__()
StopIteration 例外 (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 を作成する方法はいくつかありますが、1 つ目は非常に簡単で、リスト生成式の [] を () に変更するだけで Generator を作成できます。
ジェネレーターはアルゴリズムを保存します。next() が呼び出されるたびに、最後の要素が計算されるまで次の要素の値が計算されます。要素がなくなると、StopIteration エラーがスローされます。
コードでは、next() 関数を呼び出してエラーを報告します。これは、next() 関数が Python3 以降 __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
>>>
解析する
ジェネレーターを定義する別の方法。関数定義に yield キーワードが含まれている場合、その関数は通常の関数ではなく、ジェネレーターになります。
フィボナッチ関数は、フィボナッチ数列の計算ルールを定義します。最初の要素から開始して、後続の要素を計算できます。ただし、これはジェネレーターではありません。ジェネレーターからは 1 ステップだけ離れています。必要なのは、Change print だけです。 ©からyield©と
ジェネレーターと関数の実行フローが異なります。関数は順番に実行され、return ステートメントまたは関数ステートメントの最後の行に到達すると戻ります。ジェネレーターとなる関数は 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
functools.wraps
Python は、属性を変更するデコレータの問題を解決する簡単な方法を提供します
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...")