あなたはyieldキーワードを使用することを学ぶ必要があり、そう長くはPythonを書きました

時間と空間利用効率プログラムはしばしば矛盾し、時間のスペースのために使用することができ、時間のためのスペースを使用することができますが、同時に、プログラムを向上させることが困難である:学生はいくつかの時間のためのコードを書いて、完全にこの文を理解すべきです時間の利用と空間利用。

しかし、あなたはあなたのコードをリファクタリングするために発電機を使用しようとすると、多分あなたは、ある程度を見つけるでしょう、あなたは時間効率を改善し、スペースの利用率を向上させることができないだけ。

私たちはあなたのコードジェネレータアップとより効率的に実行しているを作る方法を説明するための例として、単純なデータクレンジングを投影します。

Redisのでは、リストがあるdatalistかもしれデータの多くは、あります、纯阿拉伯数字中文数字字符串"敏感信息"今、私たちが達成する必要があります:すべての文字列は、Redisのからのすべてのデータを読み取るために、敏感信息それをすべて失って、すべての中国は、すべてのデジタルアラビア数字に変換し{'num': 12345, 'date': '2019-10-30 18:12:14'}、このような形式でのMongoDBに挿入されます。

サンプルデータ次のように:

41234213424
一九八八七二六三
8394520342
七二三六二九六六
敏感信息
80913408120934
敏感信息
敏感信息
95352345345
三三七四六
999993232
234234234
三六八八七七
敏感信息

下図のように:

あなたは、この変換プロセスを記述した場合、次のように記述があります

import redis
import datetime
import pymongo


client = redis.Redis()
handler = pymongo.MongoClient().data_list.num

CHINESE_NUM_DICT = {
    '一': '1',
    '二': '2',
    '三': '3',
    '四': '4',
    '五': '5',
    '六': '6',
    '七': '7',
    '八': '8',
    '九': '9'
}

def get_data():
    datas = []
    while True:
        data = client.lpop('datalist')
        if not data:
            break
        datas.append(data.decode())
    return datas

def remove_sensitive_data(datas):
    clear_data = []
    for data in datas:
        if data == '敏感信息':
            continue
        clear_data.append(data)
    return clear_data

def tranfer_chinese_num(datas):
    number_list = []
    for data in datas:
        try:
            num = int(data)
        except ValueError:
            num = ''.join(CHINESE_NUM_DICT[x] for x in data)
        number_list.append(num)
    return number_list

def save_data(number_list):
    for number in number_list:
        data = {'num': number, 'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
        handler.insert_one(data)

raw_data = get_data()
safe_data = remove_sensitive_data(raw_data)
number_list = tranfer_chinese_num(safe_data)
save_data(number_list)

以下に示すような結果を実行します。

コードは、Python的、一つだけの機能は、また、コーディング標準を満たすように見えます見えます。最後に、結果も正常に実行されます。何が問題になることができますか?

問題は、このコードは、各機能は、データを処理した後に記憶されたリストを作成することです。Redisのデータは、現在のコンピュータのメモリよりも、あなたに多くのであればどのようにしますか?多くの時間を無駄に同じバッチサイクル時間のためのデータを使用することは、一度だけ再利用することはできませんか?

おそらく、あなたは、あなたが削除することができますことを言うだろう敏感信息、すべてのロジックで書かれた中国のデジタル-アラビア数字get_data関数whileループ、それはまだ唯一のサイクルタイムではないでしょうか?

それはすることができますが、この方法は、get_dataあなたは、複数のことを行う、コードは非常に紛らわしいです。後で新しいデータ処理ロジックを追加したい場合は:

以降、奇数ビット、偶数和数字の全てをチェックするために、デジタルに変換され、合計はすべて破棄さの等しい、等しい数です。

そして、あなたは変更する必要がget_dataコードを。

在开发软件的时候,我们应该面向扩展开放,面向修改封闭,所以不同的逻辑,确实应该分开,所以上面把每个处理逻辑分别写成函数的写法,在软件工程上没有问题。但是如何做到处理逻辑分开,又不需要对同一批数据进行多次 for 循环呢?

这个时候,就要依赖于我们的生成器了。

我们先来看看下面这一段代码的运行效果:

def gen_num():
    nums = []
    for i in range(10):
        print(f'生成数据:{i}')
        nums.append(i)
    return nums
nums = gen_num()
for num in nums:
    print(f'打印数据:{num}')

运行效果如下图所示:

现在,我们对代码做一下修改:

def gen_num():
    for i in range(10):
        print(f'生成数据:{i}')
        yield i
nums = gen_num()
for num in nums:
    print(f'打印数据:{num}')

其运行效果如下图所示:

大家对比上面两张插图。前一张插图,先生成10个数据,然后再打印10个数据。后一张图,生成一个数据,打印一个数据,再生成一个数据,再打印一个数据……

如果以代码的行号来表示运行运行逻辑,那么代码是按照这个流程运行的:

1->5->6->2->3->4->6->7->6->2->3->4->6->7->6->2->3->4->6->7....

大家可以把这段代码写在 PyCharm 中,然后使用单步调试来查看它每一步运行的是哪一行代码。

程序运行到yield就会把它后面的数字抛出到外面给 for 循环, 然后进入外面 for 循环的循环体,外面的 for 循环执行完成后,又会进入gen_num函数里面的 yield i后面的一行,开启下一次 for 循环,继续生成新的数字……

整个过程中,不需要额外创建一个列表来保存中间的数据,从而达到节约内存空间的目的。而整个过程中,虽然代码写了两个 for 循环,但是如果你使用单步调试,你就会发现实际上真正的循环只有for i in range(10)。而外面的for num in nums仅仅是实现了函数内外的切换,并没有新增循环。

回到最开始的问题,我们如何使用生成器来修改代码呢?实际上你几乎只需要把return 列表改成yield 每一个元素即可:

import redis
import datetime
import pymongo


client = redis.Redis()
handler = pymongo.MongoClient().data_list.num_yield

CHINESE_NUM_DICT = {
    '一': '1',
    '二': '2',
    '三': '3',
    '四': '4',
    '五': '5',
    '六': '6',
    '七': '7',
    '八': '8',
    '九': '9'
}

def get_data():
    while True:
        data = client.lpop('datalist')
        if not data:
            break
        yield data.decode()

def remove_sensitive_data(datas):
    for data in datas:
        if data == '敏感信息':
            continue
        yield data

def tranfer_chinese_num(datas):
    for data in datas:
        try:
            num = int(data)
        except ValueError:
            num = ''.join(CHINESE_NUM_DICT[x] for x in data)
        yield num

def save_data(number_list):
    for number in number_list:
        data = {'num': number, 'date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
        handler.insert_one(data)

raw_data = get_data()
safe_data = remove_sensitive_data(raw_data)
number_list = tranfer_chinese_num(safe_data)
save_data(number_list)

代码如下图所示:

如果你开启 PyCharm 调试模式,你会发现,数据的流向是这样的:

  1. 从 Redis 获取1条数据
  2. 这一条数据传给remove_sensitive_data
  3. 第2步处理以后的数据传给tranfer_chinese_num
  4. 第3步处理以后,传给 save_data
  5. 回到第1步

整个过程就像是一条流水线一样,数据一条一条地进行处理和存档。不需创建额外的列表,有多少条数据就循环多少次,不做多余的循环。

おすすめ

転載: www.cnblogs.com/xieqiankun/p/use_yield.html