RにはないPythonの文法・テクニック (データ型 編)

前回の記事で「RにはないPythonの文法・テクニック」として演算子,for文,条件分岐について書きました。記事を公開してからRガチ勢の方々に「これはRでもあるよ!」とツッコミを頂きすぐに訂正しました。

datascience-blog.com

今回はデータ型についてピックアップしてみます。名著である『Python 実践入門』『Python言語によるプログラミングイントロダクション』を参考にしながら,Python documentationを参照したいと思います。

Rに無いものだけピックアップする予定だったのに,書いてる途中にいくつかの機能はRにもあることが判明しました(私の知識不足)。でもメジャーな機能ではないということで載せたままにします爆

文字列への変数の埋め込み + 書式化

フォーマット済み文字列リテラル (f-string)

Python documentationの 2.4.3. フォーマット済み文字列リテラルのページに以下の記述があります。

フォーマット済み文字列リテラル(formatted string literal)または f-string は、接頭辞 'f' または 'F' の付いた文字列リテラルです。これらの文字列には、波括弧 {} で区切られた式である置換フィールドを含めることができます。

まず文字列リテラルとは単純にクオーテーション'で囲んでできる文字列/str型を指します。"apple"みたいなものです。フォーマット済み文字列リテラルは,文字列リテラルの一部を,別で用意している変数(文字列や数値)と置き換えられるという機能です*1。変数の埋め込みを行う際のポイントは2点です。

  • クオーテーションの前にfを付け
  • クオーテーションの中で{変数名}を用いる
>>> my_name = "パイソン太郎"
>>> my_age = 65
>>> f"Hi, my name is {my_name}. I'm {my_age} years old"
"Hi, my name is パイソン太郎. I'm 65 years old"

また以下のように数値や日付の書式化も可能です。

>>> width = 10
>>> precision = 4
>>> value = 12.34567
>>> f"result: {value:{width}.{precision}}" 
'result:      12.35'


- Rにおける変数埋め込みと書式化 -

「Rで変数埋め込みと書式化をするなら可読性の酷いpaste()とsprintf()の組み合わせだろうなぁ」と思っていましたが念の為調べてみると,なんとRにもf-stringと同じ用途のライブラリが3つもありました

どのライブラリもPythonのf-stringを模して作成されたものだと思います。str_interpとglueを使ってみました。超使いやすいです。

> library(stringr)
> my_name <- "パイソン太郎"
> my_age <- 65
> str_interp("Hi, my name is ${my_name}. I'm ${my_age} years old")
[1] "Hi, my name is パイソン太郎. I'm 65 years old"

> library(glue)
> glue("Hi, my name is {my_name}. I'm {my_age} years old")
Hi, my name is パイソン太郎. I'm 65 years old

ただ,Pythonではf-stringがデフォルトで搭載されている機能であるのに対し,Rのstr_interpやglueは比較的新しい関数なので,こういった記法がRユーザーの中ではまだ定着していないのではないかなと感じます。便利なのでどんどん使っていきましょう。

str.format

文字列メソッドの1つであるstr.formatはf-stringと同じように変数の埋め込みに利用できます用語集に以下の記述があります。

文字列の書式化操作を行います。このメソッドを呼び出す文字列は通常の文字、または、 {} で区切られた置換フィールドを含みます。それぞれの置換フィールドは位置引数のインデックスナンバー、または、キーワード引数の名前を含みます。返り値は、それぞれの置換フィールドが対応する引数の文字列値で置換された文字列のコピーです。

少し分かりづらいのでまとめると以下です。

  • 文字列の中に波括弧{}を含める
  • 文字列の後に.formatメソッドを続け,波括弧{}に入れたいもの(文字列や数値)を引数として渡す
>>> "Hi, my name is {}. I'm {} years old".format("パイソン太郎", 65)
"Hi, my name is パイソン太郎. I'm 65 years old"

他にも辞書型とformatを組み合わせた書き方など色々ありますが割愛します。


- Rにおけるstr.format -

str.formatはRのsprintfに似ていますが,sprintfは実数のみ扱えるのに対し,str.formatは文字列でも数値でも式でも扱えるので,これらは適応範囲が異なります。しかしながら上に挙げたstr_interpやglueで代用できるのでこのstr.formatの記法がRになくても特に問題なさそうです。

immutable(不変)なオブジェクト

用語集のimmutableを見てみます。

immutable(イミュータブル): 固定の値を持ったオブジェクトです。イミュータブルなオブジェクトには、数値、文字列、およびタプルなどがあります。これらのオブジェクトは値を変えられません。別の値を記憶させる際には、新たなオブジェクトを作成しなければなりません。

抑えておくべき点は数値・文字列・タプルは不変で,オブジェクトの中身を変えられないという点です。つまり数値1や文字列"a"に何か代入することができない,ということです。

>>> 1 = 2
  File "<input>", line 1
SyntaxError: cant assign to literal

>>> "a" = 2
  File "<input>", line 1
SyntaxError: cant assign to literal

Rでも数値や文字列に何か代入するはできませんが,そもそもそんな馬鹿げたことは誰もやらないと思います。なのでRユーザーで普段から"immutableなオブジェクト"について意識している人は少ないかと思います。

では何故Pythonにおいてimmutableなオブジェクトが敢えて取り上げられるのか。理由がこちらです。

イミュータブルなオブジェクトは、固定のハッシュ値が必要となる状況で重要な役割を果たします。辞書のキーがその例です。

つまり,辞書型のキーにはimmutableなオブジェクトしか与えられないということです。

# immutableオブジェクトの例
>>> {'one': 1} # 文字列
{'one': 1}
>>> {('John', '26'): 1} # タプル
{('John', '26'): 1}
>>> {1: 'one'} # 数値
{1: 'one'}

# mutable object
>>> {[1]: 0} # リストを辞書のキーとして与える
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'list'

>>> {{'one': 1}: 'first case'} # 辞書を辞書のキーとして与える
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'dict'

Rで普段意識しない"不変なオブジェクト"について,Pythonでは意識する必要があります。

カッコなしでタプルを作成

入門書には必ず説明が載っている配列の1つがタプルです。リストは要素が可変(mutable)な配列ですが,タプルは要素が不変(immutable)な配列です。

タプルの作り方についてPython documentationの5.3. タプルとシーケンスを見てみましょう。

タプルの表示には常に丸括弧がついていて、タプルのネストが正しく解釈されるようになっています。タプルを書くときは必ずしも丸括弧で囲まなくてもいいですが、(タプルが大きな式の一部だった場合は) 丸括弧が必要な場合もあります。

要するに,タプルは丸括弧かカッコなしで作成できます。また要素1つのタプルは,1つめの要素の直後にカンマを打てば作れます。

# カッコあり
>>> x = (1, 2, 3)
>>> print(x)
(1, 2, 3)

# カッコなし
>>> y = 1, 2, 3
>>> print(y)
(1, 2, 3)

# 要素一つカッコなし
>>> z = 1,
>>> print(z)
(1,)

このカッコなしで配列を作れたり,カンマで終了してOKという記法がRユーザーにとって見慣れない部分かなと思います。Rでベクトル/行列/配列を作る際は,それぞれc(),matrix(), array()関数で作りますが,関数なので当然カッコが必要あり,カンマで終わったりできません。

終わりに

間違いがあればすぐに訂正するので是非ご指摘ください。

書きながら思ったんですが,「RにないPythonのテクニック」なんて挙げ始めたら切りがないですね。 それでもメジャー所をピックアップしているつもりなので目を瞑ることにします。

次回は内包表記を取り上げます。

*1:Python3.6以前は%演算子を使った方法が主流でしたが,可読性が低いためPython3.6以降はf-stringが推奨されています。参照: PEP 498 Literal String Interpolation