2017-06-09 6 views
6

私は鍵ごとにブロックされたユーザのリストを作成する必要があります。各ユーザーには複数の属性があり、これらの属性のいずれかがキーにある場合、ユーザーはブロックされます。ネストされたforループを作る方法Pythonic

私は以下のネストされたfor -loopを書いてくれました。それは私のために働いていますが、より少ない行数とより読みやすい方法でもっとpythonicな方法で書いてみたいと思います。どうやってやるの?

for key in keys: 
    key.blocked_users = [] 

for user in get_users(): 
    for attribute in user.attributes: 
     for key in keys: 
      if attribute.name == key.name: 
       key.blocked_users.append(user) 
+4

私はあなたのコードがunpythonicだとは思わない。私はまた、節約する線が必然的にコードをより非平凡にするとは思わない。とにかく:通常は 'itertools.product'を使ってネストされたforループを避けることができますが、内側のループが外側のループ変数に依存している場合は使用できません。 – timgeb

+1

これはまあまあです。リストの理解に3つの 'for'ループを使用するだけで、醜い混乱になります。 –

答えて

2

これよりも短くすることで、操作をPythonで最適化された関数に減らすことができます。それは短くないかもしれませんが、それより速くても、スピードよりももっとピーコニックなのでしょうか? :)

たとえば、各ユーザーの属性ごとにキーを反復処理します。ちょうどそれが最適化された "離れて" sreams。たとえば、あなたは、(検索用)辞書内のキーの名前を収集でき、一度(属性名との交点のための)セット:それは問題ではないので、

for key in keys: 
    key.blocked_users = [] 

keyname_map = {key.name: key.blocked_users for key in keys} # map the key name to blocked_user list 
keynames = set(keyname_map) 

set(keyname_map)は非常に効率的な操作ですあなたは2つのコレクションを周りに保持しています。

そして、属性名と一致するキー名を取得するためにset.intersectionを使用します。

for user in get_users(): 
    for key in keynames.intersection({attribute.name for attribute in user.attributes}): 
     keyname_map[key].append(user) 

set.intersectionがあまりにもかなり速いです。

ただし、この方法では、attribute.namekey.nameはハッシュ可能である必要があります。

-1

ことがよりPython的な、のようなものと考えられていた場合、リスト内包のループのために記載されている使用してみてください:あなたはあなたの最初のfor -loopに条件付き理解を使用することができ

[key.blocked_users.append(user) for key in keys 
     for attribute in user.attributes 
     for user in get_users() 
     if attribute.name == key.name] 
+0

私はこれがより読みやすいとは思わない。あなたの行を80文字でラップすると、それほど短くはありません。 – timgeb

+0

このような長いリストの理解は本当に読みやすくなっているのか分かりません。 – SRC

+4

そして、副作用のための 'list'の理解を使うことは、酷使のようです。 – AChampion

5

for key in keys: 
    keyname = key.name 
    key.blocked_users = [user for user in get_users() if any(attribute.name == keyname for attribute in user)] 
+2

私はそれを書いた通りですが、高価な操作であれば 'users = get_users()'をループから抜け出します。私は 'keyname'を作りませんでしたが、それはマイナーなパフォーマンス改善です。 – AChampion

2

具体的なケースでは、内側のforループは外側のループ変数に依存していますが、そのままコードを残しておきます。あなたは、行数を強制的に減らすことによって、コードをよりpyononicな、または読みやすいものにすることはありません。

これらのネストされたループが直感的に書かれていれば、読みやすいでしょう。

ループ変数が「独立」のforループをネストしている場合は、itertools.productを使用できます。

>>> from itertools import product 
>>> a = [1, 2] 
>>> b = [3, 4] 
>>> c = [5] 
>>> for x in product(a, b, c): x 
... 
(1, 3, 5) 
(1, 4, 5) 
(2, 3, 5) 
(2, 4, 5) 
+0

'itertools.product(get_users()、keys)を使って'合意しても、 'key.blocked_users'の作成が厄介です。 – AChampion

+0

@AChampion私が言ったように、私はOPのケースでは 'itertools.product'の使用をお勧めしません。 – timgeb

+1

申し訳ありませんが、合意の意味です。 – AChampion

関連する問題