老犬に新しいトリックを教えたり、str.formatを愛し、あきらめた方法を教えた

habrの読者とpythonのファンの注意をひくには、文字列のフォーマットに関するかなり長い記事の翻訳があります。 物語は真実であり、その中のヒントは、習慣が頑固に抵抗しても、保守派は時々新しい何かを考慮するべきであるということです。



トピック外の質問をする傾向がある読者の好奇心を予測して、私はこの写真は最も楽しいものではないが、Pythonと間接的な関係があると言います。 理由を宿題として見つけることをお勧めします。



PMのデザインエラーとタイプミスに関するコメントを待っています。伝統的なhabraplushushkiがあります。





さらに、元の記事の著者の言葉:



私は長年にわたってPythonで書いてきました。 しかし、この旅の最初の段階で、Perlスタイルの文字列をフォーマットする方法を学ぶことに興味がありました。 Perl(および多くのUnixコマンドラインインタープリター)は2種類の文字列リテラルをサポートしていることを思い出させてください-シングルクォート(文字列をそのまま表示する場合)、および変数の値が変数に置き換えられる場合はdoubleです。 たとえば、Perlでは、次のように記述できます。



$name = 'Reuven'; print "Hello, $name\n";
      
      





それに応じて、プログラムは「Hello、Reuven」と書き込みます。



Pythonの文字列リテラルは引用符の種類に依存せず、それらの変数は値に展開されません。 これを実現するために、文字列の%演算子が従来使用されていました。 このコンテキストでは、オペレーターは自分の左側の行を見て、右側の対応する変数の値で置き換える必要がある値の数をカウントします。 操作の結果は、プレースホルダーの代わりに変数値が挿入された新しい行です。 例:



 >>> name = 'Reuven' >>> "Hello, %s" % name 'Hello, Reuven'
      
      





このpythonコードは非常にうまく機能し、パーソナライズされた挨拶を表示します。 だから、私の長年のpythonの練習にもかかわらず、この構文の適用には非常に満足していました。 はい、あまり快適ではありません。いいえ、書式設定に影響する大量のprintf修飾子を記憶していません。 つまり、常に 's'修飾子(文字列として出力)を使用し、Pythonが引数を暗黙的に文字列にキャストするだけで十分でした。



しかし、現時点では、%構文が廃止されているか、少なくとも非推奨と宣言されているのは事実です。 python-devメーリングリストには、少なくとも2022まで2.xブランチに存在するというメモがありますが、3.xブランチについては何も言われていないため、この構文のサポートはすぐに削除され、使用することは望ましくありません。 str.formatメソッドに置き換えられました。



私のPythonチュートリアルでは、常にstr.formatについて言及しましたが、特定の例ではすべてが多くの場合%に依存していました。 個人的にはもっと簡単に思えたので、学生に%を使用することを勧めました。



しかし、私は何か間違ったことをしており、おそらく生徒を誤解させているという思いがしつこく、str.formatをより詳しく研究するように促しました。 この研究の過程で、次の結論に達しました。1)%よりも複雑ではなく、一部のアプリケーションではさらに単純です。 2)str.formatの機能を最大限に活用したことはありません。また、学習に時間がかかるにもかかわらず、非常に便利です。



最も単純なものから始めましょう。 誰かに「おはよう」と言って、変数「first」と「last」に保存されていると仮定して、名前と姓で順番を変えます。 古い方法では、これを行います。



 >>> first = 'Reuven' >>> last = 'Lerner' >>> "Good morning, %s %s" % (first, last) 'Good morning, Reuven Lerner'
      
      





この例でも、%構文の問題の1つに遭遇しました。2つの変数があり、両方を使用するには、それらからタプルを作成する必要があります。 Pythonの観点からすると、これは論理的ですが、多くの学生がこれに非常に驚いていることを保証します。



str.formatの場合、この例はどのようになりますか? かなり似ています:



 >>> "Good morning, {} {}".format(first, last) 'Good morning, Reuven Lerner'
      
      





原則が若干変更されていることに注意してください。 現在、これは文字列に対する二項演算子ではなく、一連のパラメータを受け取る文字列オブジェクトメソッドです。 これは論理的で一貫性があります。 同じ学生の場合、私の例の%演算子は、文字列に対する操作ではなく、印刷の追加のように見えました。 行の後に「.format」という表記があると、このメソッドがその行に固有のものであることがより明確になります。



おそらく既にご存知のように、文字列内での「{} {}」の出現は、str.formatが2つのパラメーターを取る必要があることを意味します。そのパラメーターの値は、メソッドに渡される順序で文字列に挿入されます。 引数が2つあるため、行に{}が2回出現する必要があります。 Pythonの中カッコは、人々に辞書を教え、空のカッコはあまり見栄えが良くないので、理解するのが少し難しくなります。 しかし、それは大丈夫です、私はそれを非常にうまく生き、それを非常に簡単に受け入れました。



str.formatが%を超える最初の利点を示すのは、パラメーターを逆の順序で使用する必要がある場合です。 実際、%sではこれはまったく達成できません。 1つの変数の値を複数回使用することもできません。 str.formatを使用する場合、置換シーケンスを非常に適切に変更できます。



 >>> "Good morning, {1} {0}".format(first, last) 'Good morning, Lerner Reuven'
      
      





空の括弧「{} {}」を使用した場合、パラメータにメソッドが渡されるのと同じ順序で置換が発生することに注意してください。 パラメータを最初からインデックス付けされたシーケンスとして想像できます。順序を変更する場合は、このシーケンスの必要なインデックスを中括弧で囲みます。 最初のstr.formatの例は次のように記述できます。



 >>> "Good morning, {0} {1}".format(first, last) 'Good morning, Reuven Lerner'
      
      





インデックスを明示的に指定すると、自動インデックス作成に依存できなくなることに注意してください。



もちろん、*演算子を使用してリストのシーケンスを使用できます。



 >>> names = ('Reuven', 'Lerner') >>> "Good morning, {} {}".format(*names) 'Good morning, Reuven Lerner'
      
      





名前付き引数も使用できます。



 >>> "Good morning, {first} {last}".format(first='Reuven', last='Lerner') 'Good morning, Reuven Lerner'
      
      





私はこのオプションが特に好きです。 名前付きパラメーターはより明確であり(適切な名前がある場合)、{first}および{last}の使用は非常に読みやすく、特に%(first)sと比較すると、%演算子で必要です



名前付きパラメーターは、**演算子を使用して辞書から展開することもできます。



 >>> person = {'first':'Reuven', 'last':'Lerner'} >>> "Good morning, {first} {last}".format(**person) 'Good morning, Reuven Lerner'
      
      





私はこれをすべて学生に説明しましたが、この構文で彼らがどれほど快適に暮らしているかに非常に驚きました。 そして、自分で仕事をすることがより楽しくなりました。



名前付き引数と位置引数を技術的に一緒に使用できることに言及する価値があります。 しかし、これをしない方が良いです:



 >>> person = {'first':'Reuven', 'last':'Lerner'} >>> "Good {0}, {first} {last}".format('morning', **person) 'Good morning, Reuven Lerner'
      
      







警告した。



str.formatに欠けている可能性があるのは... um ...フォーマットです。 悪いニュースは、str.formatには出力のフォーマット方法を決定するためのまったく異なるルールがあることです。 幸いなことに、これらのルールは習得と理解が非常に簡単です。



単純なものから始めましょう。指定された長さの文字列を印刷する必要がある場合は、コロン(:)を追加し、変数名の後に文字数を追加します。 だから、私の名前を表示し、スペースで10文字に追加するには、これをしなければなりません:



 >>> "Your name is {name:10}".format(name="Reuven") 'Your name is Reuven '
      
      





(文字列は名前の後にスペースが埋め込まれることに注意してください。)



ブロックの右側に配置を設定する必要がある場合は、:と番号の間に>記号を使用します。



 >>> "Your name is {name:>10}".format(name="Reuven") 'Your name is Reuven'
      
      





はい、<を使用して左揃えが必要であることを明示的に示すことができます

ブロックの中央に値を表示する場合、<and>の代わりに^記号が使用されます。



 >>> "Your name is {name:*^10}".format(name="Reuven") 'Your name is **Reuven**'
      
      





テキストではわかりにくいですが、数字はどうですか? 個人的には、これがどのように機能するかを想像することは困難でしたが、すべてがかなり簡単であることがわかりました。 数値の単純な出力には、文字列に似た構文を使用します。



 >>> "The price is ${number}.".format(number=123) 'The price is $123.'
      
      





ただし、数値の場合、文字列よりも多くの修飾子が使用されます。 たとえば、数値をバイナリ形式で表示するには、修飾子「b」を追加します(16進数の場合)-修飾子「x」:



 >>> "The price is ${number:b}.".format(number=5) 'The price is $101.' >>> "The price is ${number:x}.".format(number=123) 'The price is $7b.'
      
      





もちろん、数字の記録は先行ゼロで補うことができます:



 >>> "Your call is important to us. You are call #{number:05}.".format(number=123) 'Your call is important to us. You are call #00123.'
      
      





実行可能なpythonコードを{}内で使用できないことに注意してください。代わりに、一般的なpythonとは別の異なるシンプルなマイクロ言語が提案されています。 いくつかの小さな例外があります。 最初に、ポイントを介して属性/プロパティの値を取得できます。次に、[]を使用してインデックスによってオブジェクトの値を取得できます。



例:



 >>> class Foo(object): def __init__(self): self.x = 100 >>> f = Foo() >>> 'Your number is {ox}'.format(o=f) 'Your number is 100'n
      
      





オブジェクト「f」の属性「x」を取得しました。 このオブジェクトには、文字列内の「o」という名前でアクセスできます。 属性を取得できますが、実行できます-いいえ:



 >>> "Your name is {name.upper()}".format(name="Reuven") AttributeError: 'str' object has no attribute 'upper()'
      
      





対応するメソッドが呼び出されることを前提に、「name.upper()」を実行しようとしましたが、pythonはこの時点でコードの実行を許可せず、括弧とともに「upper()」を属性と見なします。 括弧なしで、関数/メソッドの文字列表現を取得します:



 >>> "Your name is {name.upper}".format(name="Reuven") 'Your name is <built-in method upper of str object at 0x1028bf2a0>'
      
      





角括弧を使用すると、反復可能なオブジェクト(リスト、行)の要素をインデックスで取得できます。 ただし、スライス操作はサポートされていません。



 >>> "Your favorite number is {n[3]}.".format(n=numbers) 'Your favorite number is 3.'
      
      





しかし:



 >>> "Your favorite numbers are {n[2:4]}.".format(n=numbers) ValueError: Missing ']' in format string
      
      





[]を使用して名前で辞書のエントリを取得できますが、名前は引用符なしで入力されます。



 >>> person = {'first':'Reuven', 'last':'Lerner'} >>> "Your name is {p[first]}.".format(p=person) 'Your name is Reuven.'
      
      





引用符を使用しようとすると、例外が発生します...



 >>> "Your name is {p['first']}.".format(p=person) KeyError: "'first'"
      
      





str.formatのすべてのユースケースがここに示されているわけではありません-実際、各タイプにはフォーマットルールの仕様があります。 たとえば、浮動小数点数の精度オプションは文字列には使用できません。



クラスのオブジェクトに対して独自のフォーマットルールを追加して、特別な出力方法と修飾子を設定することもできます。



このトピックについて詳しく知りたい場合は、str.formatが説明されているPEP 3101から始めてください。 また、このトピックに関するかなり良いサマリを使ったエリックスミスによるプレゼンテーションをお勧めします。 Pythonドキュメントにはusing%からstr.formatに切り替える方法の良い例があります



楽しんでいただけましたでしょうか!



P ... S。:元の記事の著者はstr.formatと%のパフォーマンスを測定しました 。 私は%が速いという結論に達しました



PPS:Andrei Svetlov svetlovによると(彼の言葉はpython開発チームに含まれているため信頼できる)-少なくとも今後20年間、%構文はpython 3.xから削除されません



All Articles