Ccmmutty logo
Commutty IT
5 min read

[Python] アスタリスク(*)を使おう-有名なライブラリから技を盗むシリーズ#1

https://cdn.magicode.io/media/notebox/5221c2cc-0764-4aea-ba74-e725d6f5e8a2.jpeg

はじめに

Pythonを書くからには立派なPython芸人になりたい、と夢見るのが人間の性でしょう。ではどうしたら立派なPython芸人になれるのでしょうか。
プログラミングが上手な人は、決まって技の引き出しが多いです。しかしコーディングの実践技はあまり教科書に載っていないし、誰かがつきっきりで教えてくれるわけでもありません。また自己流でプログラミングをしていても、なかなか習得することはできません
一つの解決策として、「人のコードを読んで技を盗む」という方法があります。そしてどうせならnumpyやpandasなど、世界中で使われている有名なライブラリから盗むのが良いに違いありません。というわけでこの連載を始めることにしました。

今回盗む技:アスタリスク(*)でlistをunpackingする

本記事で参考にしたのはpandas.core.frame.appendのコードの一部です。このコードはDataFrameselfと、DataFrameまたはList[DataFrame]またはTuple[DataFrame]のいずれかが格納されている変数otherを結合している場面になります。
if isinstance(other, (list, tuple)):
            to_concat = [self, *other]
        else:
            to_concat = [self, other]
簡単のために、以下に似たコンセプトの関数を書いてみます。str型のstringと、str型でもList[str]でも良いstring_or_listを結合して、List[str]のoutを出力する関数です。
python
def create_list(string, string_or_list):
  if isinstance(string_or_list, (list, tuple)):
    list_of_str = [string, *string_or_list]
  else:
    list_of_str = [string, string_or_list]
  return list_of_str
これにより第二引数にstrを渡してもList[str]を渡しても、List[str]が返す関数が書けました。
python
print(create_list('a', 'b'))
print(create_list('a', ['b','c']))

['a', 'b'] ['a', 'b', 'c']
改めて上のコードを見てみると、条件分岐でコードの形が似ており、美しいですね(私の感想です)。さすがはpandas developers。
list_of_str = [string, *string_or_list]
list_of_str = [string, string_or_list]

アスタリスクによるunpacking

アスタリスク(*)ってなんですか? という読者の方もいらっしゃるかもしれません。私から解説しても良いのですが、一流のPython芸人を目指すのであれば公式ドキュメントを読むべきです(価値観の押し付け)。

公式ドキュメントを読む

さて公式ドキュメント(改行は筆者挿入)を引用してみます。
An asterisk * denotes iterable unpacking. Its operand must be an iterable. 
The iterable is expanded into a sequence of items, which are included in the new tuple, list, or set, at the site of the unpacking.
和訳すると「アスタリスクはイテラブル(筆者注1)のunpacking(筆者注2)を意味する。その被演算子(筆者注3)はイテラブルでなければならない。被演算子のイテラブルは、その構成要素の配列に展開され、unpackingされた場所で新しいtuple, listまたはsetの構成員となる。」といったところでしょうか。
つまり[string, *string_or_list]において、string_or_listを構成する要素が*によってバラされて、list_or_strの構成員になったと考えるのが本来的です。
  • 筆者注1: ざっくりfor文で回せるデータ型、と理解しておけば十分
  • 筆者注2: 分解、くらいの意味
  • 筆者注3: 被演算子(operand)は演算子(operator)の効果を受ける対象のこと。演算子は演算をコンピュータに演算を命令するためのシンボルで、+-がその一種である。 1+1というプログラムにおいて、+は演算子であり1は被演算子である。*string_or_listにおいて*は演算子でありstring_or_listは被演算子である。

だったらこんなこともできる

アスタリスクの被演算子がイテラブルということなら、以下のコードだって成立することになりますね。私も今まで気づきませんでした。
python
x = 'a'
y = 'bcd'
z = [x, *y]
print(z)

['a', 'b', 'c', 'd']
上のコード、Pythonのstr型はイテラブルだから成立するんです! なんならPythonのstr型はfor文で回せますよ。
python
for i in 'hello world':
  print(i)

h e l l o w o r l d

別の書き方で同様の挙動を実現する

プログラミングには絶対的な正解がありません。探せばいくらでも別解はありますし、探すことはコーディング力上昇につながると思います。
例えば以下の書き方なんてどうでしょうか。
python
def create_list2(string, string_or_list):
  out = [string]
  out.extend(string_or_list)
  return out
python
print(create_list2('a', 'b'))
print(create_list2('a', ['b', 'c']))

['a', 'b'] ['a', 'b', 'c']
こっちで書けば3行になるので、より短くなりますね。でももしかしたら、可読性が犠牲になるかもしれません。

まとめ

今回はアスタリスクによるunpackingについて、pandasのコードを取り上げてご紹介しました。人のコードを読んで掘り下げていくと勉強になることが実感できたのではないでしょうか。
こんな感じで次回もやっていきたいと思います。
それでは!
P. S.
もしもっと良いコードがあるよ! とかこんな面白い書き方もあるよ! などありましたら、気軽にコメントしてください!

Discussion

コメントにはログインが必要です。