Upgrade to Pro — share decks privately, control downloads, hide ads and more …

1日で基本が身につく! Python超入門

1日で基本が身につく! Python超入門

私が技術評論社から出版したPythonの入門書をベースとしたトレーニング資料です。
出版元の承諾をえたうえで400P近いスライドにして公開します。
企業の自社研修や大学/社会人の勉強会などに利用してもらって構いませんが、再販などの営利利用はお控えください。
後半にはおまけ資料としてプログラミングのレベルマップとレベル向上法および、駆け出しエンジニア向けにインフラエンジニアの世界をまとめています。

yuichi

May 20, 2020
Tweet

More Decks by yuichi

Other Decks in Programming

Transcript

  1. Python3 無料トレーニング資料 (技術評論社公認) 内容 • 解説資料(1-2時間) x 8章 • 演習

    x 8章 • おまけ(プログラミング上達法) オリジナルの書籍 副読本もしくは次のステップにご利⽤ください!! Amazon: たった1⽇で基本が⾝に付く! Python超⼊⾨
  2. はじめに • この資料は技術評論社より出版されている「たった1⽇で基本 が⾝に付く! Python超⼊⾨(ISBN978-4-7741-9112-6)」を著者 (伊藤裕⼀)が出版社の許諾を得てトレーニング資料として公開 しております。 • 個⼈だけでなく企業や組織も利⽤できますが、本資料⾃体や改 変したものを営利⽬的(販売、有償トレーニング)で利⽤するこ

    とは禁⽌いたします。⼤学での講義や社内トレーニングでの利 ⽤は問題ありませんが、当資料を改変して出⾃を不明にするこ とはお控えください。 • 本資料内で利⽤している図などの著作権は技術評論社及び著者 に帰属します。本資料外で利⽤することはお控えください。
  3. 広告(1): 技術評論社(書籍出版元) • DockerとKubernetesの書籍を2020年夏に販売 • コンテナの基礎に加えて開発技法やコンテナベースアプリケー ションのCI/CD環境の構築なども扱う 書籍表紙絵 (あとで更新) ⽬次

    • Dockerを使ってみよう • イメージの利⽤と開発を体験しよう • ネットワークとストレージ • Dockerfileでイメージ作成しよう • Composeを使ってマルチコンテナアプリを作ろう • DockerアプリでCI/CDしよう • Kubernetes⼊⾨ • Kubernetesの利⽤
  4. 広告(2): Nutanix(トレーニング資料作成) • プライベートクラウドの構築ソフトウェア開発会社 • AWS類似のクラウドをオンプレミス(⾃社内)に数時間で構築可能 • ご相談は「https://www.nutanix.com/jp」へ • インフラ⾃動化やDevOps系は著者が⽀援可能

    ネットワーク機器 (L2/L3スイッチ) サーバー サーバー サーバー サーバー クラスタ(冗⻑化と障害時の⾃動回復) 簡単な構築作業 1. サーバーをラックに設置 2. 物理スイッチにケーブル接続 3. 構築ツールで⾃動構築 構築ツール (VMアプライアンス) GUIでIP設定など (約10分) アプライアンスがサーバーを検出して インストール指⽰後は⾃動で構築(約1時間) • ブラウザ管理画⾯(⽇本語) • ハイパーバイザー • ファイルサーバー • ブロックストレージ • オブジェクトストレージ • バックアップ • マネージドKubernetes • マネージドデータベース • パブリッククラウド連携 • マイクロセグメンテーション • VDI(仮想デスクトップ基盤) • 監視、その他 ほぼフルスタックなクラウド機能 使いやすい操作画⾯(REST APIサポート)
  5. 著者について • Nutanix社のソリューションスペシャリスト(DevOps)。開発 + インフラ屋 • 得意領域 • ソフトウェア開発およびDevOps(CI/CD) •

    コンテナとオーケストレーション • モダンアプリケーション開発と⾃動化 • パブリック/プライベートクラウド • ネットワーク(L2/L3) • サーバー仮想化(VMWare, KVM) • サーバーOS(Linux) • 略歴(英語): LinkedIn(@yuichiito) • 副業で技術書や記事の執筆。企業や⼤学向け技術資料作成をしています • 仕事依頼はメールで[email protected]にお願いします 出版社様へ。中級者を想定したモダンなLinuxの使い⽅の本を書きたいです。 Twitter(@yuichi110)
  6. Pythonが苦⼿なこと • 超⾼速性が求められるシステムやデスクトップアプリケーション(⼀ 般ユーザー向けの再配布)、組み込み機器の開発が苦⼿。多くのスク リプト系⾔語も全く同じ領域が苦⼿ • ⾼速な処理(科学計算系はライブラリが⾼速なので問題なし)。⼀般 的にインタプリタ型はコンパイラ型に⽐べると実⾏時にコンパイル するので遅い。速度は⾔語より設計やアルゴリズムに起因すること が多いので、⾔語の速度はプログラミング上級者になってから気に

    すれば問題なし • Pythonがインストールされている環境でPythonアプリを利⽤するこ とは簡単だが、Pythonがインストールされていない環境(⼀般ユー ザーのPC)で動かすのは不可能ではないものの⼿間がかかる。 • 組み込み機器のような機械に近いレイアはPythonが使えないことが 多い 。基本は汎⽤OSの上でPythonを使う
  7. 汎⽤性の⾼さもPythonの魅⼒ • プログラミング⾔語には得意苦⼿がある • 様々なことが得意な⾔語を学べば何にでもその⾔語を使える • 利⽤⽬的が限られる⾔語は⽬的ごとに複数⾔語の習得が必要 • C⾔語: OSに近い領域や組み込み向けのプログラミングが得意

    • Python • 使い捨ての短い簡単なスクリプトでも使える • ⼤規模なシステムの開発でも使える • ウェブサービスの開発でも使える • 科学計算(機械学習/ビッグデータ処理など)でも使える
  8. プログラミング⾔語の分類とPython • プログラミング⾔語は以下の3種類に分類できる • ⼿続き型⾔語(オブジェクト指向型⾔語のベース) • オブジェクト指向型⾔語(現在の主流。Pythonも) • 関数型⾔語 •

    ただしPythonは⼿続き型のような書き⽅もできるし、関数型の ような書き⽅(中上級者向け)もできる ⼿続き型 ⾔語 オブジェクト 指向型⾔語 関数型 ⾔語 Python
  9. Windows: PowershellでPythonを起動する • 「インタラクティブシェル」は対話形式でPythonを利⽤する操 作法で「REPL(Read Eval Print Loop)」やとも呼ばれる • Windows左下の検索ボックスに「powershell」と⼊⼒して

    PowerShell(コマンドラインアプリ)を起動 • Powershellで半⾓にて「python」と⼊⼒することで起動する • インストール時にパスを通し忘れていると「pythonというコマ ンドは存在しない」といった旨のエラー。再インストール。
  10. コラム: Pythonとバージョン • バージョン: Python X.Y という形式。Xがメジャーバージョン (Windows7, 10などのレベル)でYがマイナーバージョン(リリー ス時期やパッチなどのレベル)。

    • Python2: ⼀般普及した最初のメジャーバージョン。新規開発は 停⽌している。バージョン2で開発されたシステムのメンテ作 業は2020年代中盤あたりまでは継続することが予想される。⽇ 本語の扱いが苦⼿ • Python3: 現在普及しているメジャーバージョン。今から学ぶな らPython3をやるべき。新規開発も当然ながらPython3を使う • Python2と3は似ているが⽂法やライブラリが異なる別⾔語
  11. インタラクティブシェルで⾜し算、引き算 • インタラクティブシェルの挙動 1. 「>>>」に続けて実⾏したいことを書く 2. Enter(Return)キーを押す 3. Pythonが書かれていることを解釈する 4.

    Pythonが実⾏結果を画⾯に表⽰する 5. ステップ1に戻る $ python Python 3.8.2 (v3.8.2:7b3ab5921f, Feb 24 2020, 17:52:18) [Clang 6.0 (clang-600.0.57)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> 5 + 3 8 >>> 7 - 2 5 注意: MacはPython3コマンドで起動
  12. • インタラクティブシェルを⽴ち上げて、「10 x 2」の計算を実 施してください • 「5 ÷ 2」の計算を実施してください •

    「5 + 2 * 2」と「(5 + 2) * 2」の計算結果の違いを確認し、な ぜ結果が異なるか説明してください • Python2とPython3の違いについて説明してください 演習
  13. 変数とは (3/3) • 変数に新しい値を代⼊(「再代⼊」とよばれる)すると、古い値 は上書きされてなくなる • 変数Aに変数A(⾃分⾃⾝)や変数Bの値を代⼊することもできる >>> abc =

    51 >>> abc + 5 56 >>> abc = 8 >>> abc 8 >>> abc = abc + 3 >>> abc 11 すでに存在している変数に新しい値を代⼊(再代⼊) 変数が持つ値は新しい値で上書きされる 変数に変数の値を代⼊することもできる。 左は「abc = 8 + 3」と同じ意味
  14. 変数の⽤語 • 変数を作成すること => 「宣⾔」する • 変数に値をいれること => 「代⼊」する •

    すでに変数を持つ変数に値をいれること => 「再代⼊」する • 変数に値を代⼊する記号 => 「代⼊演算⼦」。「=」のこと • 変数から値を取り出すこと => 変数が値を「返す」 • これらの⽤語はプログラマの常識なので、知っていないと専⾨ 書籍を読んだりプログラマと会話する際に困ります。覚えて⾃ 分も使うことをおすすめします。
  15. 変数名の規則 • 変数に使える記号は「アルファベットと数字及びアンダーバー (_)」が基本。⽇本語も使えるがおすすめしない。 • 「必ずアルファベットから始まる」というルールがある • Pythonでは変数に⼤⽂字を使わないのが⼀般的 • 特別な意味を持つ予約後(たとえばforなど)は使えない

    • 変数名にはなにが⼊っているか分かりやすいものを使う >>> abc = 3 >>> 5ab = 3 File "<stdin>", line 1 5ab = 3 ^ SyntaxError: invalid syntax >>> price = 100 >>> text_length = 10 問題のある変数名はエラーとなる 上記のような分かりやすい名前が望ましい。 意味を持つ⼩⽂字英数字で単語の区切りを アンダーバーとするのが⼀般的なPythonの 変数の名付けかた。 この命名規則は「スネークケース」と呼ばれる
  16. 値の変わらない定数 • ⽬的の分からない値のことを「マジックナンバー」と呼ぶ • マジックナンバーやプログラムのパラメーター(たとえば処理を 何秒おきに呼び出すか)などは「定数」で宣⾔するのが⼀般的 • 定数は「⼤⽂字英数字とアンダーバーを使う」のが⼀般的 • プログラムファイル内で定数を使っているとパラメーター変更

    時のコード変更箇所を減らせる • 変数と異なり「定数は内部の値が変化しない」特徴がある >>> 100 * 1.08 108.0 >>> TAX_RATE = 1.08 >>> 100 * TAX_RATE 108.0 100に1.08をかけている理由が分からない。 これなに? 1.08が定数 TAX_RATE に代⼊されていると、 100に消費税 1.08 をかけていると⼀⽬瞭然。 宣⾔部を1.1に変えれば消費税率の変更にすぐ対応できる
  17. ⽂字列の演算 • ⽂字列も数値と同じように演算できる • ただし⾜し算(結合)はできるが、引き算はできない • 数値の「123」と⽂字列の「'123'」はデータの種類が異なるの で同じではない >>> 'abc'

    + '123' 'abc123' >>> '123' + '456' '123456' >>> 123 + 456 579 >>> a = '123' >>> a += '456' >>> a '123456' >>> 456 - 123 333 >>> '456' - '123' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for -: 'str' and 'str' ⽂字列の123は数値計算されていない
  18. ⽇本語の⽂字列 • Python3では⽇本語の⽂字列も使える。Python2は⽇本語が苦⼿ • アルファベットと異なり「⽂字コード(Shift-JISやUTF-8など)」 に気を配る必要がある • Python3のデフォルト⽂字コードはUTF-8なので「Pythonでは常 にUTF-8を使う」とルール化するのが⼀般的 •

    Shift-JISなどを含んだ⽇本語の詳細は7章で扱う $ python3 >>> 'あいうえお' + 'かきくけこ' 'あいうえおかきくけこ' $ python2 >>> 'あいうえお' + 'かきくけこ' '\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81 \x88\xe3\x81\x8a\xe3\x81\x8b\xe3\x81\x8d\xe3\ x81\x8f\xe3\x81\x91\xe3\x81\x93' MacでのPython3とPython2の⽇本語の扱い
  19. 関数 • 「受け取った値」にたいして「決められた処理」をおこない、 「結果を返す」仕組みをプログラミングでは「関数」と呼ぶ • 「結果を受け取る変数 = 関数名(関数に与える値)」という書式 で使うのが⼀般的 •

    関数に与える値のことを「引数」と呼び、関数から返される値 を「返り値」と呼ぶ • 関数の詳細は第3章で扱う >>> print(15) 15 >>> print('hello world') hello world print関数は与えられた値をコンソール出⼒する
  20. type関数を使った型の確認 • Pythonは「あまり型にうるさくない⾔語」 • メリット: 気軽にコーディングをおこなえる • デメリット: 変数に⼊っている型が想定していたものと違う といったトラブルがある

    • 「type 関数」を使うことで変数の型を確認できる • 拘束性は⾼くないが変数名に型を宣⾔することも可能(割愛) >>> a = 10 >>> type(a) <class 'int'> >>> a = 'hello' >>> type(a) <class 'str'> 数値を代⼊した変数aに⽂字列を再代⼊できる。 型にうるさい⾔語(C⾔語やJava)ではできない。 type関数に変数(そのなかの値)を与えると型がわかる。 intは「整数型」のこと
  21. キャストを使った型の変換(2/2) • ⽂字列型への変換には「str 関数」を使う • 整数型への変換には「int 関数」を使う • ⽂字列から数値へのキャストは変換できれば成功し、変換でき ない場合は失敗する

    >>> price = 100 >>> str(price) + ' yen' '100 yen' >>> int('100') 100 >>> int('hello') Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid literal for int() with base 10: 'hello' 数値を⽂字列型にキャストしてから 別の⽂字列と結合 ⽂字列から数値へのキャストは失敗することがある
  22. コラム: シングルクオートとダブルクオート • ダブルクオートの⽂字列内ではシングルクオートが使え、シン グルクオートの⽂字列内ではダブルクオートが使える。 • そのため、以下の使い分けがおすすめ • 通常時: シングルクオート(Pythonでは⼀般的なため)

    • ⽂字列内にシングルクオートを持つ場合: ダブルクオート • エスケープシーケンス(後述)を使うことでシングルクオートの ⽂字列内でシングルクオートを使うこともできる。ダブルク オートも同様 >>> 'hello I'm Taro' File "<stdin>", line 1 'hello I'm Taro' ^ SyntaxError: invalid syntax >>> "hello I'm Taro" "hello I'm Taro" >>> 'hello I\'m Taro' "hello I'm Taro" >>>
  23. プログラムファイルを作って実⾏する • このセクションで学ぶこと • IDLE(標準の統合開発環境)の起動⽅法 • 新規ファイルの作成と保存⽅法 • IDLEでのプログラムの実⾏⽅法 •

    わざとエラーを発⽣させてエラー内容を読む • コメント • コンソールからプログラムを実⾏ • コラム: 複数⾏にわたる⽂字列 SECTION 01
  24. IDLEでプログラムを実⾏ • IDLEのプログラムのエディタを選択(クリックして前⾯に) • 以下のいずれかの⽅法で実⾏ • メニューバーの「<Run> -> <Run Module>」

    • F5ボタン(MacだとFnボタンを押しながら)を押す • インタラクティブシェルと異なりprint関数などで意図的に出⼒ させないとどのような動きをしているか⾒えない
  25. エラーを発⽣させて確認する • プログラムを開発していると、勘違いや⼊⼒ミスで必ずエラー は発⽣する • エラーを特定して問題箇所の修正を繰り返すことでプログラム はきちんと動作するようになる • わざとエラーを発⽣させてエラーの読み⽅を確認してみる print(1)

    print('2') a = 1 + 2 print(b) 1 2 Traceback (most recent call last): File "/Users/yuichi/Desktop/python/test_02.py", line 4, in <module> print(b) NameError: name 'b' is not defined エラーメッセージから「どのファイル(test_02.py)」の 「どの⾏(line 4)」で 「どのような問題(NameError: name ʻbʼ is not defined)」が おきているかわかる /chapter2/test_02.py コンソール出⼒
  26. コメント • 「コメント」: プログラム中の実⾏されない特別なテキスト • プログラムの説明(なにをしているか注釈)に利⽤したり、実⾏ してほしくない箇所をコメント化する使い⽅が⼀般的 • コメントの⽅法 •

    「#(シャープではなくハッシュと読む)」の後ろ(1⾏) • 「'''」もしくは「"""」で囲まれた複数⾏のテキスト #print(1) print('2') ''' a = 1 + 2 print(b) ''' 2 /chapter2/test_03.py コンソール出⼒ コメントした箇所が実⾏されていないので、 コンソール出⼒されていないことがわかる。 さきほどのエラー箇所も無視されている。 プログラムをコメント化することを 「コメントアウト」という
  27. 条件分岐の仕組みを理解する • このセクションで学ぶこと • 絶対値を求めるabs関数 • abs関数の条件分岐 • if⽂を使った条件分岐 •

    インデントによるコードブロック • Bool型 • ⽐較演算⼦ • Bool値を扱う演算⼦ • 複雑なif⽂ SECTION 02
  28. if⽂を使った条件分岐 • Pythonで条件分岐を使うには「if」を使う • ifに続けて「条件式(TrueかFalseが得られる式)」を書く • 次の⾏以降でインデント(字下げ)して処理を書く • 注意: 他のプログラミング⾔語のようにifのあとに()で条件式を

    囲んでも問題ないが、Pythonでは囲まないのが⼀般的 a = -5 if a < 0: a = a * -1 print(a) if 条件式: 条件式を満たす場合の処理 /chapter2/abs_01.py $ python3 abs_01.py 5 コンソール出⼒ If⽂の⽂法
  29. ⽐較演算⼦ (1/2) • True/Falseを得るための演算⼦ • 制御構⽂の条件式などでよく利⽤される • 数字以外でも⽂字列などでも使える 演算⼦ 意味

    使⽤例(Trueの場合) == 左辺と右辺が等しいときにTrueを返す 20 == 20 != 左辺と右辺が等しくないときにTrueを返す 20 != 30 > 左辺が右辺より⼤きいときにTrueを返す 30 > 20 >= 左辺が右辺以上のときにTrueを返す 30 >= 20, 30 >= 30 < 左辺が右辺より⼩さいときにTrueを返す 20 < 30 <= 左辺が右辺以下のときにTrueを返す 20 <= 30, 20 <= 20
  30. ⽐較演算⼦ (2/2) • 異なる型での⽐較などもできる • ⽂字列の⽐較などはCやJavaと挙動が異なるので注意(Pythonは ポインタではなく値で⽐較するので分かりやすい) >>> 4 ==

    4.0 True >>> 4 == '4' False >>> 'abc' > 'def' False >>> 'hello' == 'hello' True >>> 'hello' == ('hel' + 'lo') True コンソール出⼒ ⽂字列の⽐較はユニコードの 順番で決まっている。 aはdより前にあるので⼩さい
  31. Bool値を扱う演算⼦ • 条件式ではBool値の反転や「かつ」「または」などもよく使う • 「and」演算⼦: 左辺と右辺の値がともにTrueの時にTrueを返す • 「or」演算⼦: 左辺と右辺の値のどちらかがTrueの時にTrueを返す •

    「not」演算⼦: True/Falseを逆転させる >>> True and True True >>> True and False False >>> True or True True >>> True or False True >>> not True False >>> not False True and演算⼦の確認 or演算⼦の確認 not演算⼦の確認
  32. 複雑なif⽂ • 「elif」は 前⽅のifかelifに合致しなかったときに条件チェック される分岐。好きなだけ繰り返せる • 「else」は前⽅のifとelifの全てに合致しなかったときに呼び出 される処理を書く a =

    15 if a % 2 == 0: print('2') elif a % 3 == 0: print('3') elif a % 5 == 0: print('5') else: print('nothing') $ python3 if_elif_else.py 3 /chapter2/if_elif_else.py コンソール出⼒
  33. • 有名なFizzBuzz問題のプログラムを作成する • 変数 a に整数を任意の数を与える • 変数 a が3で割り切れれば「Fizz」と出⼒

    • 変数 a が5で割り切れれば「Buzz」と出⼒ • 変数 a が3でも5でも割り切れれば「FizzBuzz」と出⼒ • ヒント: 「1章で学んだ剰余で余りが0になること」が「割り 切れる」ということ • 変数aを様々な値に変更して挙動を確認してください 演習
  34. ループ処理で繰り返しを記述する • このセクションで学ぶこと • リストのデータを準備する • リストのデータを取り出す • リストのデータを上書きする •

    リスト⻑を取得する • リストのデータをまとめて処理するforループ • Breakによるループ打ち切り • Continueによるループの周回スキップ • Continueの便利な利⽤法 • 条件が満たされる間はループを繰り返すwhile⽂ SECTION 03
  35. リストのデータを準備する • Pythonのループでは「リスト型」の値をよくつかう • リスト型は⼀覧(リスト)となっているデータを扱うための型 • 「要素」はリストの中⾝のデータ(数値や⽂字列など)のこと • リストの要素には順序がある >>>

    ['taro', 'jiro', 'saburo'] ['taro', 'jiro', 'saburo'] >>> [] [] >>> a = ['taro', 'jiro', 'saburo'] >>> print(a) ['taro', 'jiro', 'saburo'] >>> リストの宣⾔は⼤かっこ([])に要素を並べる 要素がひとつもない場合は[]のみ リスト内の要素の順序は保たれる
  36. リストのデータを上書きする • 「リスト[インデックス番号] = 代⼊する値」という形式でイン デックス番号を指定して要素に代⼊することもできる • リストの⻑さを超えたインデックスの参照は取得でも代⼊でも エラーとなる >>>

    a = ['taro', 'jiro', 'saburo', 10] >>> a[1] = 'hanako' >>> a[3] = 'adam' >>> print(a) ['taro', 'hanako', 'saburo', 'adam'] 存在しないインデックスの参照はエラーとなる >>> a = ['taro', 'jiro', 'saburo', 10] >>> a[4] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list index out of range >>> a[4] = 100 Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list assignment index out of range リストの要素をインデックスで指定して代⼊すると その要素が代⼊値で上書きされる
  37. リスト⻑を取得する • リスト⻑: リストの要素の数のこと • 「len関数」でリスト⻑を取得できる • 「len関数の取得値-1までインデックスでアクセスができる」と 覚えておく >>>

    a = ['taro', 'jiro', 'saburo', 10] >>> len(a) 4 >>> a[3] 10 >>> a[4] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list index out of range len関数の確認と要素へのアクセス len関数の取得値でアクセス 可能なインデックス値の最⼤値が わかる
  38. リストのデータをまとめて処理するforループ • リストに代表される「シーケンス(連なりのあるデータ)」構造 • 「for」ループでシーケンスを前から後ろに順番に処理できる name_list = ['taro', 'jiro', 'saburo',

    'shiro', 'goro'] for name in name_list: print(name) $ python3 forloop.py taro jiro saburo shiro goro for 変数 in シーケンス: 処理 /chapter2/forloop.py コンソール出⼒ for⽂の⽂法 ループするごとにシーケンスの要素を前から後ろに変数に代⼊する シーケンスの最後までたどり着くとループが終了 ブロックの使い⽅はifと同じ
  39. breakによるループ打ち切り • 「break」命令でループ処理を打ち切る • ループを継続する理由がなくなった場合に利⽤される • 例: 整数のリスト中に偶数があるかをチェック。(偶数を⾒つけ たら残りのリストの要素はチェック不要なのでbreak) a

    = [5, 9, 11, 3, 6, 5, 11, 4, 9] has_even = False for i in a: print('checking: ' + str(i)) if i % 2 == 0: has_even = True break print('has even: ' + str(has_even)) $ python3 forloop_break.py checking: 5 checking: 9 checking: 11 checking: 3 checking: 6 has even: True /chapter2/forloop_break.py コンソール出⼒ リストの6より後ろはbreakされたので 出⼒されていない
  40. continueの便利な利⽤版 • breakの利⽤難易度は低いが、continueは使い慣れない⼈が多い • ループでの条件分岐のインデント階層を減らすのに便利 for i in a: if

    条件1: if 条件2: if 条件3: 処理 for i in a: if 条件1: continue if 条件2: continue if 条件3: continue 処理 Continueなし Continueあり
  41. 条件が満たされる間はループを繰り返すwhile⽂ • 「while」ループで条件式が満たされる限り繰り返す • 他のプログラミング⾔語ではwhileは多⽤されるが、Pythonで はforループを可能な限り使うこと。forが使えない場合にwhile ⽂を検討する a = [5,

    9, 11, 3] length = len(a) i = 0 while i < length: print('index ' + str(i) + ' : ' + str(a[i])) i += 1 $ python3 while_loop.py index 0 : 5 index 1 : 9 index 2 : 11 index 3 : 3 /chapter2/while_loop.py コンソール出⼒ while 条件式: 処理 while⽂の⽂法 条件式がTrueとなる限りループを継続する ブロックの使い⽅はifやforと同じ
  42. • Forループで以下の⾝⻑の平均値、最⼤値、最⼩値を求めてく ださい。ヒント: 合計値と最⼤値と最⼩値のそれぞれの変数を ループ前に定義して、ループを回るたびにその値を更新する • 180cm • 170cm •

    160cm • 165cm • 175cm • 余裕がある⼈は10進数を2進数(⽂字列)に変換するプログラム をwhileループで作成してください。検索するとアルゴリズム がでてくるはずです。2進数は⽂字列の複合代⼊演算⼦(結合)で 作成してください。 演習
  43. 役割(2) - 複雑な処理を簡単に実現する(1/3) • アルゴリズムを⾃分で書けば複雑な処理も実現できる • ⼀般的な処理はPythonが関数などを提供しているので、それを 使うほうが「簡単」「バグが少ない」「速い」 例: リストの要素のソートの実装

    選択ソートというアルゴリズム 1. リストの最⼩値を探す 2. 0番⽬の要素とスワップ 3. 1番⽬以降で最⼩値を探す 4. 1番⽬の要素とスワップ 5. … 6. 要素の最後までたどり着けばソート終了 変数間の値の交換は ⼀時的な変数が必要
  44. 役割(2) - 複雑な処理を簡単に実現する(2/3) • 現時点の知識でのソートアル ゴリズムの実装(難しい) • 変数の値の交換処理には⼀時 変数が必要 a

    = [5,9,4,1,8] length = len(a) i = 0 while(i < length): # i番⽬以降の最⼩の要素を探す minimum_index = i j = i + 1 while j < length: if a[j] < a[minimum_index]: minimum_index = j j += 1 # i番⽬と最⼩の要素をスワップする if minimum_index != i: tmp = a[i] a[i] = a[minimum_index] a[minimum_index] = tmp # i番⽬のループが終わった際のリスト print(str(i) + ': ' + str(a)) i += 1 print('sorted: ' + str(a)) $ python3 sort_list_01.py 0: [1, 9, 4, 5, 8] 1: [1, 4, 9, 5, 8] 2: [1, 4, 5, 9, 8] 3: [1, 4, 5, 8, 9] 4: [1, 4, 5, 8, 9] sorted: [1, 4, 5, 8, 9] コンソール出⼒ /chapter3/sort_list_01.py
  45. 役割(2) - 複雑な処理を簡単に実現する(3/3) • sorted関数で昇順ソート(⼩さいものから⼤きいものへ)が提供さ れている • ⾃作のソート処理より以下の点で優れる • プログラムの⾒た⽬がシンプル

    • ⾼速(選択ソートより賢いアルゴリズムがC⾔語で実装される) • バグが発⽣する可能性が低い a = [5,9,4,1,8] b = sorted(a) print(b) $ python3 sort_list_02.py [1, 4, 5, 8, 9] コンソール出⼒ /chapter3/sort_list_02.py ⾃作の20⾏以上のプログラムがたった1つの 関数呼び出しに置き換えられた
  46. 役割(3) - コードの重複の排除 • 同⼀の処理をコピーペーストで何度も書くのはよくない • ソースコードが無駄に⻑くなって読みにくくなる • バグやコードの修正が発⽣すると⾯倒だしトラブルのもと •

    関数化されていれば「⾒やすい」し「変更(修正)しやすい」 a = 5 if a < 0: a *= -1 # 何か関係ない処理 b = -3 if b < 0: b *= -1 print(a) # 5 print(b) # 3 a = 5 a = abs(a) # 何か関係ない処理 b = -3 b = abs(b) print(a) # 5 print(b) # 3 /chapter3/get_abs_01.py /chapter3/get_abs_02.py
  47. 関数の概念の復習 • 関数の仕事 1. 「引数」でデータを受け取る 2. 受け取ったデータで処理を⾏う 3. 結果を「返り値」として呼び出し元に返す •

    絶対値を求めるabs関数の例 1. 引数で数値を受け取る 2. 受け取った数値を絶対値にする(マイナスであればプラス に変換する) 3. 絶対値を呼び出し元に返す 処理 (絶対値の算出) 引数による⼊⼒(数値) 返り値による出⼒(絶対値) 関数
  48. 関数を定義する • 関数を定義する⽂法ルールがある • 引数は関数内で変数として利⽤される。再代⼊も可能 def my_abs(x): if x <

    0: x *= -1 return x def 関数名(引数1, 引数2, ...): 処理1 処理2 return 返り値 絶対値を求める関数の定義例 • 関数名: my_abs • 引数: x • 処理: if⽂でxを絶対値にする • 返り値: 処理されたx /chapter3/define_function_02.py 関数を実装する⽂法 「return⽂」で呼び出し元に値を返す 「def⽂」で関数呼び出しの定義 • 関数名 • 引数 処理は関数のブロックで定義する
  49. ⾃分で定義した関数を利⽤する • ⾃作関数の利⽤法はPythonが提供する関数(printなど)と同じ • 関数名に()をつけ、引数として渡す値や変数を()内に並べる • 関数の定義より前で関数を呼び出すとエラーになる def my_abs(x): if

    x < 0: x *= -1 return x a = my_abs(3) print(a) print(my_abs(-5)) print(my_abs(-5)) def my_abs(x): if x < 0: x *= -1 return x $ python3 define_function_04.py Traceback (most recent call last): File "define_function_04.py", line 1, in <module> print(my_abs(-5)) NameError: name 'my_abs' is not defined $ python3 define_function_03.py 3 5 コンソール出⼒ コンソール出⼒ /chapter3/define_function_03.py /chapter3/define_function_04.py 呼び出し側で渡した引数3が 関数側の引数xに代⼊される。 引数の数は同じにすること returnの 返り値が 戻される
  50. 関数の命名ルール • 関数名の⽂法的なルールは変数名と同じ • アルファベットの⼩⽂字と数字をアンダーバーで区切る • ⼤⽂字の利⽤も可能だがPythonらしくない名前 • 「関数名は動詞から始める」というのが⼀般的なルール def

    add_something(x): ... def get_something(x): ... def something_adder(x): ... def something_getter(x): ... def addSomething(x): ... def getSomething(x): ... よい変数名の例 Pythonらしくない変数名の例
  51. コラム: 変数の定義と参照の順序 • 関数と同様に変数も「定義前に参照するとエラー」となる • プログラムがどのような流れで読み込まれて処理されるかを意 識すればトラブルを避けやすい print(a) a =

    5 $ python3 define_variable.py Traceback (most recent call last): File "define_variable.py", line 1, in <module> print(a) NameError: name 'a' is not defined コンソール出⼒ /chapter3/define_variable.py
  52. • FizzBuzz問題を関数化してください • FizzBuzz⾃体の説明は2章の演習を参照してください • 引数は1つで整数を受け取ります • 返り値は「 " Fizz

    " 」「 " Buzz " 」「 " FizzBuzz " 」「 " " 」のいずれかの⽂字列とします • forループで「0から20までリストの各要素」にたいして FizzBuzzの関数を使い、結果を1つずつprintしてください • 数値を受け取り、その数値が偶数であればTrue, 奇数であれば Falseを返す is_even 関数を作成してください。ヒント: 偶数と 奇数の判定は2で割り切れるかを剰余計算(%記号)で求める 演習
  53. 関数の引数と返り値を⾃在に操る • このセクションで学ぶこと • 返り値の有無によるコードの違い • 複数のreturn⽂を記述する • 関数に複数の引数を定義する •

    キーワード引数を定義する • デフォルト引数 • 可変⻑引数を利⽤する • コラム: help関数で関数の詳細を確認する SECTION 03
  54. 返り値の有無によるコードの違い • returnが定義されていないと「None(何もないという値)」を返す • returnに変数や値が与えられていないときもNoneを返す • 「Noneを返す」ということは「関数の返り値がない」と同じ意味 def my_print1(x): print('my

    print1: ' + str(x)) def my_print2(x): print('my print2: ' + str(x)) return a = my_print1(5) print(a) b = my_print2(5) print(b) $ python3 return_value_01.py my print1: 5 None my print2: 5 None コンソール出⼒ /chapter3/return_value_01.py
  55. 関数に複数の引数を定義する • 関数の引数は好きな数だけ使うことができる • 引数を受け取らない関数は引数を定義しない • 「定義した引数の数」と「呼び出し時の引数の数」が異なると エラーが発⽣ def fun0():

    print('fun0') def fun1(arg1): print('fun1') def fun5(arg1, arg2, arg3, arg4, arg5): print('fun5: arg1 is ' + arg1) fun0() fun1(1) fun5('a', 'b', 'c', 'd', 'e') $ python3 return_value_03.py fun0 fun1 fun5: arg1 is a コンソール出⼒ /chapter3/return_value_03.py
  56. キーワード引数を定義する • 今まで利⽤してきた引数は正式には「位置引数」と呼ばれる。定 義された引数の「順序」通りに引数を与える使い⽅ • 「キーワード引数」は 定義された引数名を指定して引数を与える • 引数の数が多い場合はキーワード引数だと順番間違いがない def

    test(arg1, arg2, arg3): print(str(arg1) + str(arg2) + str(arg3)) # 位置引数 test(1, 2, 3) # キーワード引数 test(arg2='a', arg1='b', arg3='c') $ python3 keyword_argument_01.py 123 bac コンソール出⼒ /chapter3/keyword_argument_01.py 関数定義の引数名を指定して 値を渡している
  57. 可変⻑引数を利⽤する • 与える引数が固定でない関数の引数を「可変⻑引数」と呼ぶ • 可変⻑引数を⾃分で定義するのは初⼼者向けではないが、既存 関数で使う場⾯は多いので使いかたは知っておくこと • 「スプラット演算⼦(*)」を使うとリストを可変⻑引数に使える print('hello') print('hello',

    'python') print(1, 2, 3, 4, 5, 6, 7, 8, 9) $ python3 variable_arguments_01.py hello hello python 1 2 3 4 5 6 7 8 9 print([1, 2, 3, 4, 5, 6, 7, 8, 9]) print(*[1, 2, 3, 4, 5, 6, 7, 8, 9]) $ python3 variable_arguments_02.py [1, 2, 3, 4, 5, 6, 7, 8, 9] 1 2 3 4 5 6 7 8 9 コンソール出⼒ コンソール出⼒ /chapter3/variable_arguments_01.py /chapter3/variable_arguments_02.py
  58. コラム: help関数で関数の詳細を確認する • 「help関数」の引数に関数名をあたえることで、その関数の詳 細を確認することができる • 引数などを忘れた場合にインタプリタですぐに確認できる >>> help(print) Help

    on built-in function print in module builtins: print(...) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream. help関数を使ったprint関数の詳細確認
  59. 関数の中にある変数の扱い (1/2) • 関数の外では関数内で定義された変数を使えない(アクセスでき ない) • アクセスできない変数を参照しようとするとエラーが発⽣ def test(x): y

    = x + 1 return y print(test(5)) print(y) $ python3 name_space_02.py 6 Traceback (most recent call last): File "name_space_02.py", line 6, in <module> print(y) NameError: name 'y' is not defined コンソール出⼒ /chapter3/name_space_02.py
  60. 関数型 • Pythonの関数も「関数型(概念)」の値(具体的なデータ) • print関数は関数型の値の1つであり、len関数も同様 • type関数に関数を与えることで関数型である確認ができる • 関数型の値は変数に代⼊でき、変数経由で関数を呼び出せる。 上級者向けのプログラミングテクニックでよく使う

    >>> type(abs) <class 'builtin_function_or_method'> >>> def my_abs(x): ... if x < 0: ... x *= -1 ... return x ... >>> type(my_abs) <class 'function'> >>> fun = abs >>> fun(-5) 5 コンソール出⼒ 組み込み関数と⾃作関数の型の確認。厳密には違うが区別しなくてよい
  61. 関数を受け取る関数 • 関数Aの引数として関数Bを渡す場合がある。関数Aの挙動を関 数Bで調整する。関数を受け取る関数を「⾼階関数」と呼ぶ。 • たとえばソートの基準を関数として定義し、その基準通りに ソートを実施するなど a = [5,

    -7, 0, 9, -3] print(sorted(a)) a = [5, -7, 0, 9, -3] def my_abs(x): if x < 0: x *= -1 return x print(sorted(a, key=my_abs)) $ python3 sort_argument_01.py [-7, -3, 0, 5, 9] $ python3 sort_argument_02.py [0, -3, 5, -7, 9] コンソール出⼒ コンソール出⼒ /chapter3/sort_argument_01.py /chapter3/sort_argument_02.py keyはキーワード引数(デフォルト付き)の指定
  62. コラム: 制御構⽂の名前空間 • Pythonではifやforなどの制御⽂のブロックで宣⾔した変数は、 そのブロック外でも参照できる • 他のプログラミング⾔語では参照できないものが多いので注意 def fun(x): if

    x == 1: y = 5 else: y = 10 print(y) fun(1) $ python3 test.py 5 test.py コンソール出⼒ 変数yは条件分岐の構⽂内で宣⾔されているので、 print⽂があるブロック(関数)より内側にある。 問題なくアクセスできている
  63. • Pythonの名前空間の動きを説明してください • 関数内から関数外の変数のアクセス • 関数外から関数内の変数のアクセス • global宣⾔の効果 • なぜglobal宣⾔が推奨されないか

    • 関数で関数外の変数を更新したいときはどのような関数とす るのが望ましいか • sorted関数に⾃作関数を渡して降順ソート(⼤きいものから ⼩さいものへ)を実現してください 演習
  64. for⽂で便利なrange関数 • 「range関数」で数字の連番(0,1,2...など)を作れる • 利⽤法1: 引数を1つ与えて0からその値-1までの連番を作る • 利⽤法2: 引数を1つ与えて「1つめの引数の値」から「2つめの 引数の値

    - 1」までの連番を作る • 「N回ループする」ときにfor⽂と利⽤法1の組み合わせが便利 a = range(5) print(a) print(list(a)) for i in range(2,5): print(i) $ python3 range_01.py range(0, 5) [0, 1, 2, 3, 4] $ python3 range_02.py 2 3 4 コンソール出⼒ コンソール出⼒ /chapter3/range_02.py /chapter3/range_01.py
  65. • range関数とforループを使って指定された数だけ「hello」と出 ⼒する関数を作成してください • 正と負の整数を持つリストをfilter関数と⾃作関数を使って正の 整数のみを抽出したリストにしてください。たとえば[5, -3, 8, -1, 4]を与えると[5,

    8, 4]が得られます • map関数を使って数値を含むリストを⽂字列を含むリストに変 換してください。たとえば[5, -3, 8, -1, 4]を与えると['5', '-3', '8', '-1', '4']が得られます 演習
  66. メソッドでListインスタンスを操作する (1/2) • 「メソッド」はインスタンスを操作するための特別な関数 • インスタンス(値そのものか変数)の後ろに「.(ドット)」を置き、 続けてメソッド名とメソッドの引数を記述する • appendメソッド: 対象のインスタンスに要素を追加

    • reverseメソッド: 対象のインスタンスの要素の順序を反転 a = ['ab', 'cd', 'ef'] print(a) # リストに値を追加 a.append('gh') print(a) $ python3 object_02.py ['ab', 'cd', 'ef'] ['ab', 'cd', 'ef', 'gh'] a = ['ab', 'cd', 'ef', 'gh'] a.reverse() # リストの要素を反転 print(a) $ python3 object_03.py ['gh', 'ef', 'cd', 'ab'] コンソール出⼒ コンソール出⼒ /chapter4/object_02.py /chapter4/object_03.py
  67. メソッドの副作⽤と返り値 • 「副作⽤」は操作によりオブジェクトの状態が変化すること • メソッドには以下のパターンが存在する • 副作⽤はあるが、返り値はない(Listのappendメソッド) • 副作⽤はなく、返り値がある(Listのcountメソッド) •

    副作⽤も返り値もある(Listのpopメソッド) • 副作⽤も返り値もない(呼び出す意味がないので普通はない) a = [1,3,5,3,1,3] b = a.count(3) print(b) print(a) $ python3 object_05.py 3 [1, 3, 5, 3, 1, 3] a = [1,3,5,3,1,3] b = a.pop(0) print(b) print(a) $ python3 object_06.py 1 [3, 5, 3, 1, 3] コンソール出⼒ コンソール出⼒ /chapter4/object_05.py /chapter4/object_06.py 副作⽤なし 返り値あり 副作⽤あり 返り値あり
  68. コラム: dir関数を使ったオブジェクトのメソッド確認 • どの型がどういったインスタンスを使えるか記憶しきれない • dir関数を使うことでオブジェクトの属性(5章)を確認できる • メソッドも属性の⼀部なので確認可能 >>> dir([])

    ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] コンソールで属性(メソッド)の⼀覧を確認
  69. リストオブジェクトを操作するメソッド (1/4) • append: リスト末尾に要素を追加する • insert: 指定した位置に要素を挿⼊する a =

    ['a', 'b', 'c', 'd', 'e'] a.append('hello') print(a) a = ['a', 'b', 'c', 'd', 'e'] a.insert(3, 'hello') print(a) $ python3 list_method_01.py ['a', 'b', 'c', 'd', 'e', 'hello'] $ python3 list_method_02.py ['a', 'b', 'c', 'hello', 'd', 'e'] コンソール出⼒ コンソール出⼒ /chapter4/list_method_01.py /chapter4/list_method_01.py
  70. リストオブジェクトを操作するメソッド (2/4) • remove: 指定した要素を削除する。インデックス指定ではなく 消したい値を指定するので注意 • reverse: リストの順序を反転させる a

    = ['a', 'b', 'c', 'd', 'e'] a.remove('c') print(a) $ python3 list_method_03.py ['a', 'b', 'd', 'e'] a = ['a', 'b', 'c', 'd', 'e'] a.reverse() print(a) $ python3 list_method_04.py ['e', 'd', 'c', 'b', 'a'] コンソール出⼒ コンソール出⼒ /chapter4/list_method_03.py /chapter4/list_method_04.py
  71. リストオブジェクトを操作するメソッド (3/4) • clear: リストの要素を全削除して空にする • count: リストに指定した要素がいくつあるか数える a =

    ['a', 'b', 'c', 'd', 'e'] a.clear() print(a) a = ['a', 'b', 'c', 'b', 'a'] count = a.count('b') print(count) $ python3 list_method_06.py 2 $ python3 list_method_05.py [] コンソール出⼒ コンソール出⼒ /chapter4/list_method_05.py /chapter4/list_method_06.py
  72. リストオブジェクトを操作するメソッド (4/4) • index: 指定した値を持つ要素のインデックス番号を取得 • pop: リストからインデックスを指定して要素を取り出して削除 a =

    ['a', 'b', 'c', 'd', 'e'] index = a.index('d') print(index) a = ['a', 'b', 'c', 'd', 'e'] value = a.pop(0) print(value) print(a) $ python3 list_method_07.py 3 $ python3 list_method_08.py a ['b', 'c', 'd', 'e'] コンソール出⼒ コンソール出⼒ /chapter4/list_method_07.py /chapter4/list_method_08.py
  73. その他のリスト操作 (1/2) • 「in演算⼦」で指定した値がリストに含まれていればTrueを返す • 「del⽂」で指定したインデックス番号の要素を削除 a = 'c' in

    ['a', 'b', 'c', 'd'] print(a) print('c' in ['a', 'b', 'd']) a = ['a', 'b', 'c', 'd', 'e'] del a[3] print(a) $ python3 list_operator01.py True False $ python3 list_operator02.py ['a', 'b', 'c', 'e'] コンソール出⼒ コンソール出⼒ /chapter4/list_operator_01.py /chapter4/list_operator_02.py
  74. その他のリスト操作 (2/2) • 「スライス」でリストの要素の⼀部分を取り出せる。元のリストに は変化なし • a[x:y] : リストaのインデックス番号xからy-1の要素のリストを取得 •

    a[:y] : インデックス番号0からy-1までの要素のリストを取得 • a[x:] : インデックス番号のxから最後までの要素のリストを取得 • a[:] : リストaをコピーしたリストを取得 a = ['a', 'b', 'c', 'd', 'e'] b = a[1:3] print(b) c = a[:3] print(c) $ python3 list_operator03.py ['b', 'c'] ['a', 'b', 'c'] コンソール出⼒ /chapter4/list_operator_03.py
  75. ⽂字列型のメソッドはオブジェクトを変化させない (1/2) • upper: ⽂字列を⼤⽂字にした「新しい⽂字列を返す」 • replace: ⽂字列を置き換えた「新しい⽂字列を返す」 • 上記2つのメソッドを呼び出してもインスタンスは変化しない

    a = 'Hello Python' b = a.upper() print('Original a : ' + a) print('Return val b : ' + b) a = 'Hello Python' c = a.replace('Hello', 'Hell') print('Original a : ' + a) print('Return val c : ' + c) $ python3 string_method_01.py Original a : Hello Python Return val b : HELLO PYTHON $ python3 string_method_02.py Original a : Hello Python Return val c : Hell Python コンソール出⼒ コンソール出⼒ /chapter4/string_method_01.py /chapter4/string_method_01.py
  76. ⽂字列型のメソッド (2/5) • startswithメソッド: 引数の⽂字列で開始されていればTrue • endswithメソッド: 引数の⽂字列で終了していればTrue print('Hello Python'.startswith('thon'))

    print('Hello Python'.startswith('Hello')) print('Hello Python'.endswith('thon')) print('Hello Python'.endswith('Hello')) $ python3 string_method_04.py False True $ python3 string_method_05.py True False コンソール出⼒ コンソール出⼒ /chapter4/string_method_04.py /chapter4/string_method_05.py
  77. ⽂字列型のメソッド (3/5) • replaceメソッド: 第1引数の⽂字列を第2引数の⽂字列で置き 換えた⽂字列を返す • lowerメソッド: ⽂字列を全て⼩⽂字にした⽂字列を返す •

    upperメソッド: ⽂字列を全て⼤⽂字にした⽂字列を返す print('Hello Python'.replace('Python', 'World')) print('Hello Python'.replace('Pyth0n', 'World')) print('Hello Python'.replace('Python', '')) $ python3 string_method_06.py Hello World Hello Python Hello >>> a = 'Hello World' >>> a.lower() 'hello world' >>> a.upper() 'HELLO WORLD' >>> a.lower() == 'hello world' True lowerもしくはupperで⼤⽂字⼩⽂字を 気にせずに⽂字列の合致判定ができる コンソール出⼒ lowerとupperメソッドの確認 /chapter4/string_method_06.py
  78. ⽂字列型のメソッド (4/5) • stripメソッド: ⽂字列の前後の空⽩(半⾓空⽩、改⾏、タブな ど)を取り除く • joinメソッド: リストの要素を⽂字列で連結する print('

    hello\n') print(' hello\n'.strip()) list3 = ['taro', '180', '80'] text = ','.join(list3) print(text) $ python3 string_method_07.py hello hello $ python3 string_method_10.py taro,180,80 コンソール出⼒ コンソール出⼒ /chapter4/string_method_07.py /chapter4/string_method_08.py
  79. ⽂字列型のメソッド (5/5) • splitメソッド: ⽂字列を特定の⽂字列で分解して⽂字列のリス トにする • CSV形式をコンマで分けたり、改⾏コードで複数⾏のテキスト を⾏ごとに分解する処理によく使われる text

    = 'taro, 180, 80' list1 = text.split(',') print(list1) text = 'taro, 180, 80' list1 = text.split(',') def apply_strip(text): return text.strip() list2 = list(map(apply_strip, list1)) print(list2) $ python3 string_method_08.py ['taro', ' 180', ' 80'] $ python3 string_method_09.py ['taro', '180', '80'] コンソール出⼒ コンソール出⼒ /chapter4/string_method_08.py /chapter4/string_method_09.py
  80. • 不変オブジェクトについて例を使って説明してください • ⽂字列型のformatメソッドでテンプレートを埋めてください。 「'hello {}'」 • ⽂字列型のlowerを使って⽂字列からbool値を得る関数を作成 してください •

    引数が⼤⽂字⼩⽂字を問わず「true」であればTrue • それ以外は全てFalse • replaceメソッドで⽂字列「"Hello I'm Yuichi"」の名前を⾃分の 名前に変えてください。 • メソッドチェーンで「'hello world python'」を1⾏で 「['HELLO', 'WORLD', 'PYTHON']」にしてください 演習
  81. その他のデータ型を理解する • このセクションで学ぶこと • タプルの概要 • タプルの利⽤⽅法と利⽤例 • タプルからタプルへの代⼊(アンパック代⼊) •

    タプルやリストを扱う関数 • セットの概要 • セット型の操作 • 辞書型の概要 • 辞書型の操作 • 辞書型のメソッド • コラム: 基本的な型と制御がプログラミングの中⼼ SECTION 04
  82. タプルの利⽤⽅法 • 少カッコ()内に各要素をコンマ区切りで並べる。リストと同じ • 要素へのアクセスはインデックス番号を使う • 要素は上書きできない • 要素数は変更できない taro

    = ('taro', 180, 80) print(type(taro)) print(taro[1]) $ python3 tuple_02.py <class 'tuple'> 180 >>> taro = ('taro', 180, 80) >>> taro[0] = 'jiro' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment >>> del taro[1] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object doesn't support item deletion インタプリタでの挙動確認 /chapter4/tuple_02.py コンソール出⼒
  83. タプルの利⽤例 • 表の列(構成が決まっている)をタプルとする • 表の⾏(⻑さが変わる)をリストとする taro = ('taro', 180, 80)

    jiro = ('jiro', 170, 70) saburo = ('saburo', 160, 60) list1 = [taro, jiro, saburo] sum_height = 0 for person in list1: sum_height += person[1] print(sum_height/len(list1)) $ python3 tuple_03.py 170.0 /chapter4/tuple_03.py コンソール出⼒
  84. タプルからタプルへの代⼊ • 「アンパック代⼊」を使うとタプルに含まれる変数群に右側の タプルの要素をまとめて代⼊できる • 左辺のカッコは省略できる。例「a, b, c = (1,

    2, 3)」 • for⽂の変数や関数の返り値にタプルを使うことが多い (name, height, weight) = ('taro', 180, 80) print(height) taro = ('taro', 180, 80) jiro = ('jiro', 170, 70) saburo = ('saburo', 160, 60) list1 = [taro, jiro, saburo] sum_height = 0 for (name, height, weight) in list1: sum_height += height print(sum_height/len(list1)) $ python3 tuple_04.py 180 $ python3 tuple_05.py 170.0 /chapter4/tuple_04.py /chapter4/tuple_05.py コンソール出⼒ コンソール出⼒
  85. タプルやリストを扱う関数 • 「enumerate関数」でリストの要素をタプル形式にしてイン デックス番号を与える • 「zip関数」で複数のリストを束ねてタプル形式のリストにする list1 = ['a', 'b',

    'c', 'd', 'e'] enum_object = enumerate(list1) print(list(enum_object)) list1 = ['a', 'b', 'c'] for (index, item) in enumerate(list1): print('{} : {}'.format(index, item)) $ python3 tuple_06.py [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')] $ python3 tuple_07.py 0 : a 1 : b 2 : c /chapter4/tuple_06.py /chapter4/tuple_07.py コンソール出⼒ コンソール出⼒ enumerateはforループでインデックス値が必要な時に便利
  86. セットの概要 • 順序と重複がない複数の要素を保持するデータ構造 • 要素を追加、削除することができるが、Listと異なり同じ要素 を2つ持つことはできず、要素の順序もない • set()で空のセットオブジェクトを作成する >>> set1

    = set() >>> set1.add('apple') >>> print(set1) {'apple’} >>> set1.add('banana') >>> print(set1) {'banana', 'apple’} >>> set1.add('apple') >>> print(set1) {'banana', 'apple'} インタプリタでの挙動確認(詳細は次ページより)
  87. セット型の操作 (1/2) • セットオブジェクトの作成⽅法 • set() • {'elem1', 'elem2', 'elem3'}

    • 宣⾔⽅法が後述する辞書型とほぼ同じなので注意。特に「{}」 はセット型ではなく辞書型のオブジェクトが⽣成されるのはよ く間違えるポイント >>> set1 = set() >>> type(set1) <class 'set'> >>> set2 = {'elem1', 'elem2', 'elem3'} >>> type(set2) <class 'set'> インタプリタでの挙動確認
  88. セット型の操作 (2/2) • addメソッド: 要素を追加 • removeメソッド: 要素を削除 • popメソッド:

    要素をランダムに取り出し(取り出すとなくなる) • in演算⼦: 要素が存在するかチェック。リストより⾼速 >>> set1 = {'A', 'B', 'C'} >>> 'A' in set1 True >>> 'D' in set1 False >>> set1.remove('B') >>> set1 {'C', 'A’} >>> a = set1.pop() >>> a 'C’ >>> set1 {'A'} インタプリタでの挙動確認
  89. 辞書型の概要 • セットと似ているが要素は「キー(key)」と「バリュー (value)」を持つ。キーは重複できないがバリューは重複できる • キーを指定してバリューを取得/更新する • 同じキーで要素を追加するとバリューが上書きされる >>> fruits_dict

    = dict() >>> fruits_dict['apple'] = 'red' >>> fruits_dict['banana'] = 'yellow' >>> fruits_dict {'apple': 'red', 'banana': 'yellow'} >>> fruits_dict['apple'] 'red' インタプリタでの挙動確認(詳細は次ページより)
  90. 辞書型の操作 (1/3) • 辞書オブジェクトの作成⽅法 • dict() • {} • {key1:value1,

    key2:value2, ...} • セット型と使う記号が同じ中括弧。違いは要素がキーとバ リューのペアになっていることのみ • キーの型は⽂字列が⼀般的だが他の型(数値など)も使える >>> fruits_dict = {'apple':'red', 'banana':'yellow'} >>> type(fruits_dict) <class 'dict’> >>> fruits_dict2 = {} >>> type(fruits_dict) <class 'dict'> インタプリタでの挙動確認
  91. 辞書型の操作 (2/3) • 辞書オブジェクトからのバリューの取得 • 書式: OBJECT[KEY] • 辞書オブジェクトへの要素の追加 •

    存在しないキー: 新しいキーとバリューのペアが登録される • 存在するキー: キーの対となるバリューが更新される • 書式: OBJECT[KEY] = VALUE >>> fruits_dict = {'apple':'red', 'banana':'yellow'} >>> fruits_dict['apple'] 'red' >>> fruits_dict['apple'] = 'green' >>> fruits_dict['apple'] 'green' インタプリタでの挙動確認
  92. 辞書型の操作 (3/3) • in演算⼦: キーの存在確認 • 存在しないキーのバリューを取得しようとするとエラーになる • キーの存在が不明な場合は事前にin演算⼦か後述のgetメソッド を使うこと

    >>> fruits_dict = {'apple':'red', 'banana':'yellow'} >>> 'apple' in fruits_dict True >>> 'grape' in fruits_dict False >>> fruits_dict['grape'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'grape' インタプリタでの挙動確認
  93. 辞書型のメソッド (1/4) • keys: キーの⼀覧をリスト形式で返す • values: バリューの⼀覧をリスト形式で返す • items:

    「キーとバリューのタプル」をリスト形式で返す • itemsはforループでの利⽤に便利 >>> a = {'apple':'red', 'banana':'yellow'} >>> a.keys() dict_keys(['apple', 'banana']) >>> a.values() dict_values(['red', 'yellow']) >>> a.items() dict_items([('apple', 'red'), ('banana', 'yellow')]) インタプリタでの挙動確認
  94. 辞書型のメソッド (2/4) • 辞書型をforループで全て処理する操作 • for⽂のin演算⼦に辞書オブジェクトを与える: keyでループ • for⽂のin演算⼦にitemsメソッドを与える: keyとvalueでループ

    fruits_dict = {'apple':'red', 'banana':'yellow'} for key in fruits_dict: print(key) fruits_dict = {'apple':'red', 'banana':'yellow'} for (key, value) in fruits_dict.items(): print('{} : {}'.format(key, value)) $ python3 dict_01.py apple banana $ python3 dict_02.py apple : red banana : yellow /chapter4/dict_01.py /chapter4/dict_02.py コンソール出⼒ コンソール出⼒
  95. 辞書型のメソッド (3/4) • get: キーを指定してバリューを取得する。キーが存在しない場 合はデフォルト値を取得 • デフォルト値を指定しない場合はNoneがデフォルト値 • デフォルト値を取得しても元の辞書オブジェクトに変化なし

    • 似た関数にsetdefaultがある(割愛) page_counter = {} value = page_counter.get('/hello.html', 0) print(value) print(page_counter) $ python3 dict_03.py 0 {} /chapter4/dict_03.py コンソール出⼒
  96. 辞書型のメソッド (4/4) • getメソッドを使うことでカウンターのような実装を簡単に実 現できる • 複雑な初期化処理が必要な場合はin演算⼦を使って以下のよう な実装をすることを推奨 • 存在しない場合:

    初期化処理をして辞書オブジェクトに代⼊ • 存在する場合: 辞書オブジェクトから取得 page_counter = {} page_counter['/page_a.html'] = page_counter.get('/page_a.html', 0) + 1 page_counter['/page_a.html'] = page_counter.get('/page_a.html', 0) + 1 page_counter['/page_b.html'] = page_counter.get('/page_b.html', 0) + 1 print(page_counter) $ python3 dict_04.py {'/page_a.html': 2, '/page_b.html': 1} /chapter4/dict_04.py コンソール出⼒
  97. コラム: 基本的な型と制御がプログラミングの中⼼ • Pythonプログラミングの基本は以下となる • ライブラリにある処理はライブラリに任せる • ライブラリを使うプログラムは⾃分で作る(貼り合わせ) • 後者はこの章までに学んだ基本的な型(数値、⽂字列、リスト、

    タプル、セット、辞書型)を基本的な制御(if, for, while)で操作 するのが上級者でもおよそコードの8割以上と思われる • すでに学んだ関数や今後学ぶクラスや例外処理などは上記の ベーシックなプログラムを整理するためのもの。⾼度なプログ ラミングテクニックを学ぶ前に基礎を抑えるのが⼤事 • フルスクラッチで素材から開発するのではなく、ライブラリと いう「レゴの出来合いのパーツ」をスマートに⾃分のコードで 張り合わせて作品を作るのがPython流のプログラミング
  98. コラム: 著者の型の使い分け • タプル: 引数や返り値で値をまとめる • リスト: 可変⻑の複数の要素を管理するために使う。なにかを ループ処理するときに使う第⼀候補。要素探索が遅いので注意 •

    セット: 要素の存在チェックが必要な場合にリスト代替で使う • 辞書型: リストに似た使い⽅だが、要素をすばやく検索できる 必要がある場合に使う。辞書型のキーは「データベースでいう 主キーやインデックス」で、それを使って該当エントリを素早 く抜き出すようなイメージ。Pythonプログラミングでは辞書型 が⾮常に便利なので使いこなそう。 • 余談: 辞書型とListの特徴をあわせ持つ 「順序付き辞書 (OrderedDict)」などの変種もある
  99. • 数値のリストを受け取り、そこから最⼩値と最⼤値を同時に返 す関数 get_min_max を作成し、その関数の返り値の最⼩値と 最⼤値をそれぞれ別の⾏でコンソールに出⼒してください。 • リスト型での要素の探索に⽐べて、セット型での要素の探索が ⾼速である理由をインターネットで調べてください。 •

    数値のリストを受け取り、各数値が何回出現したかをカウント する関数を辞書型を使って作成してください。返り値は{数値1: 出現数1, 数値2:出現数2, ...}という形式とします。 • 上記関数の出⼒をforループでキーとバリューのペアごとに 「'{} : {}'.format(key, value)」というフォーマットでプリント 出⼒してください。 演習
  100. クラスの仕組みと設計⽅法を知る • このセクションで学ぶこと • クラスとは • この章の学習の流れ • クラスとメソッドを定義する •

    ⾃分で定義したクラスのインスタンスを作成 • メソッド定義の第⼀引数selfは⾃動で代⼊される • クラスの命名規則 • インスタンス変数 • コンストラクタでのインスタンス変数の定義 • メソッド間のインスタンス変数の共有 • インスタンス変数の外部からの参照 SECTION 01
  101. この章の学習の流れ • クラスの概念の理解は難所のため、以下の順を追って学ぶ 1. Section1 : クラスとメソッドの定義⽅法 2. Section1 :

    クラスをインスタンス化する⽅法 3. Section1 : データ(インスタンス変数)の定義 4. Section2 : クラスの有無によるプログラムの⽐較 5. Section3 : その他のトピック
  102. クラスとメソッドを定義する • classに続けてクラス名を書く • classのブロック内にメソッドを関数と同じくdefで定義 • メソッドの第⼀引数は常に「self」とする class MyClass: def

    plus(self, a, b): return a + b def minus(self, a, b): return a - b /chapter5/myclass.py クラスの定義 インスタンスが使うメソッドの定義
  103. ⾃分で定義したクラスのインスタンスを作成 • 「クラス名()」でインスタンス化をおこなう • インスタンス.メソッド名()でメソッドの呼び出しを⾏なう • 呼び出し側で引数selfに相当するものを与えていない(後述) class MyClass: def

    plus(self, a, b): return a + b def minus(self, a, b): return a - b myclass = MyClass() a = myclass.plus(5, 3) print(a) b = myclass.minus(5, 3) print(b) $ python3 myclass.py 8 2 /chapter5/myclass.py コンソール出⼒
  104. メソッド定義の第⼀引数selfは⾃動で代⼊される • 引数の数の違い • クラスでの定義: 3つ。 下図のminusは「self, a, b」 •

    メソッドで与える数: 2つ。数のminusは「7, 5」 • Pythonのクラスでのメソッド定義のルール • 第⼀引数はselfとする • selfにはインスタンス⾃⾝が代⼊されている(利⽤法は後述)
  105. クラスの命名規則 • 単語の区切りを⼤⽂字にする • クラス名の例 • MyClass • User •

    BingoMachine • この命名ルールは⼀般的に「キャメルケース」と呼ばれる。 キャメルはラクダで、⼤⽂字⼩⽂字の連なりがラクダのコブの ようにぼこぼこしているため
  106. コンストラクタでのインスタンス変数の定義 • Pythonでインスタンス変数を定義するのは「コンストラクタ」 • コンストラクタは特殊なメソッドで「__init__(self, 初期化のた めの引数)」とし、処理でインスタンス変数の初期化を書く • 「self.インスタンス変数名 =

    初期価値」として変数定義 • コンストラクタはクラスのインスタンス化時に呼び出される class User: def __init__(self, x, y): self.name = x self.age = y def dump(self): print('name:{}'.format(self.name)) print('age:{}'.format(self.age)) ana = User('ana', 30) ana.dump() $ python3 define_instance.py name:ana age:30 /chapter5/define_instance.py コンソール出⼒
  107. インスタンス変数の外部からの参照 • インスタンス変数はメソッド内部だけでなく「インスタンス.イ ンスタンス変数名」としても参照できる • この利⽤法はJavaなどでは⾮推奨とされるがPythonは問題ない ので不必要なセッターやゲッターは作らないこと class User: def

    __init__(self, x, y): self.name = x self.age = y def dump(self): print('name:{}'.format(self.name)) print('age:{}'.format(self.age)) ana = User('ana', 30) print(ana.name) print(ana.age) $ python3 check_instance.py ana 30 /chapter5/check_instance.py コンソール出⼒
  108. • 以下のクラスを作成してください • クラス名: User • 以下のインスタンス変数を持ち、コンストラクタで初期化 • id •

    name • address • 以下のメソッドを持つ • dumps: ⽂字列のformatメソッドを使ってCSV形式で「id, name, address」を⽂字列で返す • 上記クラスをインスタンス化してdumpsの結果を出⼒する 演習
  109. クラスの必要性を理解する • このセクションで学ぶこと • ⽐較題材のプログラムについて • クラスを使わないプログラムの実装 • 中級者向け: 関数内での参照と代⼊の違い

    • クラスを使わないプログラムの実⾏ • クラスを使うプログラムの実装 • なぜクラスが必要か • インスタンスに閉じた副作⽤を使う • 正しいクラスの設計ができたら中級者 SECTION 02
  110. ⽐較題材のプログラムについて • クラスを使うべき理由を具体例(ビンゴマシーン)で確認する • 仕組み • 1-99の番号が振られたボールをランダムに抽出する • 同じボールは⼀度しか出ない •

    全てのボールがなくなったら終了 ボール群をリストとする リストに要素(ボール)を追加 リストの要素(ボール)の順序を シャッフルして1つ取り出す 同じ仕組みをクラスなし/ありで実装
  111. クラスを使わないプログラムの実装 • ビンゴを実現する関数 • initialize: 1-99のボール揃える • get_ball: ランダムにボールを取り出す(ランダム操作は6章) •

    has_ball: まだボールがあるか確認 • プログラムの問題: 引数経由でしかデータ共有ができない import random # ランダムな機能を使うという宣⾔ def initialize(balls1): balls1.clear() # リストを空にする balls1.extend(list(range(1, 100))) # リストに1-99を⼊れる def get_ball(balls1): random.shuffle(balls1) # リストをランダムに並べ替える return balls1.pop() def has_ball(balls1): return len(balls1) != 0 balls2 = [] initialize(balls2) # ボールがあればビンゴを回す while has_ball(balls2): print(get_ball(balls2)) 続き /chapter5/bingo_01.py
  112. クラスを使わないプログラムの実⾏ • /chapter5/bingo_01.py: 2ページ前の正しいプログラム • /chapter5/bingo_02.py: 参照ではなく代⼊する誤ったプログラム $ python3 bingo_01.py

    29 41 93 28 84 ... 32 22 5 49 83 80 13 $ python3 bingo_02.py ボールの初期化に成功している。 そのあとのランダムな取り出し処理も ボールがなくなるまで繰り返す。 ボールの初期化に失敗してリストが空のまま。 取り出すボールがないので取り出し処理はおきない bingo_01.pyの実⾏結果 bingo_02.pyの実⾏結果
  113. クラスを使うプログラムの実装 (1/2) • クラスを使うことでメソッド間でデータ(ビンゴのボール)をイ ンスタンス変数として共有できる • あるメソッドでの変更(たとえば初期化や取り出し)が、別のメ ソッドでも反映される import random

    class Bingo: def __init__(self): self.balls = list(range(1, 100)) def get_ball(self): random.shuffle(self.balls) return self.balls.pop() def has_ball(self): return len(self.balls) != 0 bingo = Bingo() while bingo.has_ball(): print(bingo.get_ball()) 続き /chapter5/bingo_03.py
  114. なぜクラスが必要か ⽐較項⽬ クラスなし クラスあり ⽂法 シンプル 複雑 変数と関数 同じ空間上に多数存在 クラス内の数は少ない

    データの共有 難しい メソッド間はインスタンス変数を使えば簡単 データ構造の定義 タプルやリストなどの組み合わせ クラスで構造を定義し、新しい型を作れる 初期化処理 ⾃分で任意で実⾏ コンストラクタで⾃動で実施 副作⽤の利⽤ 管理しにくいので避けるべき クラス内では積極的に使うべき デバッグ しにくい 整理されているためやりやすい 総合判断 単純なプログラム向け 複雑なプログラムでは必須 • 複雑なプログラムを構造化することで、データや制御の流れをシ ンプルにできるため。難しさをクラス内に押し込める • 逆に⾔えばシンプルなプログラムではクラスを使わなくてもよい 気になる⼈は「カプセル化」というキーワードで調べてみるとよいかも
  115. • ⽂字の出現数(空⽩を含む)をカウントするクラスを作成してく ださい • コンストラクタ: 引数はselfのみでインスタンス変数 self.char_counterに{}を設定する • メソッド •

    add_text: 引数はselfとtext(⽂字列)で、メソッド内でtextを1 ⽂字ごとforループで回す。出現する⽂字を self.char_counterで数える(ヒント: dictのgetメソッドと初期 値を使うと実装が簡単) • get_counts: インスタンス変数 self.char_counter の値を返 す。 演習
  116. クラス変数 • 「クラス変数(定数)」はインスタンスをまたいで共有されるクラス レベルの変数(定数)で、クラス直下に定義する • クラス内で「クラス名.クラス変数名」で参照や代⼊をおこなう class MyClass: abc =

    'abc' DEF_GHI ='def ghi' def print_abc(self): print(MyClass.abc) def set_abc(self, abc): MyClass.abc = abc a = MyClass() b = MyClass() # クラス変数の変更前 a.print_abc() b.print_abc() # クラス変数の変更後 a.set_abc('hello python') a.print_abc() b.print_abc() 続き $ python3 class_var_const.py abc abc hello python hello python /chapter5/class_var_const.py コンソール出⼒
  117. クラスメソッド (1/2) • 「クラスメソッド」はクラス外から直接「クラス名.メソッド名()」 として呼び出せる特別なメソッド • メソッド前に「@classmethod」と「アノテーション」を加える • クラスメソッド内でインスタンス変数や通常のメソッドは使えない class

    MyClass: def __init__(self): self.a = 'aaa' @classmethod def method1(cls): print('class method') def method2(self): print('instance method') # インスタンス化してからクラスメソッドを呼び出す mc = MyClass() mc.method1() mc.method2() # インスタンス化せずにメソッドを呼び出す MyClass.method1() MyClass.method2() 続き /chapter5/method.py
  118. クラスメソッド (2/2) • メソッド呼び出し可否のまとめ • インスタンスでクラスメソッド: 呼び出せる • インスタンスで通常メソッド: 呼び出せる

    • クラス名でクラスメソッド: 呼び出せる • クラス名で通常メソッド: 呼び出せない(下記エラー) • クラスメソッドに似た「スタティックメソッド」もある(割愛) $ python3 method.py class method instance method class method Traceback (most recent call last): File "method.py", line 19, in <module> MyClass.method2() TypeError: method2() missing 1 required positional argument: 'self' コンソール出⼒ クラス名でメソッドを 呼び出そうとしてエラー
  119. クラスの継承 • 「継承」はクラスAの特徴をクラスBに引き継がせる機能 • 継承元を「親クラス」、親を継承する側を「⼦クラス」と呼ぶ • 基本機能を持つ親クラスを⼦クラスが継承して拡張する使い⽅ • ⼦クラスのクラス名の後のカッコに親クラス名を書く class

    ParentClass: def method1(self): print('parent method') class ChildClass(ParentClass): def method2(self): print('child method') cc = ChildClass() cc.method2() cc.method1() $ python3 succession.py child method parent method ⼦クラスが親クラスのメソッドも呼び出せている /chapter5/succession.py コンソール出⼒
  120. コラム: 初⼼者はあまり継承を使わない? • クラスAにクラスBの機能を取り込む際に何も考えずに継承を使うの は間違った使い⽅。継承は「Reader -> TextReader, ImageReader」といったように親を⼦がより詳細化する際に使う • 単純に機能だけを使いたければクラスA内でクラスBを使えばよい

    • 別クラスの機能を取り込むことを「委譲(デリゲーション)」という class ClassA: def __init__(self): self.class_b = ClassB() def use_class_b(self): self.class_b.print_hello() class ClassB: def print_hello(self): print('hello') class_a = ClassA() class_a.use_class_b() /chapter5/define_instance.py $ python3 delegation.py hello コンソール出⼒
  121. モジュールを利⽤する • このセクションで学ぶこと • モジュールによるプログラムの整理 • 組み込みモジュール • 標準ライブラリ •

    外部パッケージ • ⾃作のモジュール • モジュールとクラスの違い • モジュールを使ってみよう • モジュール内のクラスの利⽤ • 複数のモジュールを組み込む • モジュール名を付けずに関数やクラスを呼び出す • コラム: from⽂の問題点 SECTION 01
  122. モジュールを使ってみよう • 数学関係のモジュール「math」(標準ライブラリ)を利⽤する • モジュール利⽤の宣⾔: import モジュール名 • モジュールの関数などの利⽤: モジュール名.関数()

    • 宣⾔はプログラムファイルの先頭周辺で⾏なうのが⼀般的 # mathモジュールの読み込み import math # mathモジュールのceil関数の利⽤ a = math.ceil(5.4) print(a) # mathモジュールのfloor関数の利⽤ print(math.floor(5.4)) $ python3 math_01.py 6 5 モジュールを利⽤する前にimportしないと エラーになる。 プログラムの冒頭でimportするのが慣習 /chapter6/math_01.py コンソール出⼒
  123. モジュール内のクラスの利⽤ • ⽇時を扱うdatatimeモジュールを使う • モジュールのクラスの利⽤: モジュール名.クラス名 import datetime now =

    datetime.datetime.now() print(now) >>> import datetime >>> datetime <module 'datetime' from '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/datetime.py'> >>> datetime.datetime <class 'datetime.datetime'> >>> datetime.datetime.now <built-in method now of type object at 0x10b546678> $ python3 datetime_01.py 2020-04-17 14:44:45.660410 /chapter6/datetime_01.py 「datetime.datetime.now」は「モジュール名.クラス名.メソッド名」 コンソール出⼒
  124. 複数のモジュールを読み込む • 必要なモジュールを1⾏ごと宣⾔するのが⼀般的 • 1⾏にまとめて宣⾔することもできる • 利⽤しないモジュールは宣⾔しないこと import math import

    time, datetime print(datetime.datetime.now()) time.sleep(3) print(datetime.datetime.now()) $ python3 math_datetime_01.py 2020-04-17 14:55:04.546417 2020-04-17 14:55:07.550372 /chapter6/math_datetime_01.py コンソール出⼒
  125. モジュール名を付けずに関数やクラスを呼び出す • fromで宣⾔するとモジュール名を省略して利⽤できる • from モジュール名 import クラス名や関数名 • from

    モジュール名 import *(全て) from math import ceil from time import * print(ceil(5.4)) sleep(1) print(floor(5.3)) $ python3 from_import_01.py 6 Traceback (most recent call last): File "from_import_01.py", line 6, in <module> print(floor(5.3)) NameError: name 'floor' is not defined /chapter6/from_import_01.py コンソール出⼒ importで指定したクラスや関数のみ読み込み モジュールの全てのクラスや関数を読み込み 読み込んでいないmathのfloorを 呼び出そうとしたためエラー
  126. • 「組み込みモジュール」「標準ライブラリ」「外部パッケー ジ」の違いについて説明してください • 標準ライブラリの利⽤演習 • jsonモジュールをimportしてください • 以下のdictを作成してください。{'apple':'red', 'banana':'yellow'}

    • json.dumps(dictオブジェクト)で辞書データを⽂字列型の JSONに変換してください • json.loads(⽂字列型のJSON)で辞書データを得てください • JSONはPythonに限らずマシン間やサービス間でのやりとり に多⽤されるデータ構造なので調べてみてください。 演習
  127. モジュールを作成する • モジュールA(main.py)がモジュールB(myutil.py)を使う • 2つのファイルは同じフォルダに格納する • ファイルがあるフォルダに移動してからプログラムを呼び出し print('main.py start') import

    myutil print('main.py after import') def main_test(): print('main_test() called') main_test() myutil.myutil_test() print('main.py end') print('myutil.py start') def myutil_test(): print('myutil_test() called') print('myutil.py end') $ python3 main.py main.py start myutil.py start myutil.py end main.py after import main_test() called myutil_test() called main.py end /chapter6/main-myutil1/main.py /chapter6/main-myutil1/myutil.py 同じフォルダに使う側と使われる側の Pythonプログラム(モジュール)を置く。 そのフォルダでプログラムを起動 コンソール出⼒
  128. 定義と実⾏コードの分離 (1/2) • エントリポイント以外では定義のみ⾏い処理は実⾏させない • 「if __name__ == ' __main__

    ':」という条件式を使う • 特殊属性 __name__ にはエントリポイントの場合は 「'__main__'」が、それ以外はモジュール名が⼊る • __name__ が'__main__'の場合にのみ処理を実⾏させる import myutil def start(): myutil.myutil_test() # エントリポイントの場合の処理 if __name__ == '__main__': print('main.py is entry point') start() この条件はこのプログラムファイルが エントリポイントの場合だけ満たされる /chapter6/main-myutil2/main.py
  129. 定義と実⾏コードの分離 (2/2) import myutil def start(): myutil.myutil_test() # エントリポイントの場合の処理 if

    __name__ == '__main__': print('main.py is entry point') start() def myutil_test(): print('myutil_test() called') print('__name__ of myutil : ' + __name__) # エントリポイントの場合の処理 if __name__ == '__main__': print('myutil.py is entry point') myutil_test() $ python3 main.py main.py is entry point myutil_test() called __name__ of myutil : myutil $ python3 myutil.py myutil.py is entry point myutil_test() called __name__ of myutil : __main__ /chapter6/main-myutil2/main.py /chapter6/main-myutil2/myutil.py コンソール出⼒ コンソール出⼒ 他から使われるモジュールではエントリポイントを 「モジュールのテストの実施」に使うとよい。 モジュールレベルでの完成度が得られるまでテストする myutilの実⾏処理は動作していない
  130. • モジュール mymath.py を作成して⼩数点を受け取る「切り下 げ: floor」と「切り上げ: ceil」を関数として作成してください。 • ヒント: 切り下げはint型への変換、切り上げは「もとの値と切

    り下げ値が同⼀なら切り下げ値、そうでなければ切り下げ+1」 で実装できます。⾯倒ならmathモジュールを使ってください • 5.1 -> 切り下げ:5, 切り上げ6 • 5.0 -> 切り下げ:5, 切り上げ5 • 等号演算⼦(==)で5.0と5を⽐較するとTrueになる • main.py モジュールで上記のmymathをimportし、切り上げと 切り下げの計算を確認してください • main.pyではエントリポイントとして起動された時だけ上記処 理を実施するために「if __name__ == '__main__': 」を使って ください 演習
  131. 時間関連の処理を⾏うtimeモジュール • 時間を扱う基本的な処理 • time.sleep(x): x秒のスリープ • time.time(): 現在のUNIX時間(世界標準時の1970年1⽉1⽇から 何秒経過したか)を取得する

    • ある作業の前後で時間を取得し差分から実⾏時間を測定できる import time start = time.time() print(start) time.sleep(10) end = time.time() print(end) print(end - start) $ python3 time_01.py 1587107549.013738 1587107559.0178628 10.004124879837036 /chapter6/time_01.py コンソール出⼒
  132. 時間関連の処理を⾏うdatetimeモジュール • 時刻を扱うモジュール。⼈間の時間(2020年4⽉17⽇ 16:00な ど)はtimeではなくdatetimeが使いやすい • datetime.datetime.today(): 現在時刻の取得し、datetimeイン スタンスを返す •

    datetimeインスタンスには様々なメソッドと変数がある $ python3 datetime_02.py 2020-04-17 16:16:31.147644 2020/4/17 16:16:31 import datetime now = datetime.datetime.today() print(now) print('{}/{}/{} {}:{}:{}'.format(now.year, now.month, now.day, now.hour, now.minute, now.second)) /chapter6/datetime_02.py コンソール出⼒
  133. 乱数を⽣成するrandomモジュール • 乱数(ランダムな値)を⽣成するモジュール • random.random(): 0から1のあいだのランダムな⼩数を得る • random.randint(x, y): 引数のあいだにあるランダムな整数を得る

    • random.shuffle(): 引数のリスト要素をランダムに並び替える import random print(random.random()) print(random.randint(0, 100)) import random numbers = list(range(10)) print(numbers) random.shuffle(numbers) print(numbers) $ python3 random_01.py 0.2799910238537131 89 $ python3 random_02.py [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [4, 9, 3, 7, 1, 6, 5, 2, 0, 8] /chapter6/random_01.py /chapter6/random_02.py コンソール出⼒ コンソール出⼒
  134. パッケージをインストールする • Pythonインストール時に付属する「pipコマンド」を使う (Linuxのyumやaptに相当する) • Windows: pip install <パッケージ名> •

    Mac: pip3 install <パッケージ名> PS C: \Users\yito8> pip install requests Collecting requests Using cached requests-2.13.0-py2.py3-none-any.whl Installing collected packages: requests Successfully installed requests-2.13.0 $ pip3 install requests Collecting requests Using cached .../requests-2.23.0-py2.py3-none-any.whl Installing collected packages: requests Successfully installed requests-2.23.0 Windowsでのpipを使ったrequestsのインストール Macでのpipを使ったrequestsのインストール
  135. • pip(pip3)でcowsayパッケージをインストールしてください • インタプリタでcowsay.関数名(⽂字列)を実⾏してください • 関数⼀覧[ʻbeavisʼ, ʻcheeseʼ, ʻdaemonʼ, ʻcowʼ, ʻdragonʼ,

    ʻghostbustersʼ, ʻkittyʼ, ʻmeowʼ, ʻmilkʼ, ʻstegosaurusʼ, ʻstimpyʼ, ʻturkeyʼ, ʻturtleʼ, ʻtuxʼ] 演習 >>> import cowsay >>> cowsay.cow('hello python') ____________ < hello python > ============ \ \ ^__^ (oo)\_______ (__)\ )\/\ ||----w | || ||
  136. テキストファイルの読み書きを⾏う • このセクションで学ぶこと • ファイルの読み書き処理はテキストエディタと同じ • テキストファイルの読み込み • ファイルのクローズ •

    ファイルの書き込み処理 • ファイルの追記処理 • バイナリデータの読み書き処理 • コラム: バイナリデータの処理 SECTION 01
  137. ファイルの読み書き処理はテキストエディタと同じ • ファイル操作の流れ 1. ファイルをモード(Read/Write/Append)を指定して開く 2. ファイルを操作する 3. ファイルを閉じる •

    Pythonでファイルの読み書きには「open」関数を使う • 関数の返り値のファイルオブジェクトのメソッドでファイル操作 hello world hello python fin = open('hello.txt', 'r') print(fin) $ python3 open_01.py <_io.TextIOWrapper name='hello.txt' mode='r' encoding='UTF-8'> コンソール出⼒ /chapter7/hello.txt /chapter7/open_01.py
  138. テキストファイルの読み込み • open関数の第⼆引数を「r」にして読み込み専⽤のファイルオ ブジェクトを取得する • 読み込みモードでの主要な操作 • readメソッド: ファイルの内容を全て読み込む •

    forループ: ファイルの1⾏ずつループ処理する。省メモリ fin = open('hello.txt', 'r') text = fin.read() print(text) hello world hello python $ python3 open_02.py hello world hello python fin = open('hello.txt', 'r') i = 1 for line in fin: line = line.rstrip() print('{} {}'.format(i, line)) i += 1 $ python3 open_03.py 1 hello world 2 hello python コンソール出⼒ コンソール出⼒ /chapter7/hello.txt /chapter7/open_02.py 1⾏ずつ⾏番号とテキストを出⼒: /chapter7/open_03.py 巨⼤なファイルには 必ずforループを使う
  139. ファイルの書き込み処理 • openする際に「w(write)」モードを指定する • オープン時にファイル内容が全削除される • writeメソッドでテキストを書き込む • 改⾏は付与されないので必要ならテキストに「\n」を追加 fout

    = open('test.txt', 'w') fout.write('write text1\n') fout.write('write text2\n') fout.close() $ python3 write_01.py write text1 write text2 コンソール出⼒ /chapter7/test.txt /chapter7/write_01.py
  140. ファイルの追記処理 • すでにあるファイルの末尾にデータを書き込むには 「a(append)」を指定して追記モードでファイルを開く • 開いたあとの操作は「w」モードと同じ fout = open('test.txt', 'a')

    fout.write('write text3\n') fout.close() write text1 write text2 write text3 write text1 write text2 write text3 write text3 $ python3 write_02.py $ python3 write_02.py コンソール出⼒ /chapter7/test.txt(1回⽬の実⾏) /chapter7/test.txt(2回⽬の実⾏) /chapter7/write_02.py
  141. バイナリデータの読み書き処理 • 画像などのバイナリデータはモードに「b(binary)」を加える • 読み込みモード: 「rb」 • 書き込みモード: 「wb」 fi

    = open('sample.png', 'rb') image = fi.read() fi.close() fo = open('test.png', 'wb') fo.write(image) fo.close() $ python3 write_03.py コンソール出⼒ /chapter7/sample.png /chapter7/test.png 画像ファイルのコピー: /chapter7/write_03.py
  142. コラム: バイナリデータの処理 • バイナリデータを直接ユーザー操作することは少ない • たとえばPILモジュールによる画像操作(サイズを⼩さくする) 1. ファイルからバイナリデータを読み込む 2. ライブラリにバイナリデータを渡す

    3. ライブラリでデータ加⼯(画像サイズを⼩さくする) 4. バイナリデータをライブラリから取得 5. バイナリデータをファイルに書き込む • 「io.BytesIO」でバイナリをファイルに⾒せる処理をバイナリ 系の操作ではよく使う。ファイルパスが求められるところにバ イナリを与えたり、ファイルに書き出すのではなくバイナリを 書き出すなどができる データ操作は ライブラリ 任せ
  143. 例外とは • 「例外」はプログラムの予期しない動作で発⽣するエラー • エラーと例外(英語ではException)はほぼ同じ意味 • たとえば存在しないファイルを読もうとすると例外が発⽣する fin = open('abc.txt',

    'r') for line in fin: print(line) fin.close() print('finished') $ python3 exception_01.py Traceback (most recent call last): File "exception_01.py", line 1, in <module> fin = open('abc.txt', 'r') FileNotFoundError: [Errno 2] No such file or directory: 'abc.txt' /chapter7/exception_01.py コンソール出⼒
  144. try/exceptによる例外処理 (1/2) • 例外発⽣時に処理を継続するために「try/except」⽂を使う • try: 例外が発⽣する可能性がある場所 • except: try内で例外が発⽣した場合におこなわれる処理

    try: file_name = 'test.txt' print('ファイルの読み込み開始') fin = open(file_name, 'r') fin.read() fin.close() print('読み込み成功') except: print('エラーが発⽣') print('プログラムが終了') $ python3 exception_02.py ファイルの読み込み開始 読み込み成功 プログラムが終了 $ python3 exception_02.py ファイルの読み込み開始 エラーが発⽣ プログラムが終了 存在する ファイル 存在しない ファイル コンソール出⼒ コンソール出⼒ /chapter7/exception_02.py エラーが発⽣してもプログラム最後まで実施されている
  145. 例外の詳細を取得する (1/2) • except⽂で例外を「例外クラス」の変数に補⾜できる • 例外クラスの最も⼀般的なものは「Exception」 • 例外クラスの種類に応じた例外処理の定義なども可能で、可能 ならより詳細な例外クラスで実装することを推奨(割愛) try:

    fin = open('test999.txt', 'r') fin.close() except Exception as e: print('エラーが発⽣') print(e) $ python3 exception_03.py エラーが発⽣ [Errno 2] No such file or directory: 'test999.txt' /chapter7/exception_03.py コンソール出⼒
  146. 例外の詳細を取得する(2/2) • 「traceback」モジュールを使うとエラー発⽣時に出⼒されて いたメッセージのほぼ全てを補⾜できる • エラー発⽣箇所がファイルと⾏単位で分かる import traceback try: fin

    = open('test999.txt', 'r') fin.close() except: print('エラーが発⽣') error_text = traceback.format_exc() print(error_text) $ python3 exception_04.py エラーが発⽣ Traceback (most recent call last): File "exception_04.py", line 4, in <module> fin = open('test999.txt', 'r') FileNotFoundError: [Errno 2] No such file or directory: 'test999.txt' コンソール出⼒ /chapter7/exception_04.py 中級者向け: 例外処理に慣れたら例外クラスを 指定した「予測していた」例外処理に加えて、 予測していなかった例外処理をExceptionなどの 上位の例外クラスでtraceback付きで処理する
  147. • requestsモジュールのgetを例外処理でラップした関数を作成 してください(6章に従ってインストールが必要) • 返り値はタプルで(成功したか, レスポンスのテキスト)とします • 例外が発⽣した場合はtracebackをプリントして、(False, '') を返す

    • レスポンスに問題がある場合は(False, '')を返す • 問題がない場合(True, テキスト)を返す • リクエストレスポンスに問題があるかないかは「レスポンスオ ブジェクト.ok」で調べられます • わざと「存在しないサイトにアクセス」「存在するサイトの存 在しないURLにアクセス」をしてみてください 演習
  148. プログラムファイルの⽂字コード • Pythonの標準⽂字コードはUTF-8。これを使うのが基本 • 諸事情で他の⽂字コードを使う場合はプログラムの冒頭で「ど の⽂字コードでプログラムを書いているか」を宣⾔する # -*- coding: shift-jis

    -*- print('こんにちは') $ python3 encoding_01.py こんにちは コンソール出⼒ Shift-Jisで書かれたプログラム: /chapter7/encoding_01.py プログラムの⽂字コードの指定。 プログラムが指定した⽂字コードで なければエラーが発⽣する
  149. 様々な⽂字コードのファイルの読み書き (1/2) • OSによってopen関数が想定するデフォルト⽂字コードが異なる • Windows(⽇本語環境): Shift-JIS • Mac: UTF-8

    fin_sjis = open('sjis.txt', 'r') print(fin_sjis.read()) fin_sjis.close() fin_utf8 = open('utf8.txt', 'r') print(fin_utf8.read()) fin_utf8.close() $ python3 encoding_02.py Traceback (most recent call last): File "encoding_02.py", line 2, in <module> print(fin_sjis.read()) File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/codecs.py", line 322, in decode (result, consumed) = self._buffer_decode(data, self.errors, final) UnicodeDecodeError: 'utf-8' codec can't decode byte 0x82 in position 0: invalid start byte • デフォルト⽂字コードと異なる ファイルを⽂字コード指定なしに openするとエラー コンソール出⼒ /chapter7/encoding_02.py MacでUTF-8を期待していたのにShift-Jisをオープンしたのでエラーとなっている
  150. 様々な⽂字コードのファイルの読み書き (2/2) • open関数のencode引数で⽂字コードを指定可能 • マルチプラットフォーム環境も考慮すると以下を推奨 • テキストファイルの⽂字コードにはUTF-8を利⽤ • 移植性を考えてMacであってもファイル読み込み時の

    encodeオプションにUTF-8を指定しておく • writeモードではopen時に指定した⽂字コードでファイルが書 き出される fin_sjis = open('sjis.txt', 'r', encoding='shift-jis') print(fin_sjis.read()) fin_sjis.close() fin_utf8 = open('utf8.txt', 'r', encoding='utf-8') print(fin_utf8.read()) fin_utf8.close() $ python3 encoding_03.py あいうえお かきくけこ あいうえお かきくけこ コンソール出⼒ /chapter7/encoding_03.py
  151. • 中に書かれている⽂字コードが不明なファイルの⽂字コードを 特定するプログラムを作成してください • UTF-8, Shift-JIS, EUC-JPの3つを判定するものとします • AsciiのテキストはUTF-8に分類されるとします •

    ヒント: 指定した⽂字コードで読み込めればその⽂字コード、 エラーがおきればその⽂字コードでないと分かります • 本書籍で提供されているUTF-8とShift-Jisのサンプルファイル を使って正しく判別できるか確認してください 演習
  152. コマンドライン引数による⼊⼒ • プログラムにユーザー⼊⼒を与える場⾯は多い • 解析プログラムで解析するデータのファイル • 接続先IPやパスワードなど • プログラム内に直接定数で定義するよりユーザーに⼊⼒させた ほうがプログラム修正がいらないので利⽤が簡単

    • コンソールでプログラム起動時に与えるパラメーター(コマンド ライン引数)を「sys.argv」でリスト形式で取得できる $ python3 argument_01.py abcd efgh ['argument_01.py', 'abcd', 'efgh'] import sys print(sys.argv) 起動時にプログラム名に加えて「abcd」「efgh」が与えられている。 sys.argv(リスト)にプログラムファイルと コマンドライン引数が順番に格納されている コンソール出⼒ /chapter7/argument_01.py
  153. コマンドライン引数の利⽤例 • 指定したファイルを読み込むプログラム • コマンドライン引数を使うコツ • 引数が指定された数か最初にチェックする • sys.argvの要素の値を分かりやすい定数に代⼊する import

    sys if len(sys.argv) < 2: print('Error. please input file name') exit() FILE_NAME = sys.argv[1] fin = open(FILE_NAME, 'r') print(fin.read()) fin.close() $ python3 argument_03.py hello.txt hello world hello python hello world hello python コンソール出⼒ /chapter7/argument_03.py /chapter7/hello.txt
  154. input関数での標準⼊⼒ • 「input関数」で標準⼊⼒受け付け(ユーザーのキーボード⼊⼒ を受け付ける)状態となる • エンター(リターン)ボタンが押されたら⼊⼒終了 print(1) a = input('何か⽂字を⼊⼒してください:')

    print('⼊⼒値:' + a) print(2) $ python3 input_01.py 1 何か⽂字を⼊⼒してください: $ python3 input_01.py 1 何か⽂字を⼊⼒してください:hello ⼊⼒値:hello 2 コンソール出⼒ 「hello」と⼊⼒しエンターキー /chapter7/input_01.py
  155. input関数の利⽤例 • パラメーター⼊⼒を対話式に⾏なうプログラム • IPやパスワードなどを順番に⼊⼒させる • 設定項⽬の番号を⼊⼒させ、各項⽬でIPやパスワードを⼊⼒ • プログラムをあえて⼀時中断させる⽬的 •

    デモやテストのステップごとに⽌めて結果を表⽰。確認を終 えたら次のプログラムに向かう file_name = input('please input filename: ') fin = open(file_name, 'r') print(fin.read()) fin.close() $ python3 input_02.py please input filename: hello.txt hello world hello python コンソール出⼒ ファイル名を聞いて、それを出⼒するプログラム: /chapter7/input_02.py
  156. コラム: コマンドライン引数 vs 標準⼊⼒ • Pythonのプログラムはシェルスクリプトやバッチなどから呼び 出されることも多い • 標準⼊⼒はユーザーの敷居は低いかもしれないが、他のプログ ラムから使いにくくなる

    • コマンドライン引数は呼び出せば動くので、他のプログラムか ら使いやすい • 特別な理由がなければコマンドライン引数でパラメーターを⼊ ⼒させるのがよい • 第三の⼿法として「環境変数でのパラメーター指定」なども使 われることがある
  157. • コマンドライン引数と、input関数でそれぞれ以下を実現するプ ログラムを作成してください • 「数値1, 計算記号, 数値2」を受け取る • 対応する計算記号は +,

    -, *, / の4つ • 数値1と数値2を与えられた計算記号で計算した結果をprint 出⼒する • ヒント1: 数値は⽂字列からfloatにキャストする • ヒント2: 計算記号の値に応じてif/elseで処理を変える 演習
  158. プログラム開発の計画を作る • 題材:インターネットから複数の画像を集めるプログラムを作成 • アプリケーション開発には計画が必要 • 本章では以下のステップで開発を進めていく • ⽬的を実現するためには何が必要かまとめる •

    プログラム全体の流れを考える • プログラムの中の重要な箇所を試験的に作ってみる • プログラムの⼤枠を作る • コードに機能を追加していき設計や品質を向上させる
  159. コラム: 設計とフレームワーク • ⼊⾨書は読んで理解できたが、実際にサンプル以上のプログラ ムを書けないという⼈が多い • フレームワークは「⼤枠のなかの処理を⾃分で実装する」とい う⽅式でシステムを作成するので、ライブラリを使って⾃分が 全てを設計するよりも「設計が簡単」 •

    ⾃分で設計をするのは慣れが必要なので、HTML/CSSを学んで 簡易ウェブフレームワークで簡単な動的なウェブサイトを作っ てみるとよいかも ライブラリ ライブラリ ライブラリ ⾃分で設計した⼤枠 作成したパーツ フレームワークが⽤意する⼤枠 作成したパーツ 作成したパーツ 独⾃設計のアプリ フレームワークに従うアプリ
  160. なぜテスト実装が必要なのか • 開発スピードを向上させるため • ⼤きなサービスのなかで⼩さな1つの機能を0から開発するの は「起動」「準備(テスト値の⽤意など)」に時間がかかる • ⼩さなコードでエラーを繰り返しながら機能を実現させ、その あとで組み込めばトラブルが少ない •

    当初の想定と異なる場合があるため • 利⽤を想定していたライブラリが⽬的の機能を果たせない場合 などがある • 他を作り込んだうえで発覚すると修正が⼤変なので⼤枠を固め る前に可能な限りパーツを作っておく • すでに重要箇所が動いているとメンタル的に安⼼して開発できる
  161. URLを指定してHTMLを取得 • 本番プログラムは⼤きくて複雑なので、「本番でも使いやすい テスト⽤のコード(たとえば関数)」を作成するのがよい • HTMLの取得には6章のrequestsモジュールを利⽤ import requests def download_html(url):

    response = requests.get(url) html = response.text return html html = download_html('https://gihyo.jp/book/list') fout = open('sample.html', 'w', encoding='utf8') fout.write(html) fout.close() $ python3 get_images_01.py <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:og="http://opengraphprotocol.org/schema/" xmlns:fb="http://www.facebook.com/2008/fbml" xml:lang="ja" lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>書籍⼀覧新刊|技術評論社</title> <meta name="description" content="" />... URLを指定してHTMLを取得するコード: /chapter8/get_images_01.py 出⼒ファイル: sample.html コンソール出⼒
  162. 正規表現でHTMLからURLを取得(2/2) import re text = '<p>Hello World</p><a href="http://example.com">More Examples</a><a href="http://example2.com">Even

    More Examples</a>"' links = re.findall('<a href="?\'?([^"\'>]*)', text) print(links) $ python3 get_url_from_html.py ['http://example.com', 'http://example2.com'] import requests import re def get_urls(url): response = requests.get(url) html = response.text urls = re.findall('<a href="?\'?([^"\'>]*)', html) return urls urls = get_urls('https://gihyo.jp/book/list') print(urls) $ python3 get_images_02.py ['/book', '/site/inquiry', '/site/profile', … コンソール出⼒ コンソール出⼒ HTMLからURLを抜き出すプログラム: /chapter8/get_url_from_html.py 指定したURLにアクセスしてURLを抜き出すプログラム: /chapter8/get_images_02.py
  163. 相対URLを絶対URLに変換 • 絶対URL: https://gihyo.jp/news/info/2020/03/1301 • 相対URL(ドメインが省略): /news/info/2020/03/1301 • 相対URLは現在のURLを基準とした表記なので、プログラムで 使う場合は現在地に依存がない絶対URLへの変換が望ましい

    import urllib.parse def get_abs_url(current_url, url): abs_url = urllib.parse.urljoin(current_url, url) return abs_url print(get_abs_url('https://gihyo.jp/book/list', 'http://gihyo.jp/site/inquiry')) print(get_abs_url('https://gihyo.jp/book/list', '/book/2017/978-4-7741-8751-8')) print(get_abs_url('https://gihyo.jp/book/list', '2017/978-4-7741-8751-8')) $ python3 get_abs_url.py http://gihyo.jp/site/inquiry https://gihyo.jp/book/2017/978-4-7741-8751-8 https://gihyo.jp/book/2017/978-4-7741-8751-8 コンソール出⼒ 相対URLを絶対URLに変換するプログラム: /chapter8/get_abs_url.py
  164. URLが画像ファイルか否かを判定 • URLを以下の2種類のいずれか拡張⼦で判定する • 画像ファイル: 拡張⼦が「.jpg」「.png」「.gif」 • HTMLファイル: 画像ファイルでない場合全て •

    (厳密にはHTTPレスポンスのMIMEで判断することが望ましい) def check_url_type(url): if '.jpg' in url: print('jpg') elif '.png' in url: print('png') elif '.gif' in url: print('gif') else: print('html') urlの画像判定プログラム: /chapter8/check_url_type.py
  165. 画像ファイルを保存 • requestsで取得したレスポンスからデータを取得 • データをバイナリ書き込みモードでファイルに保存 import requests import os def

    save_image(url, filepath): # 画像データを取得 response = requests.get(url, stream=True) image_data = response.content fout = open(fpath, 'wb') fout.write(image_data) fout.close() URLの画像を保存する: /chapter8/save_image.py
  166. 本番のプログラムを作る • このセクションで学ぶこと • クラスを設計する流れ • コンストラクタの実装 • メソッドの役割を決める •

    メインメソッドの実装 • 指定ページから絶対URL⼀覧を得るメソッドの実装 • 絶対URLを画像とHTMLに振り分けるメソッドの実装 • 画像データを保存するメソッドの実装 • メインプログラムの実⾏ • コラム: プログラムのテスト SECTION 03
  167. クラスを設計する流れ • 複雑なプログラムでは「関数(メソッド含む)が別の関数を呼び出 す」を繰り返すことで実現する • 関数間で全てのデータを引数で受け渡しするとカオスになるので、 クラスを使って共有データはインスタンス変数として定義する(詳 細な理由は5章を参照) • クラス設計

    • プログラムが必要とする情報はコンストラクタで受け取る • メソッド間で共有されるデータはコンストラクタでインスタン ス変数として定義する • 処理に応じてメソッドを分けて、中⼼となるメソッドが各機能 を担当するメソッドを呼び出すシンプルな処理の流れを作る
  168. このアプリケーションの設計概要 __init__(コンストラクタ) インスタンス変数の定義 run メインメソッド Whileループで集め 終わるまで繰り返す get_abs_url 指定URLから絶対値のURL群を取得 get_image_url_list

    URL群から画像のURL群を取得 save_images 画像URL群の画像をローカルに保存 実⾏ コード クラス定義 (1) (2) (3) (4) (5) (6) この章での実装順序
  169. コンストラクタの実装 • 引数 • save_dirpath: 画像保存場所 • start_page: 最初にアクセスするURL •

    maximum_download: 集める画像の枚数 import os import re import urllib import requests class ImageCrawller: def __init__(self, save_dirpath, start_page, maximum_download): self.save_dirpath = save_dirpath self.crawl_url_list = [start_page] self.stocked_url = set() self.maximum_download = maximum_download self.download_counter = 0 • インスタンス変数 • self.save_dirpath: 引数より • self.crawl_url_list: 次にアクセスするURLのリスト • self.stocked_url: すでにアクセスしたURLのセット • self.maximum_download: 引数より • self.download_counter: 何枚保存したか数える コンストラクタの定義: /chapter8/image_crawller_01.py
  170. メソッドの役割を決める • メソッドの分割について • ⼤きい: 処理が多すぎてメソッドの⾒通しが悪くなる • ⼩さい: メソッド間の呼び出しが多すぎて分かりにくい •

    適切な粒度(分割する⼤きさ)が必要 • 本章で作成するメソッド • コンストラクタ(作成済み) • 下記3つを束ねる中⼼となるメソッド(メインメソッド) • あるURLのHTMLに含まれる絶対URLをリストで返すメソッド • URLをHTMLと画像に振り分けるメソッド • イメージを保存するメソッド
  171. メインメソッドの実装 (1/2) • 荒削りでよいので、全体の処理の流れをコメント付きで書く • 細部を実装してからメインメソッドも微修正される • 実験的に主要な機能をテストしていないと設計しづらく、あと にプログラムの⼤きな修正が発⽣する可能性が⾼まる •

    プログラム(次ページ)の設計 1. 終了条件に合致していないか確認。合致していれば終了 2. URL回収⽤のウェブページURLをリストから取得 3. そのURLにアクセスして絶対URLのリストを取得 4. 絶対URLのリストを「画像」「ウェブページ」に分類 5. 画像のURLリストの画像を全て取得 6. 1に戻る(whileによる無限ループ)
  172. メインメソッドの実装 (2/2) def run(self): while True: # 処理1: 探索するURLがなければ終了。規定数以上を集めていても終了 if

    len(self.crawl_url_list) == 0: break if self.download_counter >= self.maximum_download: break # 処理2: 次に調べるHTMLのURLを取得 crawl_url = self.crawl_url_list.pop(0) # 処理3: HTMLページから絶対URLを抽出する urls = self.get_abs_urls(crawl_url) # 処理4: 絶対URLをHTMLかイメージかに分類する。イメージのリストを返す image_url_list = self.get_image_url_list(urls) # 処理5: リストに格納されたイメージを全て保存する self.save_images(image_url_list) print('Finished') メインメソッドの定義を追加: /chapter8/image_crawller_02.py
  173. 指定ページから絶対URL⼀覧を得るメソッドの実装 def get_abs_urls(self, url): try: # URLから⽂字列のHTMLを取得 response = requests.get(url)

    html = response.text # HTMLからURLを抜き出してリストに格納 relative_url_list = re.findall('<a href="?\'?([^"\'>]*)', html) # 相対URLを絶対URLに変換。HTTP/HTTPS以外のURLは除外 abs_url_list = [] for relative_url in relative_url_list: abs_url = urllib.parse.urljoin(url, relative_url) if abs_url.startswith('http://') or abs_url.startswith('https://'): abs_url_list.append(abs_url) return abs_url_list except Exception as e: print('Error: {}'.format(e)) return [] メソッドget_abs_urlsを追加: /chapter8/image_crawller_03.py
  174. 絶対URLを画像とHTMLに振り分けるメソッドの実装 def get_image_url_list(self, url_list): try: image_url_list = [] for url

    in url_list: if url in self.stocked_url: # すでに登録されたURLなので無視 continue if '.jpg' in url: image_url_list.append(url) elif '.png' in url: image_url_list.append(url) elif '.gif' in url: image_url_list.append(url) else: self.crawl_url_list.append(url) # 画像ファイルではないのでURL取得に使う self.stocked_url.add(url) # URLを登録。同じものは再登録しない return image_url_list except Exception as e: print('Error: {}'.format(e)) return [] メソッドget_image_url_listを追加: /chapter8/image_crawller_04.py
  175. 画像データを保存するメソッドの実装 def save_images(self, image_url_list): for image_url in image_url_list: try: #

    決められた回数以上のダウンロードをした場合は終了 if self.download_counter >= self.maximum_download: return # イメージを取得 response = requests.get(image_url, stream=True) image = response.content # イメージをファイルに保存 file_name = image_url.split('/').pop() save_path = os.path.join(self.save_dirpath, file_name) fout = open(save_path, 'wb') fout.write(image) fout.close() self.download_counter += 1 print('saved image: {}/{}' .format(self.download_counter, self.maximum_download)) except Exception as e: print('Error: {}'.format(e)) メソッドsave_imagesを追加: /chapter8/image_crawller_05.py
  176. メインのプログラムの実⾏ • プログラム最後に「if __name__ == ' __main__ ' :」を定義 if

    __name__ == '__main__': save_dirpath = 'test' start_page = 'https://gihyo.jp/book/list' maximum_download = 10 crawller = ImageCrawller(save_dirpath, start_page, maximum_download) crawller.run() $ python3 image_crawller_06.py saved image: 1/10 saved image: 2/10 saved image: 3/10 saved image: 4/10 saved image: 5/10 saved image: 6/10 saved image: 7/10 saved image: 8/10 saved image: 9/10 saved image: 10/10 Finished コンソール出⼒ アプリを動かす実⾏コードを追加: /chapter8/image_crawller_06.py
  177. コラム: プログラムのテスト • 数⼗⾏以上のコードがミスなく書けていると思わないこと • 「ビッグバンテスト」と呼ばれる全てを書き終わってから動かし てテストをすることは避けること • テストを使ってきちんと動くプログラムを書くコツ •

    モジュールの設計で役割ごとにプログラムファイルを分離 • 確実に動く⼩さなプログラムを開発してモジュールに組み込む • モジュール間の依存関係を減らしてモジュールレベルでテスト を実施できるようにする • 変更を繰り返した汚いコードは綺麗に書き直す • ユニットテストでテストを⾒える化する(中級者) • ⾃動化などでテストを勝⼿に⾛らせるようにする(上級者)
  178. お悩み相談コーナー 教科書は理解したけど次のレベルに進めません!! おまけ 1. ⼊⾨者卒業: 本を読むよりコードを書くべし 2. 初級者卒業: 美しい設計を体と頭で覚える 3.

    中級者卒業: 開発はプログラミング以外にもある 4. 駆け出し編: インフラエンジニアという道もある 5. 歴史編: 開発とインフラの技術変遷
  179. ⼊⾨者卒業: 本を読むよりコードを書くべし • このセクションで学ぶこと • プログラミングのレベル • 包丁をにぎらず本ばかり読む駆け出し料理⼈ • やりたいことは書き始めてから考えよう

    • おすすめ1: Flaskによる簡易ウェブサービスの作成 • おすすめ2: Tkinterによるデスクトップアプリの作成 • おすすめ3: 業務向けの作業スクリプト作成 • 著者が興味でプログラムを書いた例 • ⼊⾨者卒業に難しい知識はいらない SECTION 01
  180. プログラミングのレベル • ドラクエと同じで「適切な場所(学習内容)」でレベル上げ • 低いレベルで難易度の⾼すぎるエリアには向かわないこと • 2010年以降は開発スタイルが⼤きく変わってきている ⼊⾨者 初級者 中級者

    中上級者 (オールラウンド型) 上級者 (プログラミング特化型) ⼊⾨書(2000円ぐらいの書籍)の レベルの内容を習得済み。 • 基礎⽂法はわかる • オブジェクト指向を使える • 簡単なスクリプトを書ける • 簡易フレームワーク習得 • ⾃分で簡単なアプリを作成できる ⾼度なプログラミング知識 • ⾃分で中規模アプリを作成できる • オブジェクト指向を設計可能 • フルスタックフレームワーク習得 • デザインパターン習得 • リファクタリング習得 • テスト習得 • 複数のプログラミング⾔語を習得 3ヶ⽉ 2年 達⼈プログラマ 中級者からの純粋レベルアップ プログラミング以外も得意 クラウドの普及で最近増加 5年 3年(中級者向けの⾼度なプログラミング知識習得をスキップ) ⼊⾨書完読。 基本レベルの演習を 全て解答可能
  181. やりたいことは書き始めてから考えよう • プログラミングは作りたいものを作ることで学べる • ただし適切なレベルの作りたいものがある⼈はごく少数 • 多くの個⼈プログラマは以下の3種類を作る • ウェブサービス <-

    おすすめ • デスクトップアプリ • 作業スクリプト • これらの利⽤法を学びながらなにか作りたいものを探すか、練 習がてら⼀般的なサービス(掲⽰板)を簡易模倣したり、仕事の 作業(エクセル操作など)をPythonで実装してみるとよいかも
  182. おすすめ1: Flaskによる簡易ウェブサービスの作成 • Pythonの軽量Webフレームワーク • アクセスされるURLに処理を関数として結びつけることでウェ ブサービスを簡単に作ることができる • ウェブアプリは現在の開発の⼤きな割合になりつつあるので、 簡易ウェブサービスの作成経験は損にはならない

    ページ(HTML)作成の関数1 ページ(HTML)作成の関数2 ページ(HTML)作成の関数3 ページ(HTML)作成の関数4 フレームワーク ウェブサーバー機能 ブラウザ ページ要求 ページを返す 開発者ページ(HTML)作成のみを開発 他のサーバー処理は全てFlaskが担当
  183. 補⾜) HTML/CSS • ウェブページのマークアップ⾔語 • HTMLにウェブページの内容を書く。これをFlaskで動的に作成 する。たとえばお店サイトの商品ページをURLごとに作成 • CSSはHTML(ページ本体)にデザインを加える。 •

    フロントエンジニアやウェブデザイナーなどの専⾨家がいる領 域なので素⼈が有名サイトレベルのものを作ることは不可能 • 素⼈でウェブ作成したければ「Bootstrap」などの「初級者で もそこそこのページ」を作れるフレームワークを学ぶとよい
  184. おすすめ3: 業務向けの作業スクリプト作成 • 以下の操作をPythonで実施して省⼒化。ウェブやGUI化してもOK • ディレクトリやファイル操作 • ExcelやCSV • Word

    • PDF • Eメール • 画像 • ウェブページのスクレープ • キーボードとマウス操作の⾃動化 • 英語ですが「Automate the Boring Stuff with Python」のウェブサ イト(無料)がおすすめ。オライリーから4000円ぐらいの邦訳本も でている
  185. 著者が興味でプログラムを書いた例 • 本トレーニングの8章の画像回収ツール相当のものをJavaで作 成(1番最初に作ったアプリ) • ゆっくり実況が好きだったので、⾳声合成ソフトのMacアプリ (Objective-C)とクラウド版(Python + C⾔語)を作成して公開 •

    仕事での巨⼤なログの解析アプリケーション(C++とQt)。あく までも仕事ではなく趣味の⼀環で • 鉱物標本のラベルを作成するサイト • インフラ作業の⾃動化系(趣味から現在の本業へ)
  186. 初級者卒業: 美しい設計を体と頭で覚える • このセクションで学ぶこと • スキルは「困ってからの設計変更」で向上 • 複雑さを減らすことに気を配ろう • クラスを徹底活⽤しよう

    • リファクタリング • テスト • デザインパターン • フルスタックフレームワーク: Django • 他のプログラミング⾔語を学んでみよう • 初級者卒業には⾼度な専⾨書が必要 SECTION 02
  187. スキルは「困ってからの設計変更」で向上 • ⾼度なプログラミングスキルの基本は「巨⼤なコードを整理して、 バグを少なく拡張しやすくする」ことに尽きる • ⾼度なプログラミングスキルが役⽴つ場⾯ • 開発前の設計 • 増築/改造を繰り返したコードの整理

    • ⼤規模な開発を効率よくおこなう • 初級者は深い経験が必要なことはできない(短時間では⾝につけに くい)ので、最初からよい設計をするための⼿法を学ぶのではなく、 「とりあえずコードを書きまくり、増築⽅式アプリをでかくしてい き、継ぎ接ぎがカオスになってからコード整理」して体で覚える • どのように設計しなおすと「構造がシンプルになるか」を考える
  188. 複雑さを減らすことに気を配ろう • プログラム複雑になるシナリオ • 関数やメソッドの呼び出しが深くなっている • 1つのデータが⾊々な箇所で読み書きされている • 機能追加をその場しのぎの継ぎ接ぎで加えた •

    複雑なコードを設計変更して綺麗にするとスキルが向上 関数 関数 関数 関数 関数 関数 関数 関数 関数 関数 汚いコード例 (深い階層) 綺麗なコード例 (階層を浅くした)
  189. クラスを徹底活⽤しよう • クラスには様々な機能がある • 処理や複数のデータをまとめられる(トレーニングで学んだ) • クラスにクラスをいれて複雑さをさらに集約できる • 継承により複数の似たクラスの実装をまとめられる •

    多重継承でクラス内のメソッドを分離できる 共通処理のクラス 機 能 機 能 機 能 機 能 機 能 機 能 利⽤したいクラス 常に継承 必要な機能の クラスのみ継承 (ハリボテ) 呼び出し側 多重継承の利⽤例(REST APIをPythonラップするライブラリの作成などに便利)
  190. デザインパターン • ⼤規模なコードを書くための設計パターン • クラスやモジュール間をどう接続して連携するのがよいかとい うベストプラクティス • 1つのシステムから構成される中規模以上のサービス開発で設 計段階で導⼊するとよい •

    ⾃分で設計したりリファクタリングした経験が積み重なってい ないと机上の理論にすぎないので、数年のプログラミングの経 験を積んでから学べばよい(超有名なシングルトンなど10個以 下のパターンをピックアップして学習してもよい)
  191. 他のプログラミング⾔語の学習 • おすすめはJavaScript。ウェブ開発で他に選択肢がない場合が多い ため • 次点はGo(モダンな開発によい)か、Java(まだ需要が多くてオブ ジェクト指向の学習によい) • 他にはやりたい仕事向きの⾔語(SwiftやC#、PHPなど) •

    以下は3⾔語め以降でよいと思う • Pythonと利⽤⽬的が近い他のスクリプト⾔語(Ruby/Perlなど) • 学習難易度の⾼いC/C++や関数型の⾔語。(これらを必要とする 仕事についていないと利⽤する機会は少ない) • 汎⽤的でない⾔語。Rなど
  192. 初級者卒業には⾼度な専⾨書が必要 • 3000円 - 4000円あたりの書籍は⼊⾨書に⽐べると専⾨的 • 中級者向けの書籍は⼊⾨書よりも扱う範囲が広いので「まんべん なく深い」のではなく「いくつかのトピックが深い」傾向がある • 2000円台の本とは違ってプログラミングの⾼度な専⾨書籍の半分

    以上の知識は必須ではないのですぐには役⽴てられない • 詰め込みではなく継続的に書籍を読み続けてスキルの⼟壌を耕し ておくとよい • コードを書き続けているうちに利⽤するべき場⾯に遭遇する場合 がある。⼟壌が整っていると難しい状況でもよい判断ができる
  193. 中級者卒業: 開発はプログラミング以外にもある • このセクションで学ぶこと • 巨⼈の肩の上に⽴つ凡⼈ > プログラミングだけを知る達⼈ • パーツごとに別々に作成するのが今のトレンド

    • パーツ間の連携⽅法 • 軽量な実⾏基盤であるコンテナ • 既存ミドルウェアをアプリケーションで使う • サービスの監視 • APIフレームワーク: FastAPI • リバースプロキシーのURL設計例 • CI/CDによる省⼒化と安定性の確保 • DevOps環境の構築と著者が採⽤している環境の紹介 SECTION 03
  194. パーツごとに別々に作成するのが今のトレンド • モノリシックなサービス: 1つの構成要素からなるアプリ • マイクロサービス: 複数の構成要素からなるアプリ • 2010年以降はマイクロサービスが⼤幅に普及 •

    ウェブサービス以外でもマイクロサービスの設計は使える。た とえばプライベート基盤を提供するNutanixの製品もコンポー ネントごとに独⽴したマイクロサービスな設計となっている モジュールC モジュールD (過去遺産) モジュールA モジュールB アプリケーション モノリシックな設計 サービスA (新規でPython製) サービスB (MongoDBなど) サービスC (Go製の過去遺産) サービスD (新規でJava製) APIで連携 マイクロサービスアーキテクチャ 達⼈プログラマ(上級) でないと設計できない 万能プログラマ(上級) でないと設計できない
  195. パーツ間の連携⽅法 • リバースプロキシー: URLによるアクセス先の振り分け。 HTTPSオフロードやキャッシュ機能などもある • メッセージング: サービス間のやりとりのための仕組み(割愛) Web Server

    API Server Image Server /api/...へのアクセス /images/...へのアクセス /...へのアクセス リバースプロキシー 内部IPで通信 外部IP(1つ) アプリケーション全体 サービスA サービスB サービスC
  196. 軽量な実⾏基盤であるコンテナ • コンテナはサービスを独⽴させるのに便利なツール • 軽量ですぐに起動できて、どこでも動かせる擬似OS環境 • 複数のコンテナを1つのホスト上で動かしたり、多数のコンテ ナを複数ホストで構成されるクラスタ上で動かせる 本番ホスト Docker

    コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ ホスト1 Docker ホスト2 Docker クラスタ(Kubernetesなど) コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ 開発ホスト Docker コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ 他環境(別IPなど)に すぐに移せる マイクロサービスの各サービスを コンテナとして動かすことが多い
  197. 既存ミドルウェアをアプリケーションで使う • 以下は筆者がマイクロサービス開発よく使うサービス。⾃作す るのではなくサービスを使う前提でプログラムを書くと簡単 • NGINX • リバースプロキシー • ウェブサーバー(静的なファイルの配布)

    • MongoDB: ドキュメント指向データベース。プログラムが使う データ構造(辞書型。JSON)をそのまま格納したり検索可能 • Redis: KVSと呼ばれる⾼速なキャッシュ • オブジェクトストレージ: ファイルやイメージをURL形式で保 存する。著者はローカルではMinioを利⽤
  198. サービスの監視 • 複雑に構成されるサービス群は監視が必要。以下は著者の構成 • Loki: システムログの集約(次世代のシスログサーバー) • Prometheus: メトリックの集約(性能や稼働状況などの指標) •

    AlertManager: しきい値を超えた際に管理者にメール通知 • Grafana: 集約したログやメトリックを表⽰、検索 ホスト Docker (Loki Logging Driver) コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ コン テナ 全コンテナの ログ 全コンテナの メトリック ウェブで表⽰
  199. ミドルウェアを利⽤する際のポイント • 完璧なものを⽬指すのではなく規模にあったコスパで選ぶ • 「構築/運⽤コストと覚えること」は少ないほうがいい • たとえば著者はログ監視にElasticsearch/Kibana/Fluentdという 構成ではなく、Grafanaを使っているのでLokiを採⽤している • 状態をホストやコンテナではなく共有されるミドルウェアに任せる

    • たとえばローカルキャッシュではなくRedisを使う • たとえば画像やファイルの管理はローカルではなくオブジェクト ストレージを利⽤する • これを守らないとパーツへの依存性が発⽣して、構成変更(ノード 数を増やすなど)の難易度が跳ね上がる
  200. マイクロサービスのウェブアプリの構成例 Node (SPA担当) ウェブサーバー (静的ファイル配信) Node (JS等のビルド) APIサーバー 1 APIサーバー

    N Minio(画像) JS CSS 画像 gulp アプリサーバー (HTML⽣成) 内部API呼び出し 画像の 取得/保存 画像取得 外部API呼び出し SPAのページへの アクセス 静的ファイル 取得 ページ アクセス Redis MongoDB キャッシュ セッション管理 データ管理 Grafana Loki Prometheus Exporters 管理/監視系 AlertManager Browser ページ アクセス セッション管理 (ログイン状態で表⽰切り替え) JavaScript API呼び出し リバースプロキシ + ⼀部のキャッシュ 信頼できないゾーン 信頼できるゾーン コンテンツビルダー (MarkDown -> JSON) ページデータ 作成 ... ⾃作ツール群
  201. APIフレームワーク: FastAPI⼊⾨ • フルスタックフレームワークはマイクロサービス向きでない • Flaskのような軽量フレームワークを主体とした⼩型のAPIサー バー群を⽤途ごとに作成する • 著者はPython製のAPI向けで⾼速なFastAPIを利⽤している Docker

    (Logging-Driver) ミドルウェア • 例外処理 • Prometheus Exporter APIの実装 他のAPIサーバー Appサーバーのみ他を参照 Redis MongoDB キャッシュ セッション管理 データ管理 バルクアクセス Prometheus Loki APIアクセス
  202. リバースプロキシーのURL設計例 • リバースプロキシーでプレフィックス(前⽅)基準でURLごとに処理変更 • ウェブ(APPサーバー)の懸念事項 • キャッシュ対象(全ユーザー共通)と⾮対象(ユーザーごと)ページがある • 同⼀ドメインで多⾔語対応させることが現在は多い(URLで⾔語区別) •

    APIの懸念事項 • 複数のAPIサーバーが存在している。APIのバージョン管理も必要 • パブリックAPI(外から)とプライベートAPI(中のみ)がある • URLの設計例 • 静的ファイル: /static/... • ウェブ: /<キャッシュの有無>/<⾔語>/... • API: /api/<public/private>/<api名>/<バージョン>/... • オブジェクトストレージ: /images/...
  203. DevOps環境の構築 • DevOpsはバズワード • DevOpsの著者なりの解釈は「CI/CDを中⼼として、(クラウド より上の世界での)インフラを維持しつつサービスを開発/更新 公開し続ける技術」 • ⼩規模なDevOps環境はある程度の知識と試⾏錯誤は必要だが、 個⼈レベルで構築可能

    • 上級者向けの個々の技術を1つ1つ深堀りする前に、とりあえず 全体像を理解するために浅知恵でよいので作って動かすべき • 開発と運⽤にDevOpsを採⽤するのであれば、チームの半分以 上は全体像を掴んでいること • 少数精鋭チームから導⼊して横展開することを推奨
  204. 著者が採⽤しているDocker中⼼のDevOps環境 Dockerレベルの運⽤ Compose (開発サーバー) Dockerfile DockerHub (Pull) Compose (本番環境) Compose

    App Infra Ansible ホストレベルの運⽤ Python GitHub (Code Push) App Infra App Compose (⾃分のPC) GitHub (Code Pull) Dockerfile Jenkins (ビルドからデプロイまで) DockerHub (Push) Jenkinsのトリガー GitHub (ドキュメント等) メトリックとログ 規模が⼤きくなると ビルドからデプロイは Composeではなく Kubernetesに切り替え
  205. 駆け出し編: インフラエンジニアという道 • このセクションで学ぶこと • ウェブ業界はレッドオーシャン • ITインフラ業界は敷居が⾼いが安定している • 代表的なインフラエンジニアの仕事

    • 本当の基盤(電気、空調、ラック) • ネットワーク系 • サーバーとストレージの世界 • パブリッククラウドとプライベートクラウド • 構成管理ツール • インフラの⾃動化 • 専⾨性や資格について SECTION 04
  206. ITインフラ業界は敷居が⾼いが安定している • 専⾨知識が要求されるため学習が必要 • ウェブ業界に⽐べると⼈員供給が少ないので需要が多め(?) • ウェブ業界に⽐べるとコア技術の変遷が⼩さい(ウェブ開発スタ イルは⼤きく変わっても、ネットワークの仕組みやLinuxの根 幹は簡単には変わらない) •

    以後で著者が経験したインフラエンジニアとしての仕事内容を 列挙していく • 仕事が⾯⽩そうと感じたなら、ウェブ屋よりもインフラ屋を駆 け出しエンジニアは⽬指したほうが仕事と給料は安定する可能 性が⾼いかも
  207. サーバーラック利⽤法の設計 • コアとなるIT機器はサーバーラックに搭載するのが⼀般的 • インフラエンジニア以外がこれらを⾒かけることは珍しい • サーバーラックの利⽤法の設計に必要なステップ 1. ネットワークとサーバーの論理設計 ->

    物理設計 2. 必要な電⼒と発⽣する熱量の設計 3. 物理⼯事(ラック設置、電⼒供給、ACの設置) • 配線が多い場合はネットワーク機器をToR(Top of Rack)ではな くMoR(Middle of Rack)にするとケーブルを分散させやすい。 必ずケーブルガイドを使って配線を左右に逃がすこと • スイッチのポート(ケーブルがささる側)はラックサーバーにさ すものは背⾯側の機種を選ぶ。それ以外は前⾯側にする
  208. サーバーラック利⽤法の設計例 ... 1G L2 Switch 1G L2 Switch 2U/4Node Servers

    2U/4Node Servers ... 2U/4Node Servers 2U/4Node Servers ... Core L3 Switch Core L3 Switch Data Stack Data Stack ... Voice Stack (PoE) Voice Stack (PoE) ... ... Infra Servers 10G/40G L3 Switch 10G/40G L3 Switch 1G L2 Switch 1G L2 Switch 2U/4Node Servers 2U/4Node Servers ... 2U/4Node Servers 2U/4Node Servers ... Firewall Firewall ONU ONU Career1 Career2 Internet Wifi Stack (PoE) Wifi Stack (PoE) Wifi Controller Wifi Controller PCs on Desk IP Phones on Desk Meeting Systems APs 終端ラック(1,2): ラック間の接続その他 ラック(1-N): ワークロード⽤ 10G/40G L3 Switch 10G/40G L3 Switch バックボーン接続(冗⻑化) ラック内接続(冗⻑化) ラック内接続(冗⻑化) ラック内接続(冗⻑化) 床下配線 冗⻑化 オフィスエリア 天井配線
  209. 電⼒と空調の設計 • IT基盤を動かすより低いレイヤーの基盤 • 電⼒: サーバーは200kVAでのPSU冗⻑化が⼀般的(kVA≒KW) • ラックあたりの電⼒量: ⼀般的には4kVA -

    20kVA程度 • 重要な環境は電気系統と空調も冗⻑化すること • コールドアイル(冷たい空気)の通路をラック前⾯に設置し、 ホットアイル(排熱による暖かい空気)をラック背⾯に設置 • 排出熱量に猶予をもたせること。空調が冷やせる限度を越える と冷⾵が送れずに急激にオーバーヒートをおこす。夏は室外機 (排熱)も⾼温になるので冷却性能が落ちる点に注意 • ITインフラとは異なる領域なので、その専⾨家(電気、空調等) と相談しながら要件にあった設計をする(⼿伝う)必要がある
  210. 電⼒と空調の設計例 Front Front コールド アイル 室内機 室外機 室外機 室内機 ホット

    アイル 発⽣する熱量と電気使⽤量、価格 あたりから適切な機器を選定する。 弱いよりは強いほうがだいぶマシ。 稼働後の増強は難しい(⾦と時間がかかる) 排熱環境に注意 排熱できない = 冷やせない 200V 100V 200V 100V 200V 100V 200V 100V 200Vの電⼒使⽤量の⾒積もりが必要。 100Vを⼤量に使⽤する場合は⼤容量の 配電が可能な200Vを使うことを検討。 ⽇本では100Vもないと不便 100V配電盤 200V配電盤 ... PDU(電源タップ)は 背⾯側の左右が基本
  211. L2/L3ネットワークの設計 • L2ネットワーク: 同じネットワーク内の通信 • L3ネットワーク: ネットワーク間をまたぐ通信 • L3スイッチ: ネットワークの中⼼なる機器。L2/L3の両対応

    • 設計のコツ: 利⽤⽤途から正しいサイジングをして、L2の物理 範囲を狭めて、アドレス集約を徹底してルーティングを簡単に する。責任分界点には社内であってもFirewallを設置する • 重要箇所は全て冗⻑化することが必須 • ネットワークを使う側のサーバーエンジニアのレベルは⾊々 • ネットワークに詳しくない⼈が使う⼤きなVLAN(Native) • 詳しい⼈が複雑なことをできる複数の⼩さなVLAN • パブリックIPやDMZの設定は素⼈にはやらせないかガイド
  212. L2/L3ネットワークの設計例 Goto Corp, External Office 10.4.0.0/16 Office Core Office Infra

    Office Data 1 Office Voice 1 Office Wifi 1 External Firewall VRF Routing + Internal Firewall Goto Corp, External Site B Small Sites Big VLAN Small VLANs Site A External Firewall Lab1 10.5.0.0/16 Lab2 10.6.0.0/16 10.4.0.0/23 10.4.100.0/24 10.4.101.0/24 10.4.2.0/23 10.4.4.0/22 10.4.8.0/22 10.4.12.0/22 10.5.0.0/17 10.5.128.0/20 ⽀社A 10.4.0.0/14 本社: 10.0.0.0/8 (全⽀社は内部にある) ... ⽀社B ⽀社C ... サイト間VPN(冗⻑化) サイト間VPN(冗⻑化) サイト間VPN(冗⻑化) 必要に応じて 拠点間も直接 接続をする 極論すると全てスタティックルートの設定で まかなえるぐらい経路を集約しまくること。 最初に正しく設計してないと集約できなくなる Internet 10.0.0.0/8 10.4.0.0/14 0.0.0.0/0 Corp 0.0.0.0/0 10.6.0.0/16 全ての通信を 通す必要はない
  213. 名前解決の設計 • ドメイン名をIPアドレスに変換する仕組み • アプリケーションを様々な環境で動かすための必須条件なので、 クラウドが⼀般化した今では昔よりも重きがおかれる • 完全にランダム割り振られるDHCPや固定IPの使⽤ではなく、 「IPAM(IP Address

    Management)」で賢くDHCPを使うこと • クラウドにはIPAMが搭載されることが多い • 存在しない場合には作る(Linux or Windows) • アプリケーションを構成するノード間は名前解決で連携できる ように実装しておくこと。接続先を固定IPではなく解決可能な ドメイン名にしておく。⾃分⾃⾝のIPもDHCPでIPAMから得る
  214. 名前解決の設計例 プライベート クラウド パブリック クラウド コンテナ基盤 VLAN 172.30.0.0/16 VPC 172.31.0.0/16

    コンテナネットワーク 172.32.0.0/16 Backend VM Front VM Reverse Proxy etc Backend Instance Front Instance CDN etc Backend Container Front Container Load Balance r etc IPAM + DHCP(管理コンソール) IPAM + DHCP(管理コンソール) YAMLでの定義 ネットワークアドレスに依存せずサービス 構成ノード間で会話できる必要がある。 IPではなく名前で相⼿を指定する。 フロントも直接外に出さないほうがよい Global IP DNS 公開⽤パブリックIPとサービスのドメイン名の結びつけ
  215. Wifiの設計 • 物理設計 • L2ネットワークへの接続と給電(PoEが多い) • アクセスポイントがカバーできる範囲の調査 • 設置ポイントと⼈⼯密度、衝突しない周波数の調整など •

    認証システム • 物理的に離れた多数のAPをどう⼀元管理するか • 数⼗台以上のAPは個別ではなくコントローラーに設定を集 約するのが基本
  216. セキュリティの設計 • Firewall • 社外から社内だけでなく、社内から社外への通信も対象 • 社内の異なるグループ間もFirewallを通すべき • VPN •

    サイト間接続 • PCからの接続 • ActiveDirectory(AD) • セキュリティ系の機能はADに強く依存しているものが多い。 また、ADを安定稼働させるための専⾨知識が必要 • FirewallやActiveDirectoryも壊れることがあるので、冗⻑化と 壊れた際にきちんとシステムが動き続けるかの検証が必須
  217. クラウド間接続の設計 • 接続⽅式 • VPN • 専⽤線(ダイレクトコネクト): ⾼額 • アドレスレンジを「/16」程度で分離しておかないとルーティ

    ングができなくなる(パブリッククラウドには従来のL2/L3の ルールが適⽤されないことが多いので注意) • アプリのレイヤーを「グループレベルでクラウド間で動く」こ とを前提に構築しておく必要がある。クラウド間やリージョン 間は遅延が⼤きいので「アプリはクラウドでDBはオンプレ」 などの構成はNG
  218. クラウド間接続の設計例 172.31.0.0/16 Public Cloud (AWS) 172.30.0.0/16 Public Cloud (Azure) 172.31.1.0/24

    172.31.2.0/24 172.31.3.0/24 VM VM VM VM VM 172.30.1.0/24 172.30.2.0/24 172.30.3.0/24 VM VM VM VM VM Firewall Appliance 1 Firewall Appliance 2 L3 Switch (Gateway) 172.16.0.0/16 Private Cloud (Nutanix) 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 VM VM VM VM VM VPN VPN 0.0.0.0/0 via L3 Switch 172.31.0.0/16 via L3 FW1 172.30.0.0/16 via L3 FW2 172.16.0.0/16 via L3 Switch 172.31.0.0/16 via AWS 172.16.0.0/16 via FW1 172.30.0.0/16 via Azure 172.16.0.0/16 via FW2 VLAN X VLAN Y ⽮印の通信経路の設定はスタティック ルートか⾃動学習(BGPとOSPFなど)で与える
  219. 物理サーバーの設計 • 今は「仮想化」を前提に考える。ベアメタルは特殊な状況のみ • 基盤導⼊後に拡張することを前提に考えておく(ネットワークのア ドレス空間の⼤きさ、物理スペース、電⼒、エンジニアの物理と 設定作業のコストなど) • ミドルレンジより性能が上がっても落ちても「処理能⼒あたりの 購⼊価格」が⾼く傾向、台数が増えると維持コストが増える

    • 物理より上の価格(ライセンス代など)が⾼ければ、⾼いサーバーで コストを圧縮したほうがよい。普通はコスパでミドルレンジを選 び、予算がなければローエンドを選ぶ。ミッションクリティカル 環境はハイエンド • ワークロード(負荷)に応じたサイジング(台数や構成決定)をする
  220. 物理サーバーの設計例 ハイパー バイザー ハイパー バイザー 仮想化基盤のクラスタ 物理サーバー 物理サーバー SAN Switch

    San Switch Network Switch Network Switch 上流 ネットワーク ストレージ ストレージ VM VM VM VM VM VM VM VM VM VM VM VM VM VM ... 上流 ネットワーク 物理サーバー 物理サーバー Network Switch Network Switch プライベートクラウド VM VM VM VM VM VM マネージドDB その他機能 レガシーな「3Tier」と呼ばれる構成 構築と拡張が難しい 現在主流になりつつあるプライベートクラウド⽅式 全般的に3Tierよりも簡単(個⼈で作る⼈もいるぐらい) ... コン テナ コン テナ コン テナ コン テナ
  221. 障害と保守 • 機械は壊れるもの • ソフトウェアにはバグがある • エンジニアは知らないことが多いし間違えることも多い • アプリでバグがおきるようにインフラにも障害が発⽣する •

    障害に対応するように以下が必要 • ハードウェアの冗⻑化(2つ構成で組み、1つ壊れても動き続 ける) • 冗⻑化と故障を想定した設計のソフトウェア • 障害発⽣時の対処法(知識かマニュアル) • 保守契約(ハード交換や障害復旧の⼿伝い) • ハードやソフトに詳しくなりたいならきちんとした保守(技術サ ポート)の仕事から始めるとよいかも。著者も経験者
  222. ストレージの設計 • ⼤前提として以下の3種類を区別する • サーバー内のSSD。⾼速だが死ぬと終わるし融通が利かない • NAS(共有ファイルサーバー, NFS/SMB) • 外部ブロックストレージ(SCSI,

    iSCSI) • サーバー冗⻑化には外部ストレージが必要なことが多い • SCSI(SAN)は⼤企業向けだが、iSCSIは⼩規模向け • 環境の引っ越しをしにくくなる第⼀要因はストレージ(DB含む) なので最初から移⾏プランを考えておくのがよい • ストレージとSANの障害はインフラ障害で最もダメージが⼤き いのでデリケートな箇所。基盤のパフォーマンスのボトルネッ クにもなりがち。バックアップ必須 • ストレージとSANのエンジニアは⽐較的レアキャラ
  223. データベースの設計 • 2種類のDBエンジニア • アプリより: アプリが使いやすいテーブルの設計 • インフラより: 複雑なクエリーや、冗⻑化やバックアップ •

    純粋な「データベースだけをやるエンジニア」は少ないのでア プリであれば開発(アプリ側がDBをどう使うか)を先に学び、イ ンフラであればDBが動くサーバーレベルを先に習得するのが 個⼈的推奨 • SQL⾃体に詳しくなる(知っていてあたりまえ)というより、運 ⽤(クローンやバックアップ、スケールアップ)周りの知識が特 に求められる • DBの障害(システム全停⽌)でもデータは絶対になくさないこと
  224. バックアップの設計 • 冗⻑化(Raidなど)はバックアップではない • 仮想化のスナップショットもバックアップではない • 集約箇所やシステムごとなくなる可能性を考慮しておくこと • バックアップのレベル •

    ファイルレベル(以下のどれかの付属機能なことが多い) • OSレベル • 仮想環境レベル • ストレージレベル • バックアップ取得時間とバックアップからの復旧時間もだいじ な指標なので、検証環境で実測しておくこと(バックアップはあ るけど戻しに2⽇かかります。などということも。。。)
  225. パブリッククラウドの設計 • 「とりあえずOSを使いたい」という状況でインフラの構築と運 ⽤が不要で簡単に使えるというメリットがある • 動かすサービスをクラウドに適した設計に変更し、インスタン スを使い捨て(スケール含む)にできることが望ましい • マネージドサービスを使うことで「DBなどの⼿がかかるマシ ンのおもり」をへらすことができる

    • インターネットに近いのでウェブサービスに強い • 「従来の業務⽤VMを24時間稼働させる」⽬的でパブリックク ラウドを使うと割⾼になることが多いので注意が必要 • 注意:「特定パブリッククラウドのシステムにだけ強い」という エンジニアは⻑期的には不安定だと思われる。クラウドにも役 ⽴つベーシックなLinuxと開発スキルにも投資すべきかも
  226. パブリッククラウドの設計例 パブリッククラウド基盤 インスタンス (ウェブサーバー) マネージドDB インスタンス (アプリサーバー) バックアップ インスタンス (アプリサーバー)

    インスタンス (ウェブサーバー) CDN(Cache) + HTTPS Offload ドメイン名 RDS S3 EC2 Cloud Front Route53 インターネット ユーザー 名前解決 アクセス サービス名にはAWSを使ったが、基本機能程度の利⽤であれば 主要クラウドであれば似たサービスを持っている。 これ以外にもサーバーレスや各クラウド独特の機能は多い。 初⼼者は汎⽤(どのクラウドでも使える)機能から学ぼう プライベートネットワーク 広いエリアからの⼤量のアクセスに 強いが、特定の拠点(会社など)からの ⼤量のアクセスには弱い。 ⾃社ファイルサーバーなどはクラウド ではなく拠点内に置くのが⼀般的。
  227. プライベートクラウドの設計 • パブリッククラウド相当のものを社内に構築する • レガシーな3Tier構成やOpenStack(難しいので専任エンジニア が多数必要)より、NutanixのHCIなどの構築と運⽤が簡単なも のがよい • 展開する台数が多い場合は仮想マシン(インスタンス)単価で考 えると、パブリッククラウドより安価な傾向(1/2

    - 2/3程度) • 初期投資が必要なので⼩規模な場合はパブリッククラウドより も⾼額になる傾向 • 24時間稼働のエンタープライズアプリや、⾼トラフィックが必 要なVDI、⾃由な操作が求められる開発基盤などに適している • 注意: プライベートクラウドはパブリッククラウドに⽐べると 設計と運⽤がきちんとできるインフラエンジニアが必要
  228. プライベートクラウドの設計例 物理基盤(電気、ネットワーク) VM VM VM VM VM VM VM VM

    VM VM VM VM VM VM VM VM VDIサービス (管理された⼤量のユーザーマシン) ⼤量の社内通信 (40G以上の⾼速なLAN) ⾃社設備(オフィス、データセンター) VM VM VM エンタープライズ アプリケーション (会社の重要システム) DB バック アップ プライベートクラウド基盤 ⼤量のユーザー(社員) 社内通信 秘匿データ VM VM VM VM インターネット Firewall セキュリティ管理 Firewall セキュリティ管理 VPN 社外の ユーザー (社員)
  229. 構成管理ツール • 主にサーバーレベルでのインフラ構築/変更作業は「構成管理 ツール(Ansibleなど)」を使うことで省⼒化できる • 定義した構成にサーバーを⾃動設定(初期構築/変更)してくれる サーバーA サーバーB サーバーC 構成ファイル

    (Yamlで定義) Ansibleを実⾏ Ansibleが⾃動設定 必ず構成管理ツールで 全ての設定をしないと いけないわけではない 著者の利⽤例 • 構成管理ツール: Dockerホスト やK8s環境の構築(⼟台) • コンテナ: アプリケーションの 構築
  230. インフラの⾃動化(1) • ITインフラの仕事には定型業務が多い • 新規ネットワークを作成した際の機器の設定変更 • 仮想マシンを作成する際の設定作業 • マシンやログの監視とアラートの作成 •

    決まりきったことを毎回⼈間がやるのではなくプログラムに実 ⾏させることで労⼒削減ができる • 定形作業(⼿続きの流れが明確)以外は⾃動化できない • システム開発には「開発とインフラの両⽅の知識」が必要 ⼈海戦術の廃⽌ プログラム ITインフラ 操作省⼒化 ⾃動実⾏(定期/イベント駆動)
  231. インフラの⾃動化(2) • 管理者が増えると作業が遅くなる。マシン展開の例 1. ネットワークエンジニアが場所とアドレスを決める 2. セキュリティエンジニアがポリシーを設定 3. サーバーエンジニアがインスタンスを作成 4.

    アプリエンジニアが環境をもらえる 5. 監視員がリソース利⽤状況をチェックして使いすぎを警戒 • ⾃動化された例。インスタンス構成のプラン化と⾃動作成 1. アプリエンジニアが必要なインスタンスの種類を選ぶ 2. システムが機械的にインスタンスを作成 3. アプリエンジニアが環境をもらえる 4. システムがクオーターを超えた利⽤やアイドルを検知した らユーザーと管理者に通達
  232. インフラの⾃動化の構築例 VM VM VM VM 基盤構築アプライアンス クラウド基盤 (Nutanix) Foundation VMs

    基盤のイメージング 物理ネットワーク設定 基盤の初期設定 (論理ネットワーク等) 仮想マシン管理 (クラウドレベル) 仮想マシン設定 (OSレベル。Ansible) Nginx(Reverse Proxy) + Node(SPA) DB (基盤情報) 物理電源操作 (Power-on/off) ベアメタルサーバーへの クラウド基盤インストール REST API IPMI, SSH SSH, Ansible REST API ⾃動化の利⽤者 L3物理 Switch 物理ネットワークの結線 マイクロサービス設計の ブラウザアプリ SSH, API SSHにはPythonのParamikoを利⽤ するのがおすすめ。 ローカルコマンドと同じ感覚で リモートでのコマンド発⾏が可能 ブラウザGUI 仮想マシンレベル の⾃動化 基盤レベル の⾃動化 ネットワークレベル の⾃動化 Docker (VM設定後)
  233. インフラのおかね • 買う価格: 物理機器やライセンスなどのコスト。性能と安定性及び 価格を考慮して構成を決める • 維持する価格: 買った後に維持するコスト。⼈件費や保守費。複雑 なシステムだと⾼くなる •

    「常に⾼機能/⾼性能であればいいわけではない」ことに注意。た とえば画像編集の素⼈は⾼いお⾦を払ってPhotoshopを買うよりも 安いiPhoneアプリのほうがうまく使えるのと同じ。必要になって からPhotoshopを買えばいい。必要な機能を⾒極めて「可能な限り シンプルなものを選ぶ」ことを⼼がける • オープンソース vs ベンダー製品: オープンソースは素晴らしいが、 それを採⽤することによる「構築コスト、運⽤コストの増加、サ ポートを得ることが難しい」点を懸念すること。全てのオープン ソースが著名なオープンソース相当のレベルと思わないこと
  234. 開発やインフラの専⾨性を変えることについて • キャリア的に袋⼩路に陥ったと思ったら「今の⾃分が得意なこと」 から「半歩だけ外(完全に別領域ではない)」に出てみる • 著者の場合はデータセンターというカテゴリで「ネットワークエン ジニア(4年) -> サーバー仮想化エンジニア(4年)」とキャリアチェン ジし、クラウドという観点で「インフラエンジニア(合計8年)

    -> DevOpsエンジニア(現在1年め)」とキャリアチェンジしている • 正しいタイミングで正しい場所に移れるように⾃分が関わるIT技術 全般についてはエキスパートレベルではなくとも「素⼈ではない」 状態にしておくのが⼤事 • 指標として業界の最⾼峰と呼ばれる⼈達と会話して「なんか違う な」「⾃分のほうができる(たいていは勘違い)」と感じたら少し別 の領域へ移ることも検討する
  235. IT系の資格について • IT資格は特定領域を包括的に勉強するには役⽴つ(投資的価値) • 資格を取るだけでは本当のエキスパートにはなれない。たとえば 著者が社会⼈4年⽬のときに「Cisco CCIE」「RedHat RHCE」 「VMWare VCAP」「Oracle

    Java Gold」と広いエリアの⾼ランク の資格を持っていた。知識はあったが技術⼒評価では社内の最下 位職位の下位20%程度。知識と技術⼒は別ものと割り切ること • 戦略的に適切なタイミングで転職やポジションの変更をおこなう ことで机上の知識を現場スキルに落とし込める • 英語ができると国内でもキャリアの選択肢が増えて、ライバルが ⼤幅に減るので、個⼈的には既にプロ以上のレベルの⼈はIT系より も英語の勉強のコスパのほうがはよいかもしれないと思う
  236. 歴史編: 開発とインフラの技術変遷 • このセクションで学ぶこと • おおまかなインフラの歴史 • おおまかな開発の歴史 • ペット

    vs 家畜 • クラウドの抽象化 • トレンドを読むことで将来に備える • 終わりに SECTION 05
  237. おおまかなインフラ基盤の歴史 • - 2005ベアメタル • 全OSを直接ハードにインストール • 2000 - :

    サーバー仮想化(3Tier) • ハード上のハイパーバイザーに複数のOSをインストール • 2010年までは主流だったが構築と維持コストが低いパブリック/プラ イベートクラウドに置き換えられることが現在は多い • 2010 - : パブリッククラウド • 開放されているコンピュート資源をお⾦を払って借りる • ⾃分で構築しないのでメンテナンスコストが下がる • 2015 - : プライベートクラウド • 構築と維持が難しいサーバー仮想化を簡単に実現できる • パブリッククラウド相当のものを⾃組織のために構築する • 2020 - : ハイブリットクラウド • 複数のパブリッククラウドとプライベートクラウドのミックス構成
  238. おおまかなインフラ基盤の歴史(図) HW HW HW OS OS OS HW HW HW

    OS サーバー仮想化 ストレージ OS OS OS OS OS OS OS HW HW HW OS OS OS OS Managed Services SAN HW HW HW OS OS OS OS Managed Services ベアメタル 2000 ~ 2010 ~ 2015 ~ 2020 ~ サーバー仮想化 クラウド 2020 ~ パブリック プライベート ハイブリット OS OS OS OS HW HW HW OS OS OS ハイブリット 2020 ~ ハイブリット
  239. おおまかな開発⼿法の歴史 • - 2000: 開発の対象はデスクトップアプリ中⼼ • Javaによるエンタープライズ向けサービスの開発など • 2000 -

    2010: サーバーサイドレンダリングのウェブサービス開発 • Windows95/98あたりからPCとインターネットが⼀般に普及 • ⼀般ユーザー向けのサービスがブラウザ経由で提供されはじめる • 2005 - : クライアントサイドレンダリングのウェブサービス開発 • ページ遷移を伴わずにページ内容を書き換えるブラウザアプリ • Google Mapなどが広く普及し始める。多くはサーバーサイドレンダリ ングとの併⽤ • 2010 : ⾼速な開発/更新とスケールするウェブサービスの開発 • インターネット上のユーザー数が膨⼤な数となる • ユーザーを集めるために⾼速にサービスを開発/公開を繰り返して、⼤ 量のトラフィックを捌ける設計が採⽤されるようになる • スマホやタブレット向けアプリも⼤きく普及
  240. ペット vs 家畜 • インフラと開発に共通するトレンド。ペットのような環境から家畜のよう な環境へ。 • ペット • ⾮常に⼤事な存在なので、⼿がかかる

    • 数は少ない • 病気になったり死んだりするとダメージが⼤きい • 家畜 • 機械的に管理される存在 • 数は多い • ごく⼀部が病気になったり死んだりしても、ダメージは⼩さい • クラウドで「家畜のように扱えるサービスを開発」することで、インフラ 側の構築/運⽤コストを⼤幅に減らせる
  241. HW HW OS サーバー仮想化 OS HW HW OS OS クラウド

    HW HW HW HW クラウド OS OS OS Managed Network Managed Storage Managed Database マネージドサービス • マシン台数の削減 • 複雑な操作を削減 コンテナ サービス開発⼒ 維持管理 変更コスト OS OS OS OS Legacy Backup etc. Legacy Backup etc. VS レガシーな開発と運⽤⽅式 新しい開発と運⽤⽅式 ビジネススピード ペット vs 家畜 (図)
  242. クラウドの抽象化 • 変化が激しい世界ではサービス(⼀般向け、社内向け)を塩漬け させないことが⼤事 • 塩漬けさせないコツ • 「開発して終わり -> 2年後に更新」ではなく運⽤を続けな

    がら開発を継続する • 開発したサービスをすぐに「どこにでも」展開できる • 特定クラウドにサービスを依存させないコツ • 特定クラウドに特化した機能ではなく、汎⽤的な機能を使う • コンテナやオーケストレーションツールをつかって、サービ ス⾃⾝を最初から可搬性が⾼い設計で作る
  243. クラウドの抽象化 (図) HW HW HW HW HW HW パブリック1 プライベート

    HW HW HW パブリック2 Ct2 Ct1 クラウドの抽象化レイヤー(Docker, Kubernetesなど) Ct2 Ct1 開発 本番(ウェブサービス) Ct4 Ct3 開発 Ct4 Ct3 Ct4 Ct4 本番(ビッグデータ処理)
  244. トレンドを読むことで将来に備える • ⾰新的な技術はいきなりは普及しない • その技術が求められている⼟壌が確実にある • 技術が誕⽣してから最先端の⼈達に広がるのに3年以上はか かる。⼀般層に広がるにはもっと時間がかかる • ⽇本で普及する前にアメリカで普及することがほとんど

    • 相当な審美眼がない限りは超最先端を追う必要はなく、超最先 端な技術の多くは普及せず終わる • 「世間に広まりつつあるな」と思ってから「誰にたいして」 「背景」「タイミング」などについて考察してみる • 興味を持てば新しいことは楽しんで学べるし試せる