質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.99%

str.format()の{ }内の評価について

解決済

回答 2

投稿 ・編集

  • 評価
  • クリップ 0
  • VIEW 371

0ipanema0

score 9

前提・実現したいこと

pythonの初心者です。
str.format()の引数に辞書を入れたところ、エラーになってしまいました。

発生している問題・エラーメッセージ

KeyError: "'y'"

該当のソースコード

d = {'y': 1, 'm': 8}
m = "{0['y']}, {0['m']}".format(d)
print(m)

試したこと

m = "{0[y]}, {0[m]}".format(d)
を実行し、エラーから脱したのですがなぜkeyに『'』が不要なのかということがわかりません。

f-strings では
d = {'y': 1, 'm': 8}
print(f"{d['y']}, {d['m']}")
でうまくいきました。

また、f-strings は{}内をexpressionで評価するそうですがstr.format()は{}内をどのように評価するのですか。
pithonの公式サイトには
str.format(*args, **kwargs)
とありますがこのアスタリスクは可変長位置引数と可変長キーワード引数を取るいうことでしょうか。それとも展開するということでしょうか。

初歩的な質問で恐縮ですが、以上のご教示をお願いします。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+4

(A)"{0[a]}".format({'a': 1})ではなく
(B)"{0['a']}".format({'a': 1})の方が自然

という感覚はよくわかります。では

(C)i = 0; "{0['abc'[i]]}".format({'a': 1, 'b': 2, 'c': 3})

と記述できても(例がイマイチで申し訳ないですが)よさそうに感じませんか?つまり整数を1、文字列を'a'と表記するルールにするなら「整数や文字列のリテラルにとどまらずそれ以外の型を結果とするような任意の式も記述できるようにしないと中途半端」に感じないでしょうか?

実際にformatにはそこまでの能力はなく添え字には「整数」と「文字列」のリテラル相当が指定できるだけです。それでも一応添え字表記ができることによって

"name={0[name]}, id={0[id]}".format({id: 10, name: 'ksoh'})

と記述でき、こうした方が

d = {id: 10, name: 'ksoh'}; "name={0}, id={1}".format(d['name'], d['id'])

と書くよりは「書式化の結果を把握しやすい」という利点を得ることができますよね?

このように多少の制限を付けた上でちょっと便利な記述を許す程度にしておくのがformatの設計者の感覚だったのだろうと思います。例えばですが整数のtupleをキーとしたdictに対して

d = {(0, 0): 'a', (0, 1): 'b'}
d[0, 0]   # => 'a'
d[0, 1]   # => 'b'
"{0[0, 0]}, {0[0, 1]}".format(d) # => 'a, b'


なんてできたら少し便利かも知れませんが、そこあたりをやり始めると結局「かなり広いPythonの構文解析をせねばならないのは必至」なので「整数と文字列のリテラルだけで止めておこう」というわけですね。整数と文字列のリテラルしか指定できないなら

"{0[0]}, {1['a']}".format(...)

ではなく、どうせ制限の強い仕様なので

"{0[0]}, {1[a]}".format(...)

でいいや・・・ってことではないでしょうか。


もちろん、(C)のような記述を許し「添え字に任意の式を表記できる」ような仕様にもできますけど、そうするためにはformat関数にPythonの言語パーサーと同等の構文解析能力を組み込まなければならなくなります。またプログラムが起動されたのちに書式文字列をPythonの式の構文に従って解析するという大変「遅くてメモリーの消費もばかにならない実装」になることでしょう。

それはformat関数がやろうとしていることに対して「あまりにオーバースペックで、それこそいびつな仕様である」と設計者が考えたのだろうと想像します。

ただ使う側にとってformat関数の書式指定の仕様はなんとも中途半端な感じがするのは同意します。それゆえPython 3.6で「fomrat関数ではなく、Python言語の構文を拡張しスクリプトをパースする時点で書式を解析するようにしたフォーマット済み文字列リテラルが導入された」のだと思います。(このような言語仕様はPython以外の言語でも通常は言語の構文パーサーがソースファイルを解析する時点で解釈するのが一般的だと思います。)

フォーマット済み文字列リテラルなら、使う側にとっても納得できる仕様だし実装的にも問題が少ないものにできていると思います。当初からこの言語仕様があればformat関数の仕様に添え字をリテラル限定で指定できる仕様はできなかったかも知れませんね。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/15 14:13

    なんか長々と書いてしまいましたが・・・
    当初format(*args,**kwargs)の言語仕様の説明から入って、ぐだぐだ書きすぎてしまい、それを編集し余計な説明を省いてみたところ残った内容は結局hayatakaさんコメントと同じになっちゃいました... orz

    キャンセル

  • 2019/03/15 18:37

    ご回答感謝します。

    キャンセル

  • 2019/03/15 18:43

    >プログラムが起動されたのちに書式文字列をPythonの式の構文に従って解析するという大変「遅くてメモリーの消費もばかにならない実装」になることでしょう
    あー、formatメソッドは任意の文字列オブジェクトに対して呼べるので、確かにevalしちゃうといろいろ大変ですね。
    f-stringsならリテラルなので、実行時にどうこうなる要素はないのでその分柔軟にできるというのは確かに納得しました。

    キャンセル

+2

なぜkeyに『'』が不要なのかということがわかりません。

そういう仕様であるから、としか言いようが無いのです。

>>> dct = {1: 'a', '1': 'b', '"1"': 'c'}
>>> for k in dct:
...     print(type(k), k)
...
<class 'int'> 1
<class 'str'> 1
<class 'str'> "1"
>>>
>>> '{0[1]}'.format(dct)
'a'
>>> '{0["1"]}'.format(dct)
'c'

以前同じような疑問を抱き、調査したことがあります。
Qiita - str.formatの書式指定文字列の、腑に落ちない点


str.format(*args, **kwargs)
とありますがこのアスタリスクは可変長位置引数と可変長キーワード引数を取るいうことでしょうか。それとも展開するということでしょうか。

前者です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/15 16:54

    @0ipanema0 さん
    その認識でよろしいかと。
    未知のキーワード引数が kwargs にまとめられるのは **kwargs が用意された関数全体に言える話で、それがどのように活用されるかは関数に依ります。

    @hayataka2049 さん
    リンク先拝見しました。
    何と言うか、シンプルにしようとした結果、却って理解しづらくなっている気がしますね。

    先のQiitaの記事に反映させていただいてもよろしいでしょうか?

    キャンセル

  • 2019/03/15 17:17 編集

    私はリンクを貼っただけなので、Qiitaの記事に反映するのであれば特に言うことはありません。

    キャンセル

  • 2019/03/15 18:35

    みなさま、ご回答感謝します。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる