必見の記事

知っているようで意外と知らなかったPython小ネタ集

2019.08.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

仕事ではよくPythonを書いています。

よく使うのでそれなりに知っている気になっていたのですが、 コードをレビューしてもらったり本を読んだりしているうちに”もっと早く知っておきたかった・・・”というネタが溜まってきたので、その中から厳選した5つの小ネタをまとめてみました。

*この記事で使用しているPythonのバージョンはPython 3.7.3です。

この変数、一体何桁?

例えばこんな変数があったとします。

num1 = 100000000
num2 = 10000
num3 = 3023204903

こんな変数がたくさんあったらどうしましょう。
桁を数えるだけで目が疲れそうです。

ぱっと見でだいたい何桁あるかわかるといいですよね。

Pythonでは数値型に_を挟んでも、そのまま数値として計算することができます。

>>> num1 = 100_000_000
>>> num2 = 10_000
>>> sum = num1 + num2
>>> sum
100010000

そのままいい感じにアンダースコアをセパレータに置き換えたい場合はこのように書くことができます。

>>> f'{sum:,}'
'100,010,000'

三項演算子、使っていこう

他の多くの言語同様Pythonでも三項演算子(Ternary Operator)が使えます。

このようなif分岐だと、

condition = True

if condition:
  x = 1
else:
  x = 2

三項演算子を使って一行でスマートに書くことができます。

condition = True

x = 1 if condition else 2

ネストが深い場合はif文を使った方が読みやすい事もあるので うまく使い分けたいですね。

if文 OR 三項演算子?

余談ですが、PythonではConditional Statement(if文)とTernary Operator(三項演算子)は違うのだろうか・・・と気になったので調べてみたところ、ドキュメントのあちこちでTernary Operatorと言っていたり、Conditional Statementと言っていたりしたので明確な線引きはなさそうだという結論に落ち着きました。

Conditional Expressions (Python's Ternary Operator) Python supports one additional decision-making entity called a conditional expression. (It is also referred to as a conditional operator or ternary operator in various places in the Python documentation.) Conditional expressions were proposed for addition to the language in PEP 308 and green-lighted by Guido in 2005.

Conditional Statements in Python

enumerateって便利

配列をループする際に、Indexが欲しいことがあると思います。

names = ['Tom','Peter','Taro','Chris']
index = 0

for name in names:
  print(index, name)
  index +=1

実行結果:

0 Tom
1 Peter
2 Taro
3 Chris

上のように実装することもできますが、enumerateを使うと更にスマートに書くことができます。

names = ['Tom','Peter','Taro','Chris']

for index, name in enumerate(names):
  print(index, name)

実行結果:

0 Tom
1 Peter
2 Taro
3 Chris

enumerateはキーに数値Indexを割り振ってくれます。

Indexの初めの値を設定するにはstart=1のオプションをつけます。

for index, name in enumerate(names, start=1):
  print(index, name)

実行結果:

1 Tom
2 Peter
3 Taro
4 Chris

この特性を使って辞書にenumerateを代入してみると、 キーにIndexが割り当てられいい感じです。

new_dict = dict(enumerate(names))
print(new_dict)

実行結果:

{0: 'Tom', 1: 'Peter', 2: 'Taro', 3: 'Chris', 4: 'Tom'}

zip()

こんなに便利な関数があるのかと感動したのがzip()です。
複数のイテラブルオブジェクトをループしたい時にzip関数はとても便利です。

names = ['Tom','Peter','Taro','Chris']
age = [20, 13, 30, 35]
dept = ['Business', 'HR', 'Manager', 'Engineer']

for name, age, dept in zip(names, age, dept):
  print(f'{name}: {age}: {dept}')

実行結果:

Tom: 20: Business
Peter: 13: HR
Taro: 30: Manager
Chris: 35: Engineer

zipでそれぞれのリストをタプルに変換する

names = ['Tom','Peter','Taro','Chris']
age = [20, 13, 30, 35]
dept = ['Business', 'HR', 'Manager', 'Engineer']

for value in zip(names, age, dept):
  print(value)

実行結果:

('Tom', 20, 'Business')
('Peter', 13, 'HR')
('Taro', 30, 'Manager')
('Chris', 35, 'Engineer')

zip_longest

zipは一番短い配列に合わせて処理を終了するので、長い配列の余った要素が無視されてしまいます。

names = ['Tom','Peter','Taro','Chris']
age = [20, 13]
dept = ['Business', 'HR', 'Manager', 'Engineer']

for value in zip(names, age, dept):
  print(value)

実行結果:

('Tom', 20, 'Business')
('Peter', 13, 'HR') # TaroとChrisがいないよ!

長い配列に合わせたい場合はzipの代わりに zip_longest()が使えます。

from itertools import zip_longest

names = ['Tom','Peter','Taro','Chris']
age = [20, 13]
dept = ['Business', 'HR', 'Manager', 'Engineer']

for value in zip_longest(names, age, dept):
  print(value)
('Tom', 20, 'Business')
('Peter', 13, 'HR')
('Taro', None, 'Manager')
('Chris', None, 'Engineer') # Good!

足りない分の要素にはデフォルトでNoneが入りますが、 fillvalueで任意の値を割り当てることもできます。

names = ['Tom','Peter','Taro','Chris']
age = [20, 13]
dept = ['Business', 'HR', 'Manager', 'Engineer']

for value in zip_longest(names, age, dept, fillvalue=20):
  print(value)

実行結果:

('Tom', 20, 'Business')
('Peter', 13, 'HR')
('Taro', 20, 'Management')
('Chris', 20, 'Engineer')

二つの配列を辞書に変換

zipを使って二つの配列をそのまま辞書に代入することもできます。

new_dict = dict(zip_longest(names, age, fillvalue=25))

実行結果:

>>> new_dict = dict(zip_longest(names, age, fillvalue=25))
>>> new_dict
{'Tom': 20, 'Peter': 13, 'Taro': 25, 'Chris': 25}

配列の展開

下のコードを実行すると、どうなるでしょう

a, b, c = ['Tom','Peter','Taro','Chris']

エラーになりますね。
配列の値の数に対して変数が足りてないよ、と怒られています。

>>> a, b, c = ['Tom','Peter','Taro','Chris']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3)

初めと二番目の値のみ欲しいけど、後は別にいらない、という時
最後の変数に*をつけると巻き取ってくれます。

>>> a, b, *c = ['Tom','Peter','Taro','Chris']
>>> a, b, c
('Tom', 'Peter', ['Taro', 'Chris'])

三番目以降の値が配列になってcに代入されているのがわかります。

1番目と4番目の値だけ欲しい場合はbに*をつけることで二番目と三番目の値がbに代入されます。

>>> a, *b, c = ['Tom','Peter','Taro','Chris']
>>> a, b, c
('Tom', ['Peter', 'Taro'], 'Chris')

本当に使わない時は変数の代わりに_にしてしまいましょう。 Pythonでは使っていない変数にWarningが表示されることがありますが、_にしておくことで回避することができます。

>>> a, *_, c = ['Tom','Peter','Taro','Chris']
>>> a, c
('Tom','Chris')

あとがき

”もっと早く知っておきたかった・・・!”と思った小ネタをまとめてみました。
特に、zip関数は存在すら知らなかったのでとても勉強になりました。

毎日何かしらの学びがあるのは楽しいですね。
この記事が少しでも誰かの役に立てれば嬉しいです。

参考リンク