2014/03/03

【Python】変形するリスト

ついつい気づかずやっちゃうやつ。

リストを for にいれて回してるときとかに、
ちゃんと動いてはいるようなんだけど、
なーんか結果が思ってるのと違うなー、みたいな。
mylist = ['a','b','c','d','e','f']
for i in mylist:
    if i in ['b','d','e']:
        mylist.remove(i)
print mylist
とか、そういうやつです。
ざっと見ると問題ないような気もしますが、ダメなんですね!(わらい

やってみると、最終的に mylist は
['a', 'c', 'e', 'f']
となります。あれ? 'e' が除外されてない……

(※追記アリ : 20140304)


どこがだめかというと、
ここです。
    if v in ['b','d','e']:
        mylist.remove(v)
ここで、条件に該当した場合リストの要素を減らしていますが、
これによって、
forで回しているリスト自体が徐々に変形していくことになります。

なかでどういうことが起きているか調べてみましょう
mylist = ['a','b','c','d','e','f']
for i,v in enumerate(mylist):
    if v in ['b','d','e']:
        mylist.remove(v)
    print i,v,mylist
ループの最後に、今が何回目か / 検査対象 / mylistがどうなったか
をプリントさせてみます。

実行すると、こう
0 a ['a', 'b', 'c', 'd', 'e', 'f']
1 b ['a', 'c', 'd', 'e', 'f']
2 d ['a', 'c', 'e', 'f']
3 f ['a', 'c', 'e', 'f']
['a', 'c', 'e', 'f']
なんと、条件に該当する度にリストが変形しているのが分かります! <そりゃそうだ
リストの要素はスタート時には6個なのですが、
結果的に検査は4回しか行なわれていません(!!!

検査対象の部分を見ると分かりますが、
冒頭で漏れていることがわかった 'e' 以外にも、
cも検査されていないですね。
素敵にトラブルの温床になりそうですね。

どうやって回避しようかなーという話しですが、
mylist = ['a','b','c','d','e','f']
result = []
for i in mylist:
    if i not in ['b','d','e']:
        result.append(i)
print mylist,result
ぐるぐる回しながらremoveするのはあかんってことで、
てっとりばやく結果入れる用のリストを用意してみました。

実行してみると
['a', 'b', 'c', 'd', 'e', 'f'] ['a', 'c', 'f']
このような結果に。
もちろんmylistには手を加えていないので、スタート時のままです。
これで無事 'e' もフィルタリングされました。

実際には、たとえばMayaなら、
mylist = pm.selected()
みたいな感じで最初のリストを取得したり、
 if のとこももっと複雑になったりするかと思います。

(ココから追記です:20140304)

匿名様のコメントにて、
set型の利用を示唆していただきました。
具体的にはこういうことかと思います。
myset = set(['a','b','c','d','e','f'])
myset -= set(['b','d','e'])
print myset
瞬く間に狙いの ['a', 'c', 'f']的なものが得られましたw
素晴らしいですね。
集合同士を操作するときはぜひ使って行きたいと思います!

最後に、追記前の本文の「実際には〜」以降に続きそうな例を。


ちょっと荒んだシーンですが(笑
Outlinerに見えるような構造のグループがいくつかシーン内にあって、
(例えばgroup1を選択中として) group1 直下のポリゴンのうち、
ナーブスカーブを子に持つやつだけが欲しいとします。

このとき、
import pymel.core as pm

objs = pm.selected()[0].getChildren()
result = []
for obj in objs:
    for child in obj.getChildren(type='transform'):
        if child:
            if isinstance(child.getShape(),pm.nt.NurbsCurve):
                result.append(obj)
print '# result:',result
…という感じでappendしていけばよかったのですが、
そのときはつい、resultリストを立てない路線をとか考えたのでしょう、
そして安直にremoveしていった結果、
今回のリスト破壊エントリーを書くきっかけとなった次第です…。南無。
(※実際のシーンはどんなだったか覚えていません(笑
(最後の例はリスト内包hy▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわあああああああ
 いやそうでなくともほかにもっといい書き方ががが
(あと、出発時点のリストをcopyして回すのがいいと今日教えてもらいましたw
 また試してみたいところです)



■参考

5. 組み込み型 @ Python » Documentation » Python 標準ライブラリ
http://docs.python.jp/2/library/stdtypes.html#set

[pymel] pymel始めました @ mtazmi-Lab
http://mtazmi-lab.blogspot.jp/2012/03/pymel-pymel.html

5.1.4. リストの内包表記 @ Python » Documentation » Python チュートリアル
http://docs.python.jp/2/tutorial/datastructures.html#id6

3.18 copy -- 浅いコピーおよび深いコピー操作
http://docs.python.jp/2.4/lib/module-copy.html

2 件のコメント:

  1. set型は使わないんですか?

    返信削除
  2. コメントありがとうございます!
    すいません、普段あまりsetを使わないもので、今回の課題にとってどういういいことがあるのかと考えてみたのですが、
    おっしゃる意図としては、集合同士の比較ならsetを使った方がよいということでしょうか…
    確かにsetなら圧倒的にすっきりしますし素晴らしいですね!
    ただ、普段扱っている内容が、最初の例のように集合が明らかになっていることがほとんどありませんでしたので、
    ついいつもの感じで進めてしまいました(^^;;
    例として悪く、申し訳ありませんでした。
    より意図に近い実例を付記してみましたので、
    またよりよき解法がありましたら、ご教示いただけると幸いです!

    返信削除