次のような2Dリストがあります。
table = [['donkey', '2', '1', '0'], ['goat', '5', '3', '2']]
最後の3つの要素を整数に変更したいのですが、以下のコードは非常に醜く感じます。
for row in table:
for i in range(len(row)-1):
row[i+1] = int(row[i+1])
しかし、私はむしろ次のようなものを望んでいます:
for row in table:
for col in row[1:]:
col = int(col)
上記のコードを書く方法があるはずだと思いますが、スライスはオリジナルとは別のイテレータ/新しいリストを作成するため、参照は引き継がれません。
もっとPythonicソリューションを取得する方法はありますか?
for row in table:
row[1:] = [int(c) for c in row[1:]]
上記はよりpythonicに見えますか?
試してください:
>>> for row in table:
... row[1:]=map(int,row[1:])
...
>>> table
[['donkey', 2, 1, 0], ['goat', 5, 3, 2]]
申し訳ありませんが、list
スライスに割り当てると、新しいlist
を作成する代わりに、操作が強制的に実行されます。
私はシェカールがたくさん答えるのが好きです。
一般的なルールとして、Pythonコードを記述しているときに、for i in range(len(somelist))
を記述していることに気づいた場合、それは間違っています。
enumerate
を試してくださいZip
またはitertools.izip
を試してください。あなたの場合、最初の列が異なるため、列挙をエレガントに使用することはできません。
for row in table:
for i, val in enumerate(row):
if i == 0: continue
row[i] = int(val)
「醜い」コードは、2つの引数を指定してrange
を呼び出すだけで改善できます。
for row in table:
for i in range(1, len(row)):
row[i] = int(row[i])
これは、新しい一時リストを割り当てずに(リスト内包、map
、および/またはスライスを使用して)アイテムを適切に変更することを主張する場合に実行できる最善の方法です。参照 Pythonの「マップ」に相当するインプレースはありますか?
お勧めしませんが、独自のインプレースマップ関数を導入して、このコードをより一般的なものにすることもできます。
def inplacemap(f, items, start=0, end=None):
"""Applies ``f`` to each item in the iterable ``items`` between the range
``start`` and ``end``."""
# If end was not specified, make it the length of the iterable
# We avoid setting end in the parameter list to force it to be evaluated on
# each invocation
if end is None:
end = len(items)
for i in range(start, end):
items[i] = f(items[i])
for row in table:
inplacemap(int, row, 1)
個人的には、これはlessPythonicだと思います。それを行うには明らかな方法が1つだけあることが望ましいが、これはそうではない。
リスト内包表記を使用:
table = [row[0] + [int(col) for col in row[1:]] for row in table]
これは動作します:
table = [[row[0]] + [int(v) for v in row[1:]] for row in table]
ただし、リストが最初に作成された時点で変換を行うことを検討したい場合があります。