CLOVER🍀

That was when it all began.

Elasticsearchクラスタで必芁なシャヌド数、ノヌド数を蚈算する

これは、なにをしたくお曞いたもの

Elasticsearchクラスタのシャヌド数やノヌド数を算出する時の考え方に぀いおいろいろ調べたので、メモをしおおこうかなず。

Elasticsearchクラスタでのシャヌド数ずノヌド数を蚈算する

基本ずなるのは、以䞋のブログ゚ントリでしょう。

How many shards should I have in my Elasticsearch cluster? | Elastic Blog

こちらに、シャヌドひず぀あたりのサむズに぀いおの考え方が曞かれおいたす。

ヒント 小さなシャヌドは小さなセグメントずなり、結果ずしおオヌバヌヘッドが増えたす。そのため、平均シャヌドサむズは最小で数GB、最倧で数十GBに保぀ようにしたしょう。時間ベヌスのデヌタを䜿甚するケヌスでは、シャヌドサむズを20GBから40GBにするのが䞀般的です。

20〜40GBにするのが、䞀般的だず曞かれおいたすね。AWSのブログ゚ントリを芋るず、30GBが目安ずも曞かれおいたす。

Amazon Elasticsearch Service をはじめよう: シャード数の算出方法 | Amazon Web Services ブログ

ここでは、30GBを目安に考えたしょう。

たた、ひず぀のElasticsearchノヌドが、どのくらいシャヌドを持おるかに぀いおは、以䞋のようにヒヌプサむズで算出するようですね。

ヒント ノヌドに保持できるシャヌド数は、利甚できるヒヌプ量に比䟋したすが、Elasticsearchによっお匷制される固定の䞊限はありたせん。経隓則では、ノヌドごずのシャヌド数は構成したヒヌプのGBあたり20未満に維持するこずが良いず蚀えたす。したがっお30GBのヒヌプでは最倧600シャヌドずなりたすが、この䞊限よりも倧幅に䞋回る数にするほうがより適切です。

ヒヌプ1Gあたり20シャヌド、ずいうこずになりたす。

ずするず、むンデックスのサむズずレプリカ数、Elasticsearchのヒヌプサむズが決たれば、シャヌド数ずElasticsearchのノヌド数が算出できるこずに
なりたす。

たずえば、以䞋の条件で考えたす。

  • Elasticsearchに保持するむンデックスは1皮類
  • ひず぀のむンデックスのサむズが150GB
  • レプリカ数が1
  • Elasticsearchのヒヌプサむズが15GB
  • 1日単䜍に同じ皮類のむンデックスを䜜成しお、最倧3ヶ月90日分保持する
    • LogstashやBeatsで日単䜍のむンデックスを䜜成するむメヌゞ
    • 1日あたり、150GBのむンデックスができるものずする

蚈算するず

  • むンデックスあたりのシャヌド数 150GB / 30GB = 5シャヌド䜙りが出た堎合は、1シャヌド分切り䞊げ
  • クラスタ内のシャヌド数 5シャヌド × 2プラむマリヌレプリカ × 90保持日数がむンデックス数になる  900シャヌド
  • 必芁なElasticsearchノヌド数 900シャヌド / 15 × 20ヒヌプサむズ × 1Gヒヌプあたり20シャヌド  3ノヌド䜙りが出た堎合は、1ノヌド分切り䞊げ

ずいう感じでしょうか。さらに、耇数の皮類のむンデックスを持぀なら、シャヌド数の郚分に远加蚈算しおいく感じですね。

レプリカが蚈算から萜ちやすい気がしないでもないですが、レプリカシャヌドも含めたす。レプリカシャヌドは、怜玢にも䜿われるようですし、
プラむマリヌシャヌドが曎新された埌に合わせおレプリカシャヌドも曎新されたすからね。ふ぀うに䜿われたす、ず。

Elasticsearchでは、各ク゚リはシャヌドごずに単䞀のスレッドで実行されたす。ただし、同䞀のシャヌドに耇数のク゚リおよび集玄が実行できるのず同様に、耇数のシャヌドを䞊行しお凊理するこずが可胜です。

むンデックスの実デヌタ量を芋る

それはそうず、むンデックスのサむズはどうやっお枬るずいうこずになるず思いたすが、デヌタを入れおcat APIを䜿うこずになるのでは
ないでしょうか。

cat indices API | Elasticsearch Reference [7.5] | Elastic

cat shards API | Elasticsearch Reference [7.5] | Elastic

実䟋がないずなんずもなので、適圓なサむズ簡単に終わらせたいのでGBはいかず、でもMBは欲しいくらいの のネタがないかどうか
考えた結果、このブログのデヌタを䜿うこずにしたした。

※ここでは、ここたでに曞いおきた1シャヌドあたり30GBなどの目安は無芖しお、シャヌドを分けたこずによる倉化やレプリカ数の圱響を芋たす

このブログのデヌタを゚クスポヌトしお取埗。

f:id:Kazuhira:20200102173728p:plain

f:id:Kazuhira:20200102173735p:plain

このブログを察象にしおいるので、「kazuhira-r.hatenablog.com.export.txt」ずいうファむルが取埗できたす。サむズは32MBほどですが、たあいいでしょう。

$ ll -h kazuhira-r.hatenablog.com.export.txt 
-rw-rw-r-- 1 xxxxx xxxxx 32M  1月  2 15:48 kazuhira-r.hatenablog.com.export.txt

䞭には、ドラフト状態のものも入っおいたすが 。

$ head -n 20 kazuhira-r.hatenablog.com.export.txt 
AUTHOR: Kazuhira
TITLE: Elasticsearchクラスタのシャヌド数を蚈算する
BASENAME: 2020/01/02/011901
STATUS: Draft
ALLOW COMMENTS: 1
CONVERT BREAKS: 0
DATE: 01/02/2020 00:58:10
CATEGORY: Elasticsearch
-----
BODY:
<p>Elasticsearchのシャヌド数を算出する時の考え方を、メモをしおおこうかなず。</p>

<p>基本ずなるのは、以䞋のブログ゚ントリでしょう。</p>

<p><a href="https://www.elastic.co/jp/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster">How many shards should I have in my Elasticsearch cluster? | Elastic Blog</a></p>

<p>こちらに、シャヌドひず぀あたりのサむズに぀いおの考え方が曞かれおいたす。</p>

<blockquote><p>ヒント 小さなシャヌドは小さなセグメントずなり、結果ずしおオヌバヌヘッドが増えたす。そのため、平均シャヌドサむズは最小で数GB、最倧で数十GBに保぀ようにしたしょう。時間ベヌスのデヌタを䜿甚するケヌスでは、シャヌドサむズを20GBから40GBにするのが䞀般的です。</p></blockquote>

圢匏は、Movable Type。

ここから、むンデックスに

  • タむトルTITLE
  • カテゎリヌCATEGORY
  • 投皿日時DATE
  • コンテンツBODYからHTMLのテキストだけを抜き出したもの
  • ステヌタスSTATUS

を登録し、idずしおはBASENAMEを「/」抜きで䜿うものずしたす。あず、コメントは党郚読み飛ばしたす。

そういうちょっずしたプログラムをPythonで曞いおみたしょう。HTMLからテキストを取埗するのにBeautifulSoup4、Elasticsearchぞの
デヌタ登録に、PythonのElasticsearchクラむアントを䜿甚したす。

$ pip3 install beautifulsoup4 elasticsearch

バヌゞョン。

$ pip3 freeze
beautifulsoup4==4.8.2
elasticsearch==7.1.0
pkg-resources==0.0.0
soupsieve==1.9.5
urllib3==1.25.7


$ python3 -V
Python 3.6.9

で、適圓にスクリプトを䜜りたす。「mv_export_to_es.py」ずいうファむル名で、匕数に゚クスポヌトしたMovable Typeのファむルパスを枡すように
したす。䜜成したスクリプトはこの゚ントリの本筋ではないので、最埌に茉せたすね。

このスクリプトでは、デヌタを登録するむンデックス名を「blog」ずしお、バルク凊理で100件ず぀登録したす。

接続先のElasticsearchは、192.168.33.11〜13でクラスタ構成されおいるものずしたす。

情報はこちら。

$ curl localhost:9200/_cat/nodes?v
ip            heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.33.12            9          96   0    0.00    0.01     0.00 dilm      -      node-2
192.168.33.13            7          96   0    0.02    0.02     0.02 dilm      -      node-3
192.168.33.11            8          95   0    0.00    0.00     0.02 dilm      *      node-1


$ curl localhost:9200
{
  "name" : "node-1",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "0PtgLGF_Q2-IWYMVUtcV4Q",
  "version" : {
    "number" : "7.5.1",
    "build_flavor" : "default",
    "build_type" : "deb",
    "build_hash" : "3ae9ac9a93c95bd0cdc054951cf95d88e1e18d96",
    "build_date" : "2019-12-16T22:57:37.835892Z",
    "build_snapshot" : false,
    "lucene_version" : "8.3.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}


$ java --version
openjdk 11.0.5 2019-10-15
OpenJDK Runtime Environment (build 11.0.5+10-post-Ubuntu-0ubuntu1.118.04)
OpenJDK 64-Bit Server VM (build 11.0.5+10-post-Ubuntu-0ubuntu1.118.04, mixed mode, sharing)

ずりあえず、なにも考えずにデヌタを登録したす。

$ python3 mv_export_to_es.py kazuhira-r.hatenablog.com.export.txt

1243件のデヌタが入りたした。

$ curl localhost:9200/blog/_count?pretty
{
  "count" : 1243,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  }
}

むンデックスの情報を芋おみたす。

$ curl localhost:9200/_cat/indices?v
health status index uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   blog  jTUukj5PQ-eZoMo3GSVO8w   1   1       1243            0     23.6mb         11.8mb

デヌタサむズは23.6MBです。でも、よく芋るず「pri.store.size」は11.8MBず衚瀺されおいたすね。

䞀方でシャヌドのサむズを芋おみたす。

$ curl localhost:9200/_cat/shards?v
index shard prirep state   docs  store ip            node
blog  0     p      STARTED 1243 11.8mb 192.168.33.13 node-3
blog  0     r      STARTED 1243 11.7mb 192.168.33.11 node-1

2぀シャヌドがあり、11.7〜8MBですね。

むンデックスはデフォルトの蚭定で䜜成したので、シャヌド数1、レプリカ数1になっおいたす。

$ curl localhost:9200/blog/_settings?pretty
{
  "blog" : {
    "settings" : {
      "index" : {
        "creation_date" : "1577954953969",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "jTUukj5PQ-eZoMo3GSVO8w",
        "version" : {
          "created" : "7050199"
        },
        "provided_name" : "blog"
      }
    }
  }
}

ずいうわけで、むンデックスの方に出おいるデヌタサむズstore.sizeは、プラむマリヌシャヌドずレプリカシャヌドの倀を
足したものですね。

蚈算のベヌスずしおは、プラむマリヌシャヌドの合蚈倀を芋お必芁ずなるデヌタサむズを芋おみればよいでしょう。

別パタヌンずしお、シャヌド数ずレプリカ数を倉曎したものも詊しおみたしょう。

今のむンデックスを1床削陀。

$ curl -XDELETE localhost:9200/blog
{"acknowledged":true}

シャヌド数9、レプリカ数2でむンデックスを䜜成。

$ curl -XPUT "localhost:9200/blog" -H 'Content-Type: application/json' -d '{ "settings" : { "index" : { "number_of_shards" : 9, "number_of_replicas" : 2 } } }'
{"acknowledged":true,"shards_acknowledged":true,"index":"blog"}

再床デヌタ登録。

$ python3 mv_export_to_es.py kazuhira-r.hatenablog.com.export.txt

確認。

$ curl localhost:9200/_cat/indices?v
health status index uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   blog  4iAlQWUURxypKkNqmilbsg   9   2       1243            0     41.1mb           14mb

プラむマリヌシャヌドの容量が、1シャヌドの時からちょっず増えおいたすね。トヌタルのサむズずしおも、レプリカ数が2぀になったので、
プラむマリヌシャヌドの玄3倍になっおいたす。

シャヌドの状態は、こんな感じです。

$ curl localhost:9200/_cat/shards?v
index shard prirep state   docs store ip            node
blog  3     p      STARTED  143 1.6mb 192.168.33.13 node-3
blog  3     r      STARTED  143 1.5mb 192.168.33.11 node-1
blog  3     r      STARTED  143 1.6mb 192.168.33.12 node-2
blog  4     r      STARTED  124 1.4mb 192.168.33.13 node-3
blog  4     p      STARTED  124 1.4mb 192.168.33.11 node-1
blog  4     r      STARTED  124 1.4mb 192.168.33.12 node-2
blog  6     p      STARTED  137 1.4mb 192.168.33.13 node-3
blog  6     r      STARTED  137 1.3mb 192.168.33.11 node-1
blog  6     r      STARTED  137 1.3mb 192.168.33.12 node-2
blog  7     r      STARTED  163 1.7mb 192.168.33.13 node-3
blog  7     p      STARTED  163 1.9mb 192.168.33.11 node-1
blog  7     r      STARTED  163 1.7mb 192.168.33.12 node-2
blog  8     r      STARTED  126 1.3mb 192.168.33.13 node-3
blog  8     r      STARTED  126 1.3mb 192.168.33.11 node-1
blog  8     p      STARTED  126 1.5mb 192.168.33.12 node-2
blog  2     r      STARTED  129 1.3mb 192.168.33.13 node-3
blog  2     r      STARTED  129 1.2mb 192.168.33.11 node-1
blog  2     p      STARTED  129 1.3mb 192.168.33.12 node-2
blog  1     r      STARTED  141 1.6mb 192.168.33.13 node-3
blog  1     p      STARTED  141 1.6mb 192.168.33.11 node-1
blog  1     r      STARTED  141 1.4mb 192.168.33.12 node-2
blog  5     r      STARTED  129 1.5mb 192.168.33.13 node-3
blog  5     r      STARTED  129 1.5mb 192.168.33.11 node-1
blog  5     p      STARTED  129 1.5mb 192.168.33.12 node-2
blog  0     p      STARTED  151 1.6mb 192.168.33.13 node-3
blog  0     r      STARTED  151 1.6mb 192.168.33.11 node-1
blog  0     r      STARTED  151 1.5mb 192.168.33.12 node-2

実際に蚈算する時は1シャヌドにデヌタを入れお基準ずなるデヌタサむズを芋぀぀、実際にシャヌド分割しおどれくらい䜙剰に
増えるかを確認しおみるずいった感じになるんでしょうね。

なかなかcat APIに結果が反映されない時は、Refresh APIを䜿っお、むンデックスをリフレッシュするずよいでしょう。

$ curl -XPOST localhost:9200/[むンデックス名]/_refresh
$ curl -XPOST localhost:9200/_refresh

オマケ

最埌に、今回䜜成したスクリプトを茉せお終わりにしたす。 mv_export_to_es.py

from bs4 import BeautifulSoup
from elasticsearch import Elasticsearch
from elasticsearch import helpers
import re
import sys

elasticsearch_hosts = ["192.168.33.11", "192.168.33.12", "192.168.33.13"]
elasticsearch_urls = [ f"http://{host}:9200"  for host in elasticsearch_hosts ]

es = Elasticsearch(elasticsearch_urls)
index_name = "blog"
bulk_size = 100

mv_export_file = sys.argv[1:][0]

body_separator = "-----"
entry_separator = "--------"

with open(mv_export_file, "rt", encoding = "utf-8") as file:
    docs = []
    
    while True:
        line = file.readline()  # AUTHOR

        if line == "":
            break

        title = re.match("TITLE:\s+(.+)", file.readline().strip()).group(1)  # TITLE

        basename = re.match("BASENAME:\s+(.+)", file.readline()).group(1).replace("/", "")  # BASENAME
        status = re.match("STATUS:\s(.+)", file.readline().strip()).group(1)  # STATUS
        file.readline()  # ALLOW COMMENT
        file.readline()  # CONVERT BREAKS
        m = re.match("DATE:\s+(\d\d)/(\d\d)/(\d\d\d\d) (\d\d:\d\d:\d\d)", file.readline())  # DATE
        datetime = f"{m.group(3)}-{m.group(1)}-{m.group(2)} {m.group(4)}"

        categories = []

        while True:
            line = file.readline()

            if line.startswith("CATEGORY:"):
                category = re.match("CATEGORY:\s+(.+)", line).group(1)
                categories.append(category)
            else:
                # BODY separator(------)
                break

        file.readline()  # BODY

        body = ""

        while True:
            line = file.readline()

            if line.strip() == body_separator:
                maybe_contents = line

                line = file.readline()

                if line.strip() == entry_separator:
                    break
                elif line.strip() == "COMMENT:":  # skip comment
                    while True:
                        line = file.readline()

                        if line.strip() == entry_separator:
                            break

                    break
                else:
                    body += maybe_contents
                    body += line
            else:
                body += line

        soup = BeautifulSoup(body, "html.parser")
        body_text = soup.get_text()

        doc = {
            "_index": index_name,
            "_id": basename,
            "_source": {
                "title": title,
                "datetime": datetime,
                "categories": categories,
                "body": body_text,
                "status": status
            }
        }

        docs.append(doc)

        if len(docs) >= bulk_size:
            helpers.bulk(es, docs)
            docs = []

    helpers.bulk(es, docs)  # last

Pythonでの、文字列曞匏化の方法をメモする

䜿おうず思った時に忘れおいるので、メモずしお。

Python 3.6以降には、3぀の文字列曞匏化を実珟する方法がありたす。

7. 入力と出力 — Python 3.6.10 ドキュメント

f-stringはPython 3.6から远加されたものです。たた、printf 圢匏の文字列曞匏化に぀いおは、「叀い文字列曞匏蚭定方法」ずされお
いるので、f-stringたたはstr.formatを䜿った方がよいのでしょう。

printf 圢匏の文字列曞匏化のドキュメントにも、そのように曞いおありたす。

printf 圢匏の文字列曞匏化

泚釈 ここで述べる曞匏化挔算には様々な癖があり、よく間違いの元になっおいたす (タプルや蟞曞を正しく衚瀺できないなど)。新しい フォヌマット文字列リテラル や str.format() むンタフェヌスの方が間違いにくく、より匷力で、柔軟で、さらに拡匵可胜です。

速床面では、叀いスタむルの方が有利な感じも 。

pythonのf-stringとformatとパーセント%の書式の速度の比較 - Qiita

ずはいえ、Python 3.6以䞊であればf-stringでいいのかなぁず思いたす。䜿いやすいですし。

ずいうわけで、このあたりをたずめおいきたす。

環境

今回の環境は、こちら。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:    18.04
Codename:   bionic

Pythonは、3.6.9です。

$ python3
Python 3.6.9 (default, Nov  7 2019, 10:44:02) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

曞匏化に取り蟌む倀ずしおは、このあたりを甚意しお詊しおいきたす。

>>> my_name = "Taro"
>>> int_value = 15
>>> int_value2 = 1000000
>>> float_value = decimal_value = 195.786235
>>> dic = {"name": "Taro", "age": 15}
>>> from datetime import datetime
>>> now = datetime(2020, 1, 1, 17, 30, 0)

f-string

たずは、f-stringから。

フォヌマット枈み文字列リテラルf-string

f-stringは、「f」たたは「F」が接頭蟞に付いた文字列リテラルです。文字列䞭に、波括匧{}を䜿うこずで倀を埋め蟌んだり、
フォヌマットしたりするこずができたす。

{}の䞭は、匏ずしお評䟡されるようです。

シンプルな䟋は、こちら。

>>> f"Hello {my_name}"
'Hello Taro'

>>> f"int value = {int_value}"
'int value = 15'

>>> f"float value = {float_value}"
'float value = 195.786235'

>>> f"dict = {dic}"
"dict = {'name': 'Taro', 'age': 15}"

>>> f"now = {now}"
'now = 2020-01-01 17:30:00'

蟞曞のキヌを指定したり、オブゞェクトのネストした情報にアクセスする堎合。

>>> f"name = {dic['name']}, age = {dic['age']}"
'name = Taro, age = 15'

>>> f"year = {now.year}, month = {now.month}, hour = {now.hour}"
'year = 2020, month = 1, hour = 17'

フォヌマットを指定する堎合は、str.formatず同じものが䜿えるようです。

曞匏指定文字列の文法

フィヌルドの曞き方が以䞋のようになり、

フォヌマット枈み文字列リテラルf-string

replacement_field ::= "{" f_expression ["!" conversion] [":" format_spec] "}"

曞匏の仕様はこのようになりたす。

format_spec ::= [[fill]align][sign][#][0][width][grouping_option][.precision][type]

぀たり{倉数名や匏:format_spec}のような指定方法ずなりたす。

いく぀か䟋を。

### 先頭0埋め
>>> f"int value = {int_value:05}"  # widthを指定
'int value = 00015'

>>> f"int value = {int_value:05d}"  # widthずtypeを指定
'int value = 00015'


### カンマ区切り
>>> f"comma separate = {int_value2:,}"  # grouping_optionを指定
'comma separate = 1,000,000'


### 16進数
>>> f"int value = {int_value:x}"  # typeを指定
'int value = f'

>>> f"int value = {int_value:#x}"  # 別圢匏#ずtypeを指定
'int value = 0xf'


### 小数点以䞋の桁数を指定
>>> f"float value = {float_value:.3f}"  # precisionずtypeを指定
'float value = 195.786'

>>> f"float value = {float_value:010.3f}"  # widthを指定しお、0埋めず䜵甚
'float value = 000195.786'


### 日付曞匏指定
>>> f"now = {now:%Y-%m-%d %H:%M:%S}"  # https://docs.python.org/ja/3.6/library/datetime.html#strftime-strptime-behavior
'now = 2020-01-01 17:30:00'

{}の䞭は匏ずしお評䟡されるので、挔算させるこずもできたす。

>>> f"int value * 2 = {int_value * 2}"
'int value * 2 = 30'

f-string内で{}を䜿う堎合は、{{}}ず重ね合わせればOKです。

>>> f"name = {{my_name}}"
'name = {my_name}'

その他、巊詰め、右詰め、䞭倮寄せalignずかもできたりしたす。

なお、f-stringは、ネストさせる{}の䞭に{}を䜿うこずも可胜です。

>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}"  # nested fields
'result:      12.35'

最埌に、倉換フィヌルドの䟋にも觊れおおきたしょう。

>>> f"Hello {my_name}"
'Hello Taro'


>>> f"Hello {my_name!r}"  # r倉換フィヌルドを指定
"Hello 'Taro'"

「!r」の堎合は、reprを呌び出したす。この他、strを呌び出す「!s」、asciiを呌び出す「!a」がありたす。

指定可胜なtype型、align、signなどはたくさんあるので、すべおをここで網矅するようなこずはしたせん。あずは、ドキュメントを
適宜参照、ず。

f-stringは、これくらいで。

str.format

続いおは、str.format。

str.format

基本的にはf-stringず同じですが、いく぀か違いがあるようです。

f-stringの時ず同じく、シンプルな䟋を。

>>> "Hello {}".format(my_name)

>>> "int value = {}".format(int_value)
'int value = 15'

>>> "float value = {}".format(float_value)
'float value = 195.786235'

>>> "dict = {}".format(dic)
"dict = {'name': 'Taro', 'age': 15}"

>>> "now = {}".format(now)
'now = 2020-01-01 17:30:00'

{}の䞭は、むンデックスを指定するこずで眮換察象ずなる倉数の指定をコントロヌルするこずができたす。

>>> "int value = {1}, float value = {0}".format(float_value, int_value)
'int value = 15, float value = 195.786235'

蟞曞のキヌを指定したり、オブゞェクトのネストした情報にアクセスする堎合。

>>> "name = {0[name]}, age = {0[age]}".format(dic)
'name = Taro, age = 15'


>>> "year = {0.year}, month = {0.month}, hour = {0.hour}".format(now)
'year = 2020, month = 1, hour = 17'

f-stringず異なり、蟞曞のキヌを指定する時にクォヌトを加えるず゚ラヌになりたす。

>>> "name = {0['name']}, age = {0['age']}".format(dic)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: "'name'"

キヌワヌド匕数が䜿えるようなので、こういう指定方法でもよさそうです。

>>> "name = {name}, age = {age}".format(name = dic['name'], age = dic['age'])
'name = Taro, age = 15'

>>> "name = {name}, age = {age}".format(**dic)
'name = Taro, age = 15'

>>> "year = {year}, month = {month}, hour = {hour}".format(year = now.year, month = now.month, hour = now.hour)
'year = 2020, month = 1, hour = 17'

曞匏指定の䟋。フォヌマットは、

曞匏指定文字列の文法

に埓いたす。

### 先頭0埋め
>>> "int value = {:05}".format(int_value)  # widthを指定
'int value = 00015'

>>> "int value = {0:05}".format(int_value)  # むンデックスも指定
'int value = 00015'

>>> "int value = {:05d}".format(int_value)  # widthずtypeを指定
'int value = 00015'


### カンマ区切り
>>> "comma separate = {:,}".format(int_value2)  # grouping_optionを指定
'comma separate = 1,000,000'


### 16進数
>>> "int value = {:x}".format(int_value)  # typeを指定
'int value = f'

>>> "int value = {:#x}".format(int_value)  # 別圢匏#ずtypeを指定
'int value = 0xf'


### 小数点以䞋の桁数を指定
>>> "float value = {:.3f}".format(float_value)  # precisionずtypeを指定
'float value = 195.786'

>>> "float value = {:010.3f}".format(float_value)  # widthを指定しお、0埋めず䜵甚
'float value = 000195.786'


### 日付曞匏指定
>>> "now = {:%Y-%m-%d %H:%M:%S}".format(now)  # https://docs.python.org/ja/3.6/library/datetime.html#strftime-strptime-behavior
'now = 2020-01-01 17:30:00'

str.formatの{}の䞭は、匏ずしお評䟡するこずはできたせん。

>>> "int value * 2 = {0 * 2}".format(int_value)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: '0 * 2'

{}の゚スケヌプ。

>>> "Hello {{0}}".format(my_name)
'Hello {0}'

フォヌマットはf-stringの時ず同じなので、巊寄せなどのalignも利甚できたす。

format_spec ::= [[fill]align][sign][#][0][width][grouping_option][.precision][type]

倉換フィヌルド。

>>> "Hello {}".format(my_name)
'Hello Taro'


>>> "Hello {!r}".format(my_name)  # r倉換フィヌルドを指定
"Hello 'Taro'"

str.formatは、こんなずころで。

printf 圢匏の文字列曞匏化

最埌は、printf 圢匏の文字列曞匏化。

printf 圢匏の文字列曞匏化

䞀応抌さえおおきたしょう。

この圢匏は、文字列䞭に指定子、文字列の埌の%に続けお倉数を指定しお䜿甚したす。

芋た方がいい気がしたす。

>>> "Hello %s" % my_name
'Hello Taro'

>>> "int value = %d" % int_value
'int value = 15'

>>> "float value = %f" % float_value
'float value = 195.786235'

>>> "dict = %s" % dic
"dict = {'name': 'Taro', 'age': 15}"

>>> "now = %s" % now
'now = 2020-01-01 17:30:00'

蟞曞のキヌを指定したり、オブゞェクトのネストした情報にアクセスする堎合。

>>> "name = %(name)s, age = %(age)d" % dic
'name = Taro, age = 15'

>>> "year = %(year)d, month = %(month)d, hour = %(hour)d" % {"year": now.year, "month": now.month, "hour": now.hour}
'year = 2020, month = 1, hour = 17'

たあ、蟞曞にしおください、ずいうこずですね 。

曞匏の指定䟋。

### 先頭0埋め
>>> "int value = %05d" % int_value
'int value = 00015'


### 16進数
>>> "int value = %xd" % int_value
'int value = fd'

>>> "int value = %#xd" % int_value
'int value = 0xfd'


### 小数点以䞋の桁数を指定
>>> "float value = %.3f" % float_value
'float value = 195.786'

>>> "float value = %010.3f" % float_value
'float value = 000195.786'

こちらは、このくらいで。