python入門 : 4日間コース社内トレーニング
TRANSCRIPT
Python 入門
Cisco Systems, Japan TAC 伊藤 裕一 (twi7er: @yuichi110)
本スライドは Cisco Japan 社内で開催した TAC (トラブルシューティングを専門とするネットワークエンジニア) 向
けの python トレーニングを一部修正したものです。
演習込みで 4時間 x 4日 で開催しています
トレーニングの目的
• Python を使えるようになる • プログラミングの概念を理解 • ソースコード(diffなど)を読む • SDN関連のSRをさばく • pythonで解析ツールを作ってチームに貢献
3 Python 入門
トレーニングの対象者
• WindowsなりMacなりを使いこなせるユーザ • プログラミングに興味がある • トレーニングの時間 + 自習時間を持てる • ネットワークエンジニア
4 Python 入門
補足
トレーニングの内容は Alan Gauld さんの Learning to proguram ver 2 を参照して作成。
Python 入門 5
Learning to Program h7p://www.alan-‐g.me.uk/
また、O’REILLY の「初めてのPython」も参照しています。
プログラミングとは
Python 入門 6
プログラミングとは
• 電子機器を「自分が意図する通り」に動かす手段 • 制御文で処理の流れを決める • 各処理は連続した命令で成り立つ
7 Python 入門
Ping の挙動
• ICMP echo を cisco.com に3回送信し、その reply が届いた割合とRTTを算出
Python 入門 8
yuichi$ ping -‐c 3 cisco.com PING cisco.com (72.163.4.161): 56 data bytes 64 bytes from 72.163.4.161: icmp_seq=0 7l=238 cme=149.949 ms 64 bytes from 72.163.4.161: icmp_seq=1 7l=238 cme=149.783 ms 64 bytes from 72.163.4.161: icmp_seq=2 7l=238 cme=149.826 ms -‐-‐-‐ cisco.com ping stacsccs -‐-‐-‐ 3 packets transmi4ed, 3 packets received, 0.0% packet loss round-‐trip min/avg/max/stddev = 149.783/149.853/149.949/0.070 ms
Pingの実装
(1)宛先アドレスを入力(cisco.com) (2)宛先に向けて ICMP echo request を打つ (3)結果を表示 -‐ ICMP echo reply がかえってきた場合 到着時刻 -‐ 送信時刻 の RTT を表示 -‐ ICMP echo reply が帰ってこなかった場合 lost したことを表示 (4) (2)-‐(3) をあと2回繰り返す (5)3回のICMPの結果を集計し、表示
S D
1回目表示
2回目表示
3回目表示
3回の結果を表示
9 Python 入門
echo request
echo reply
PC サーバ
ping の実装
プログラムを組むということ • コードによる建築作業 • 簡単なモノなら少ない知識で作れる • 巨大なモノは高度な専門知識が必要
Python 入門 10
とりあえずの目標地点
PYTHON
Python 入門 11
プログラミング言語
• プログラムを記述するための言語 • コンピュータ(機械語)と人間(自然言語)の仲介言語 • いくつかの種類がある
12 Python 入門
00100101001101010 print('hello world') 'hello world'を表示させる
プログラミング言語 人がやりたいこと 機会が解釈可能な命令
なぜ Python ?
• 文法がシンプルで初心者向け • パワフルな言語 • オブジェクト型指向言語
13 Python 入門
Python 以外の選択肢
• C: ハードに近い概念を意識する必要がある OSに近いことをしないと開発が非効率 • Java: 良い言語だが、覚えることが多い Python のほうが簡単 • その他Script系言語: cisco機器が非サポート • Web系言語: cisco で重要度が低い • その他: 中級者-‐> 上級者 になりたい人向け
14 Python 入門
C, Java, Python の比較 • 現在のディレクトリ以下の構造を書き出すプログラム
Python 入門 15
[/Users/yuichi/Documents/_TRAINING] CCIE Java listFile.py python [/Users/yuichi/Documents/_TRAINING/CCIE] ccie-‐lab.key [/Users/yuichi/Documents/_TRAINING/Java] Java Basic Training Day 2.pptx Java Basic Training Day 3.pptx Java Basic Training Day 4.pptx Java Basic Training Day1.pptx [/Users/yuichi/Documents/_TRAINING/python] adfunc.py dict-‐test.py excepcon.py func.py
_TRAINING CCIE
Java
Python
ccie-‐lab.key
Java Basic Training Day2 .....
adfunc.py .....
lismile.py プログラムの出力
C 言語での例
Python 入門 16
Javaでの例
Python 入門 17
Python での例
Python 入門 18
IDLE
比較結果
Python 入門 19
• Python は簡単にプログラムが書ける
Python 入門 20
Agenda
• Day1: インストール、基本文法、データ型 • Day2: 続データ型、モジュールと関数 • Day3: オブジェクト指向、アルゴリズム • Day4: 正規表現、エラー処理、データベース
21 Python 入門
Agenda Day 1
• Python のインストール • プロンプトとIDLEの使い方 • 型と変数 • 条件分岐 • ループ • 標準入力 • 関数とモジュールの利用 • 演習
22 Python 入門
PYTHON のインストール
Python 入門 23
Python Installacon • Macはターミナルで最初から使える • 開発環境を作るためにバイナリからインストール • バージョンは 2.7 系の最新版
Python 公式ページ
24 Python 入門
Python のバージョン
• Version 3.3.x: 最新版のバージョン • Version 2.7.x: まだまだ主流のバージョン • Cisco: 2.7.x を利用
25 Python 入門
Windows へのインストール
Python 入門 26
Mac へのインストール
Python 入門 27
PROMPT と IDLE の利用
Python 入門 28
Prompt で python を呼び出す
• Windows: Start -‐> python2.7 (command line) • Mac: terminal で python2.7 と打つ
Python 入門 29
Prompt で python を呼び出す
• Windows: Start -‐> python2.7 (command line) • Mac: terminal で python2.7 と打つ
Python 入門 30
Prompt の利用
• Prompt で以下のようなコマンドを発行してみる
Python 入門 31
print('hello world') 6+1*3 (6+1)*3 5/2 5/2.0 5/0
print("{} + {} = {}".format(2,3,5)) print """hello I love Cisco I love Python too """ import sys sys.exit()
コマンドで script ファイルを実行 • ファイルに処理を記述し、それをコマンドで実行 • Python のスクリプトファイルは拡張子が .py • Windows は必要あればpython をパス指定する
Python 入門 32
print “hello Cisco” print “hello Python” print “done!”
test.py Mac の実行画面
$ python ファイル名
コマンドで script ファイルを実行 • ファイルに処理を記述し、それをコマンド実行 • Python のスクリプトファイルは拡張子が .py • Windows は必要あればpython をパス指定する
Python 入門 33
※ 時間のあるときにパスの設定を加えておくと楽
Windows の実行画面
IDLE
• このトレーニングのメインの開発環境 • Windows: startup -‐> python -‐> idle • Mac: applicacon -‐> python -‐> idle
Python 入門 34
IDLE プロンプト画面
IDLE の利用 • IDLE のエディタを使った開発 • メニューより先ほど作った test.py を開く • エディタ画面を選択し、F5 を押すと実行
Python 入門 35
コメントアウト
• コメント: プログラムの注釈。処理系に無視される • 一行のコメントアウト: # コメント文 • 複数業のコメントアウト: """ 複数行指定可能 """
Python 入門 36
型と変数
Python 入門 37
型 • データには型(種類)がある • 整数型(Integer): 1,5,-‐1 • 文字列型(String): "Cisco", "Python"
Python 入門 38
型と制御
• 型とプログラムの制御には密接な関係がある – 文字列を数字で割ることはできない – 文字列と数字を結合できない
• よくある処理は手続き方法を覚える – 文字列と数字の結合は、数字を文字列に変換し
てから結合する
Python 入門 39
変数 • 変数: Dataを保存する容器
Python 入門 40
5
print(5) => 5 x = 5 print(x) => 5
5 変数 X データ(定数 5) 代入
print ( ) 5
print ( ) 5
変数と型
• 型宣言: この変数はこの型を格納するという宣言 • Python の変数には型宣言がない • 変数にどのような型でもいれられる
Python 入門 41
int x = 5; x = "Java" //Error
x = 5; print(x) x = "Python" # OK print(x) Java
Python
Python の型
• 組み込み型 – 数字: int, float など – 文字列: 英語、日本語 – リスト: 配列に似ている – Bool: True or False – 関数: Python では関数も型の一つ – その他
• ユーザが定義した型(Class)
Python 入門 42
数字
• 整数(int)と実数(float)、複素数がある • 上限は気にしなくてよい
Python 入門 43
使える演算子 説明 M + N 足し算 M – N 引き算 M * N かけ算 M / N 割り算 M % N 剰余(あまり) M**N べき乗 (M * M * M … をN回)
>>> 1234567 * 3456789 4267637625363 (42兆)
数字
• インクリメントは使えない • 代入演算子で代用
Python 入門 44
演算子 説明 M += N M = M + N M -‐= N M = M – N M *= N M = M * N M /= N M = M / N M %= N M = M % N
Java int i = 0 i++
Python i = 0 i += 1 ß i = i+1
文字列型 • 3種類の記述方式
Python 入門 45
シングルクオテーション string = ‘ Hello Python’ ダブルクオテーション string = “Hello Python” トリプル・ダブルクオテーション string = “””Hello World Hello Python # this isn’t comment Hello CIsco”””
改行や特殊記号も そのまま文字列に。
文字列型
• 文字列も演算記号を使える
Python 入門 46
print “I love “ + “python” => “I love Python” print “hello “ * 3 => “hello hello hello “ print “I say “ + (“hello “ * 3) => “I say hello hello hello”
x = “I love “ y = “Python” print(x+y) => "I love Python"
Bool型
• いわゆる True, False を持つ型 • If 文などの制御分でよく利用される
Python 入門 47
演算子 意味 A and B A も B も True なら True A or B A か B が True なら True A == B A と B が同じなら True A != B A と B が違えば True A <> B 同上 not A A が False なら True
Bool型
• いわゆる True, False を持つ型 • If 文などの制御分でよく利用される
Python 入門 48
演算子 意味 A > B A が B より大きければ True A >= B A が B 以上なら True A < B A が B より小さければ True A <= B A が B 以下なら True
List型 • 拡張ができる配列 [x,y,z] という形式で作成 • 演算子 [x] で、配列の x 番目にアクセスする • 何番目か(index)の指定は0から数える
Python 入門 49
list1 = [3,4,5] print(list1) => [3, 4, 5] list2 = [“I”, “Love”, “Python”] print(list2[2]) => “Python”
list3 = [[1,2], ['A',”B”]] print(list3) => [[1, 2], [“A”, “B”]] print(list3[1][1]) => “B” print(list3[5][0]) => # index out of range の Error 表示
List型 • 配列長を取得: len(X) • オブジェクトの index を取得: index(X) • 配列にオブジェクトを追加: append(x) • 配列からオブジェクトを削除: del
Python 入門 50
list1 = [1,2,3,4,5] print(list1.index(3)) => 2 print(len(list1)) => 5
list1.append(6) print(list) => [1,2,3,4,5,6] del list2[1] print(list2) => [1,3,4,5,6]
その他の型(後日)
• Tuple • Dicconary • File • Dates, Times • ユーザ定義の型(クラス) • 関数
Python 入門 51
演習1
Python 入門 52
演習 (演算)
• 以下の図形の面積をプログラムで計算してください
• 余裕があれば、import 以外は一行(ワンライナー)としてください
Python 入門 53
4cm
3cm
10cm
5cm 2cm
8cm
半径 2cm
※ 円周率は math module の π を 使ってください
>>> import math >>> math.pi 3.141592653589793
演習 (ドキュメント)
• 数字 1984 と文字列 “cisco” を文字列結合し、表示してください (そのまま結合するとエラーになるので、数字を文字列に変換する必要あり)
• ググるのではなく、以下のページから必要な「組み込み関数」を探してください
Python 入門 54
Python 標準ライブラリ h7p://docs.python.jp/2/library/index.html
(英) h7p://docs.python.org/2.7/library/index.html#python
制御構造
Python 入門 55
制御構造
• 簡単なバッチ処理: 上から下に一行ずつ実行していく
• プログラミング – 上から下に実行していく – 決められた文法と単語(予約語)で処理方法を記述 – if文、loop文などでプログラムの流れを制御する – クラスや関数などを使って処理をまとめる
Python 入門 56
プログラムの処理の流れ
• プログラムの基本的な流れは上から下へ
Python 入門 57
Step1
Step2
Step3
予約語 • 空白で区切られた単語の連続でプログラムを記述する • 予約語: プログラムの制御のための「特別」な単語 • 予約語以外: 定数、変数、関数、クラス名など自由につ
けられる名前
Python 入門 58
プログラムの制御 if, for, while 宣言 class, def
予約語の例
コードブロックとインデント
• C,Javaなど: {} という記号で処理をまとめる • Python: インデント(字下げ)で処理をまとめる
Python 入門 59
for(int i=0; i<10; i++){ if(i%2 == 0){ System.out.println(i + " is even"); }else{ System.out.println(i + " is odd"); }
} System.out.println("done");
for i in range(10): if(i%2 == 0): print("{} is even".format(i)) else: print("{} is odd".format(i)) print("done")
Java Python
条件分岐
Python 入門 60
条件分岐 IF
• 特定の条件を満たす場合のみ処理を行う
Python 入門 61
Step1
Test Condicon
Path2 Path1
条件分岐 IF
• 特定の条件を満たす場合のみ処理を行う
Python 入門 62
if(条件A): 条件AがTrueなら実行 …. elif(条件B): 条件AがFalseかつ条件BがTrueなら実行 …. else: 条件AもBもFalseなら実行 …. if の条件分岐の結果に関わらず実行 ….
条件分岐 IF
• Python のコード
Python 入門 63
i = 5 if (i > 10): print “This is never printed” else: print “This might be printed”
(補足)条件分岐 SWITCH-‐CASE
• 実装されていない。If文で作る
Python 入門 64
switch(条件){ case A: 条件がAの場合の処理 break; case B: 条件がBの場合の処理 break default: それ以外の場合の処理 }
if(条件 == A): 条件がAの場合の処理 elif(条件 == B): 条件がBの場合の処理 else: それ以外の場合の処理
Java Python
ループ処理
Python 入門 65
ループ処理 for
• 特定の処理を決められた回数こなす • Pythonではリストの走査に使用
Python 入門 66
Repeated Steps
Test Condicon
リストとfor
• CやJavaなどのforとは別物 • CやJavaと同じような使い方もできる
Python 入門 67
A B C D リスト
(1) Aを使って処理を実行 (2) Bを使って処理を実行 (3) Cを使って処理を実行 (4) Dを使って処理を実行
for文の書式
Python 入門 68
for 変数 in リスト: 実行する処理
リストの0番目を変数に代入して処理を実行 リストの1番目を変数に代入して処理を実行 .....
string = "" for char in ["H","e","l","l","o"]: string += char print(string)
>>> Hello
forによる一定回数の処理
• 一定回数の処理には range 関数を併用 • range(x): 0 から x-‐1 までの連番のリストを返す • range(x,y): x から y-‐1 までの連番のリストを返す
Python 入門 69
for(int i=0; i<10; i++){ System.out.println(i) }
for i in range(10): print(i)
Java Python
(補足)イテレータ
• python の for は iterator と呼ばれる仕組み • タプルや、文字列、辞書なども key として使える
Python 入門 70
for char in "hello": print(char) => h e l l o
ループ処理 while • 特定条件を満たすまでループを繰り返す • 「〜 の間はずっとループ」というようなイメージ • For ほどは使われない
Python 入門 71
while に適したループ処理
• 何周すればいいか分からない処理に使う • 無限ループにならないように注意が必要
Python 入門 72
x = 6789329 i = 1 while(2**i < x): i += 1 print("2 ^ {} > {}".format(i,x))
与えられた数を2進数で表現するのに必要な桁数を求める
2 ^ 23 > 6789329
演習2
Python 入門 73
演習 (条件分岐)
• 1 – 100 までの偶数の値を持つ List を作成する • 1 – 10000 までの素数を表示する。 素数: 1と自分以外の整数で割れない整数
• Fizz buzz を 100 まで行う 1-‐100までを表示。 3で割り切れる時は fizz, 5で割り切れる時は buzz と表示 3でも5でも割り切れる時は fizz buzz と表示する
Python 入門 74
関数とモジュールの利用
Python 入門 75
関数の呼び出し • 特定の処理を行うための呼び出し口 • 関数名に引数を与えて呼び出す • 関数は返り値を返す
Python 入門 76
関数名(引数)
返り値
>>> length = len([1,2,3,4,5]) >>> print(length) 5
関数で複雑な処理を隠す
• 関数を使うことでコードがわかりやすくなる
Python 入門 77
i = -‐5 if(i<0): i = -‐i print i => 5
ぱっと見、何をやって いるか分からない
i = -‐5 i = abs(i) print i => 5
関数名ですぐ「絶対数を 得るプログラム」とわかる
関数でコードの重複を減らす
• コードの重複をなくし、保守性を向上させる
Python 入門 78
i = -‐5 j = 8 If(i<0): i = -‐i If(j<0): j = -‐j print(i) => 5 print(j) => 8
i = -‐5 j = 8 関数 abs の定義 print(abs(i)) => 5 print(abs(j)) => 8
モジュール
• モジュールはプログラムの整理整頓手法
Python 入門 79
vs
モジュールがない モジュールを利用
モジュールによる階層化
• 機能ごとによるプログラムの整理
Python 入門 80
math file system
os 自分が作成
組み込み (デフォルトで 使える)
モジュールへのアクセス
• モジュールを利用するには import する必要がある
Python 入門 81
>>> random() Traceback (most recent call last): File “<stdin>”, line 1, in <module> NameError: name 'random' is not defined >>> import random >>> random.random() 0.6019003331149728
import していないと 利用できずにエラー
「モジュール名.関数」 でモジュールの関数を利用
関数とモジュールの作成
• 作成の仕方などについては day2 で扱う
Python 入門 82
標準入力
Python 入門 83
標準入力
• ユーザからの入力を受け取る
Python 入門 84
input = raw_input() ユーザからの入力を待つ
print(input) => “hello”
“hello” と入力される 入力待ちが解除される
標準入力
• 無限 echo プログラム
Python 入門 85
while(True): print “please input:” input = raw_input() print input
※ Ctr-‐C で抜ける
コマンドライン引数
Python 入門 86
コマンドライン引数
• スクリプトを「パラメータ」つきで起動する • スクリプト内に直接書き込むより融通性がある
Python 入門 87
pingのプログラム (1) 宛先を引数で受け取る (1) 宛先へ icmp echo request を送る (2) 宛先から icmp echo reply を受け取る (3) 受信結果を表示
ping.py
cisco.com
python.org
#python ping.py cisco.com
#python ping.py python.org
sys.argv • コマンドライン引数にアクセスするには sys モジュー
ルを import する • 引数がargvにリストとして格納されている
Python 入門 88
import sys print("-‐-‐-‐-‐ args -‐-‐-‐-‐") print(len(sys.argv)) print(sys.argv[0]) print(sys.argv[1]) print(sys.argv[2]) print("-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐") a = int(sys.argv[1]) b = int(sys.argv[2]) print("{} + {} = {}".format(a,b,a+b))
$ python test.py 1 5 -‐-‐-‐-‐ args -‐-‐-‐-‐ 3 test.py 1 5 -‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐ 1 + 5 = 6
test.py
出力
演習3
Python 入門 89
演習(標準入力)
• ユーザが入力した文字列の文字数を表示するプログラムを作成
• 入力は何度でも受け付けるが、文字列 exit を受け取ると終了すること
Python 入門 90
次回予告
• 関数を作る • 高度な型を使う • ファイル処理
Python 入門 91
• できるようになること – テキスト処理によるログの解析 – OSを操作するためのシェルスクリプトの作成
Python 入門 92
Python 入門 Learning to Program
For Cisco Network Engineers Day 2/4
Japan TAC DC Team.
Yuichi Ito
Agenda Day 2 • Python • 続データ型 • 関数の作成 • モジュールの作成 • 演習1(Map) • 文字列処理 • ファイル処理 • OSコマンドの利用 • 演習2(ログ解析、Linuxのスクリプト)
94 Python 入門
PYTHONの歴史
Python 入門 95
Python • 登場時期: 1990年 • 設計者: グイド・ヴァンロッサム • 型付け: 強い動的型付け
Python 入門 96
• 1.x: 1994年 • 2.x: 2000年。メジャー言語化 • 3.x: 2008年。後方互換性なし
続データ型
Python 入門 97
Python の型
• ユーザ定義の型(Class) • 組み込み型(pythonが提供)
Python 入門 98
数値型 -‐ 整数 -‐ 長製数 -‐ 浮動小数点 -‐ 複素数 シーケンス型 -‐ 文字列 -‐ リスト -‐ タプル -‐ xrange型
マップ型 -‐ 辞書 ファイル型 -‐ ファイルオブジェクト
用語
• オブジェクト • インデックス • エレメント • リテラル • 代入 • 引数 • 返り値
Python 入門 99
辞書型 • Key と Value のペアを保持するデータ型 • KeyからValueを取得する
Python 入門 100
Apple : Red
Lemon : Yellow
Grape : Purple
Dicconary
Appleの色は?
Red !!
X : Y
追加 取得
辞書の操作
• 辞書の形式: {key1:value1, key2:value2} • 値の取得: 辞書オブジェクト[key] • 値の追加(更新): 辞書オブジェクト[key] = value • 値の削除: del 辞書オブジェクト[key] • key一覧の取得: 辞書オブジェクト.keys() • keyを持っているか: 辞書オブジェクト.has_key(key)
Python 入門 101
辞書型の操作 # 辞書オブジェクトを作成 >>> d = {"Apple":"Red", "Lemon":"Yellow"} # key から Value を取得 >>> d["Apple"] 'Red' # 辞書オブジェクトに要素を追加 >>> d["Grape"] = "Purple" >>> d {'Grape': 'Purple', 'Lemon': 'Yellow', 'Apple': 'Red'} # 辞書オブジェクトの要素を削除 >>> del d["Apple"] >>> d {'Grape': 'Purple', 'Lemon': 'Yellow'}
Python 入門 102
辞書とリストの違い
• リスト型は線形にデータ保持 • 辞書型はハッシュを利用してデータ保持 • 要素数が増えた場合の検索が高速
Python 入門 103
ハッシュの仕組み
辞書型のサンプル • Arp Table を List と 辞書で実装(クラスなし)
Python 入門 104
ipList = [] macList = [] def registerMAC1(ip, mac): if ip in ipList: index = ipList.index(ip) macList[index] = mac else: ipList.append(ip) macList.append(mac) def getMAC1(ip): if ip in ipList: index = ipList.index(ip) return macList[index] else: return None
arpTable = {} def registerMAC2(ip, mac): arpTable[ip] = mac def getMAC2(ip): return arpTable.get(ip)
Listによる実装: 探索コスト Order(N) 辞書による実装: 探索コスト Order(1)
Listによる実装
辞書による実装
セット
• 重複、順序のない複数のデータを保持する型 • Value のない Map に近い
Python 入門 105
>>> a = set([1,2,3,4,5,3,2]) >>> print(a) set([1, 2, 3, 4, 5])
>>> a = set("hello") >>> b = set("world") >>> print(a) set(['h', 'e', 'l', 'o']) >>> print(b) set(['d', 'r', 'o', 'w', 'l']) >>> print(a & b) set(['l', 'o'])
セットの操作
• 初期化: set(シーケンス型のオブジェクト) • 追加: setオブジェクト.add(value) • 削除: setオブジェクト.discard(value)
Python 入門 106
タプル型 • 不可変なデータ構造 • 要素の数が決まった「一組のデータ」
Python 入門 107
Link down
OSPF Neighbor XXX down
Admin up
Link up
OSPF Neighbor XXX up
....
# show logging
行数(要素数)は伸びる => List型
社員情報
Yuichi
Ito
2011-‐04-‐01
Tokyo
TAC
行数(要素数)は伸びない 順不同 => Tuple 型
タプル型の操作
• Tuple の作成: tupleObject = (elem1, elem2,...) • 要素の取得: item = tupleObject[index] • 複数の要素の取得: • 要素の更新: できない
Python 入門 108
タプル型の操作 # Tuple の作成 >>> yuiito = ("Yuichi", "Ito", 2011) >>> yuiito ('Yuichi', 'Ito', 2011) # 要素の参照 >>> yuiito[1] 'Ito' >>> yuiito = ("Yuichi", "Ito", 2011) >>> (name, famName, year) = yuiito >>> year 2011 # 要素の更新 >>> yuiito[2] = 2009 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment
Python 入門 109
タプル型サンプル
• データ構造を作成可能(クラスの簡易版) • 不可変なオブジェクト
Python 入門 110
def getMinMax(numList): minNum = numList[0] maxNum = numList[0] for n in numList: if n < minNum: minNum = n if n > maxNum: maxNum = n return (minNum, maxNum)
None
• 何もないことを明示するための特殊な型 • C, Java の NULL とは違うが、ほぼ同じ
Python 入門 111
関数の概念
Python 入門 112
関数
• 特定の処理を行う受付窓口 • 細かい処理を隠蔽
Python 入門 113
成果物(パン) プログラム(パンの作成関数)
あんぱん
カレーパン
あんぱんの製造工程
Python 入門 114
こねる
材料を用意 粉などをまぜる
こねる
発酵
生地を整形 放置
あんぱんの形作り
生地を適量とる 餡をいれる
丸める
焼く
オーブンを温める パンをいれる 一定時間待つ
取り出す
カレーパンの製造工程
Python 入門 115
こねる
材料を用意 粉などをまぜる
こねる
発酵
生地を整形 放置
カレーパンの形作り
生地を適量とる カレーをいれる
丸める
揚げる
油を温める パンをいれる 一定時間待つ
取り出す
関数を使わないベタ書き
• 処理の区切りが分かりづらく、重複が多い
Python 入門 116
材料を用意 粉を混ぜる
こねる 生地を整形
放置 生地を適量とる
餡を入れる 整形
オーブンを温める パンをいれる 一定時間待つ
取り出す
AnPan.py
材料を用意 粉を混ぜる
こねる 生地を整形
放置 生地を適量とる カレーを入れる
整形 油を温める
パンをいれる 一定時間待つ
取り出す
CurryPan.py
関数による処理単位の整理
Python 入門 117
こねる 発酵
あんぱん形作り 焼く
カレーパン形作り 揚げる 重複の排除
関数によるコードの整理
Python 入門 118
関数: こねる 材料を用意 粉を混ぜる こねる 関数: 発酵 生地を整形 放置 関数: あんぱん形作り 生地を適量とる 餡を入れる 整形 関数: 焼く オーブンを温める パンをいれる 一定時間待つ 取り出す <以下省略>
Pan.py (つづき)
関数: makeあんぱん こねる 発酵 あんぱん形作り 焼く 関数:makeカレーパン こねる 発酵 カレーパン形作り 揚げる …..
Pan.py
関数名
• 関数を区別するための名前 • 組み込み関数と被らないように注意が必要
Python 入門 119
引数
• 関数を呼び出す際に関数に与える値 • 仮引数(関数の定義側で受け取る変数) • 実引数(関数の呼び出し側で与える値)
Python 入門 120
def 関数名(仮引数) 関数内の処理
関数名(実引数)
定義
呼び出し
返り値
• 関数で処理されたデータを呼び出し元に返す • 返り値がない場合もある(副作用のみの関数)
Python 入門 121
length = len([1,2,3,4,5])
len([1,2,3,4,5]) length
5 返り値
関数の実装
Python 入門 122
関数作成の書式1
• 引数、返り値なしの書式 • 返り値を明示しない場合は None が返る
Python 入門 123
def 関数名(): 処理
def printHello(): print("hello") printHello() => hello
関数作成の書式2
• 引数、返り値あり • 引数は関数名の後の()に必要な数だけ指定 • 返り値は return で明示的に書く
Python 入門 124
def 関数名(引数): 処理 return 返り値
def adder(a, b): return a + b print(adder(3,4)) => 7
関数のサンプル def power(x): return x*x def absolute(x): if(x<0): return -‐x else: return x print(power(6)) -‐> 36 print(absolute(-‐5)) -‐> 5
Python 入門 125
引数のデフォルト値 • 指定しない場合に設定される引数
Python 入門 126
def func(a, b=1) print(a) print(b) func(5,6) -‐> 5 6 func(5) -‐> 5 5
global
• 関数内でグローバル変数にアクセスするための宣言
Python 入門 127
gv = 0 def func1(): gv = 1 def func2(): global gv gv = 2
print(gv) -‐> 0 func1() print(gv) -‐> 0 func2() print(gv) -‐> 2
演習1
Python 入門 128
演習
• day2-‐1.py.zip をダウンロードし解凍 • text = """〜""" として事前定義されている文字列の
単語出現数をカウントするプログラムを作成 • 「単語:出現数」というフォーマットで出力する • 文字列の分割: 文字列.split() -‐> [単語の配列]
Python 入門 129
dog:1 cat:2 ...
• 出現数が多い単語順にソートして表示する • ヒントは次ページ
発展編
演習補足 • リストを使った簡易ソートアルゴリズム • sort関数を使っても実装可能だが、まだ教えてない
テクニックが必要
Python 入門 130
list1 = [3,6,2,9,0,1,5] list2 = [] while(0 != len(list1)): maxValue = max(list1) index = list1.index(maxValue) list2.append(maxValue) del list1[index] print(list2) -‐> [9, 6, 5, 3, 2, 1, 0]
モジュール
Python 入門 131
モジュールの必要性
• モジュール • ユーザ作成モジュール: 1つのpythonファイル
Python 入門 132
モジュールへのアクセス
• 機能ごとによるプログラムの整理
Python 入門 133
math file system
os 自分が作成
組み込み (デフォルトで 使える)
ユーザによるモジュールの作成
Python 入門 134
ファイル 200行 ファイル 50000行
• プログラムの開発によりコードが巨大に。
モジュールの分割
• 巨大なファイルは「どこに何があるか」わかりにくい • 適切な粒度でファイルを分割する
Python 入門 135
010101010101010101010101010101010010101010101010101010101010101010010101010101010101010101010101010010101010101010101010101010101010010101010101010101010101010101010010101010101010101010101010101010010101010101010101010101010101010010101010
010101010101010101010101010101010010101010101
010101010101010101010101010101010010101010101
010101010101010101010101010101010010101010101
010101010101010101010101010101010010101010101
巨大なファイル 複数の小さなファイル
モジュールの参照
Python 入門 136
010101010101010101010101010101010010101010101
010101010101010101010101010101010010101010101
010101010101010101010101010101010010101010101
010101010101010101010101010101010010101010101
010101010101010101010101010101010010101010101010101010101010101
モジュールを使うプログラム 全体の流れを記述
モジュールを使うプログラム 全体の流れを記述
import
• import することでモジュールが使える
Python 入門 137
import mymath print(mymath.add(2,3)) # -‐> 5 print(mymath.minus(2,3)) # -‐> -‐1 print(mymath.mulcply(2,3)) # -‐> 6 print(divide(2,3)) #ERROR HAPPEN Traceback (most recent call last): File "~/main.py", line 6, in <module> print(divide(2,3)) NameError: name 'divide' is not defined
def add(a,b): return a + b def minus(a, b): return a -‐ b def mulcply(a, b): return a * b def divide(a, b): return a / b
from
• from ファイル名(パッケージ名) import * でファイル名の指定なしに呼び出しが可能になる
Python 入門 138
def add(a,b): return a + b def minus(a, b): return a -‐ b def mulcply(a, b): return a * b def divide(a, b): return a / b
from mymath import * print(add(2,3)) # -‐> 5 print(minus(2,3)) # -‐> -‐1 print(mulcply(2,3)) # -‐> 6 print(mymath.divide(2,3)) # ERROR HAPPEN Traceback (most recent call last): File "~/main.py", line 6, in <module> print(divide(2,3)) NameError: name 'mymath' is not defined
(補足) 循環参照
• ファイルAとBが互いに参照しあっている • 望ましくなく、エラーになる場合がある
Python 入門 139
import file2 print("file1")
import file1 print("file2")
file1.py
$ python file1.py file1 file2 file1
file2.py なんで2回?
文字列
Python 入門 140
文字列の処理1
• 文字列の作成: str1="文字列", str2=' 文字列' • 文字列の結合: 文字列 + 文字列 • エスケープ記号: \ に続けて特定の文字 • 長さの取得: len(文字列)
Python 入門 141
サンプル:文字列の処理1 # 文字列の作成 >>> str1 = "hello" >>> str2 = ' world' # 結合 >>> print(str1 + str2) hello world # エスケープ >>> print(str1 + "\t" + str2 + "\ncisco") hello world cisco # 長さの取得 >>> len(str1) 5
Python 入門 142
文字列の処理2
• 繰り返し: 文字列 * 繰り返し回数 • 文字列化: str(文字列以外のオブジェクト) • 文字列の一文字を取得 文字列[index] • 文字列の範囲取得: 文字列[start:end]
Python 入門 143
サンプル:文字列の処理2 # 繰り返し >>> print("cisco" * 3) ciscociscocisco # 文字列化 >>> print("value: " + str(5)) # 非文字列を文字列化してから結合 value: 5 # 文字列の一文字を取得 >>> "hello world"[4] 'o' # 文字列の一部を取得 >>> "hello world"[4:8] 'o wo'
Python 入門 144
文字列の処理3
• 置き換え: 文字列.replace(old, new) • 分割: 文字列.split("区切り文字") • 含むか: "探す文字列" in "探される文字列" • 位置: 文字列.find("探す文字")
Python 入門 145
サンプル:文字列の処理3 # 置き換え >>> "hello world".replace("world", "python") 'hello python' # 分割 >>> "hello world".split(" ") ['hello', 'world'] # 含むか >>> "hell" in "hello world" True # 位置 >>> "hello world".find("o") 4 >>> "hello world".find("X") -‐1 # 指定した文字列が含まれていない場合は -‐1
Python 入門 146
正規表現
Python 入門 147
正規表現とは
• 文字列"パターン"の定義 • 柔軟に文字列のパターンマッチングを行うこ
とができる – パターンマッチングに該当するか – 該当する文字列の取得
Python 入門 148
正規表現の有効性 • 文字列が整数かどうか判定するためのプロ
グラムを作成する • 正規表現の有無でコード量が大幅に違う
Python 入門 149
def isIntegerString(string): numberList = ['1','2','3','4','5','6','7','8','9','0'] if len(string) == 0: return false for char in string: if char not in numberList: return False return True
import re def isIntegerString2(string): return re.match('^[\d]*$', string) != None
正規表現なし
正規表現あり
python の正規表現作法
• re: 正規表現のパッケージ import re
• match : 合致する文字列群を抜き出す re.match(パターン文字列, 文字列)
• search: 合致する文字列の位置を返す re.search(パターン文字列、文字列)
Python 入門 150
正規表現
Python 入門 151
>>> m = re.match("^[\d]*$", "12345") >>> print(m) <_sre.SRE_Match object at 0x1005bdbf8> >>> m.group() '12345' >>> m.start(), m.end() (0, 5)
ファイル処理
Python 入門 152
ファイル処理の流れ
• ファイル処理の流れ 1. fileをオープンして fileオブジェクトを取得 2. fileオブジェクトに対して処理を行う 3. fileをクローズする
Python 入門 153
ファイルオブジェクト
• ファイルオブジェクトの作成 open(ファイルパス, オープンするモード)
– ファイルパス: 相対 or 絶対 – モード(デフォルトは"r") – r: 読み込みモード – w: 新規書き込みモード – a: 追加書き込みモード
• ファイルオブジェクトのクローズ f.close()
Python 入門 154
読み込み処理
• Read – readline(): 一行読み込み – readlines(): 全ての行を list として読み込み
Python 入門 155
>>> f = open("file.txt", "r") >>> f.readline() 'hello world1\n' >>> f.readline() 'hello world2\n' >>> f.readlines() ['hello world3\n', 'hello world4\n']
hello world1 hello world2 hello world3 hello world4
file.txt
書き込み処理 • Write – write(文字列) : 文字列を書き込み – writelines(文字列のリスト) : リストを書き込み – flush() : バッファをディスクにフラッシュ
Python 入門 156
>>> f2 = open("file2.txt", "w") >>> f2.write("hello") >>> f2.write("world") >>> f2.write("\n") >>> wlist = ["abc", "def", "ghi"] >>> f2.writelines(wlist) >>> f2.flush() >>> f2.close()
helloworld abcdefghi
file2.txt
上書きと追記
• 上書き: ファイルの内容を消してから書き込む • 追記: ファイルの末尾に追加で書き込む
Python 入門 157
PICKEL
Python 入門 158
Pickel • プログラムの実行状態はプログラムを停止すると消失 • Pickel はオブジェクトをファイルに永続的に保存する手法
• Pickel – dump: オブジェクトをファイルに書き出し – load: ファイルからオブジェクトを読み出し
Python 入門 159
Pickel
Python 入門 160
yuichi$ python >>> data = ["hello", "world", 1, 2, 3] >>> import pickle >>> f = open("dump.txt", "w") >>> pickle.dump(data, f) >>> f.close() >>> exit()
(lp0 S'hello' p1 aS'world' p2 aI1 aI2 aI3 a.
$ python >>> f = open("dump.txt") >>> import pickle >>> data = pickle.load(f) >>> print(data) ['hello', 'world', 1, 2, 3]
dump.txt
PATH
Python 入門 161
パス
• OS上のファイルやディレクトリの所在地を示す
Python 入門 162
/Users/yuichi/Documents/python.pptx
/ Users/ yuichi/ Documents/ python.pptx
etc/
var/
Shared/ Desktop/
Pictures/
java.pptx
VM/
絶対パスと相対パス • 絶対パス: ルートから辿るパス • 相対パス: 現在の自分の位置から辿るパス – ./ : 自分がいるディレクトリ – ../ : 自分の上の階層のディレクトリ
Python 入門 163
/ Users/ yuichi/ Documents/ python.pptx
etc/
var/
Shared/ Desktop/
test.txt
java.pptx
VM/
/Users/yuichi/test.txt
../test.txt
パスの取得 • 現在のカレントディレクトリを取得: os.getcwd() • ディレクトリの移動: os.chdir(移動先のパス) • 絶対パスの取得: os.path.abspath(path) • OS非依存のパスの取得方法 os.path.join(TUPLE)
Python 入門 164
yuichi$ ls | grep Documents Documents yuichi$ python >>> import os >>> os.getcwd() '/Users/yuichi' >>> os.path.abspath("Documents") '/Users/yuichi/Documents' >>> os.path.join("/","dir1","dir2","file1") '/dir1/dir2/file1'
パスに関する関数
• ファイル or ディレクトリの存在確認: os.path.exists(パス) • ディレクトリか: os.path.isdir(パス) • ファイルか: os.path.isfile(パス)
Python 入門 165
パスの操作関数
Python 入門 166
>>> os.path.exists("/Users/yuichi") True >>> os.path.isfile("/Users/yuichi") False >>> os.listdir("./") ['dir1', 'file1'] >>> os.mkdir("dir2") >>> os.rmdir("dir2") >>>
ディレクトリの操作関数
• ディレクトリの作成: os.mkdir(ディレクトリのパス) • ディレクトリの削除: os.rmdir(ディレクトリのパス) • ディレクトリの中身一覧: os.listdir(パス) • ファイル、ディレクトリの削除: os.remove(パス) • ディレクトリの再帰的削除: os.removedirs(パス)
Python 入門 167
PYTHON AS シェルスクリプト
Python 入門 168
シェルスクリプト
• プログラミング: C や Java のようなシェルを意識しないスタイル
• シェルスクリプト: シェル自体を操作するプログラミングスタイル
Python 入門 169
import os currentDir = os.getcwd() files = os.listdir(currentDir) for fname in files: print(fname)
import commands files = commands.getoutput("ls").split() for fname in files: print(fname)
通常のプログラミングスタイル シェルスクリプトのスタイル
シェルスクリプト記述のコツ
• 可能な限り既存のコマンドを使う • コマンドとコマンドの接続に python を使う
Python 入門 170
(1) 結果 = コマンドの発行 (2) 結果を処理 (3) 処理結果にもとづいて次のコマンドを作成 (4) (1)へ
コマンドの呼び出し
Python 入門 171
• os.system("コマンド") : 出力が不要な場合 • commands.getoutput("コマンド") : 出力が必要な場合
>>> commands.getoutput("ls").split("\n") ['CONFIG_FILE', 'Cisco Mac Support.webloc', 'Desktop', 'Documents', 'Downloads', 'Dropbox', 'Library', 'MATERIAL', 'Movies', 'Music', 'Pictures', 'Public', 'SR', 'STP-‐BA-‐Dispute_Japan-‐RSVT.pptx', 'VM', 'appProperces', 'bingo.py', 'bingo.pyc', 'dump.txt', 'file.txt', 'file2.txt', 'get-‐fpath-‐info.py', 'mki-‐fp-‐capture.zip', 'tcpdump', 'tcpdump.zip', 'test', 'unctled folder']
command.getoutput()
Python 入門 172
import commands result = commands.getoutput("ping -‐c 5 cisco.com") lines = result.split("\n") for line in lines: print(line)
PING cisco.com (72.163.4.161): 56 data bytes 64 bytes from 72.163.4.161: icmp_seq=0 7l=239 cme=195.865 ms 64 bytes from 72.163.4.161: icmp_seq=1 7l=239 cme=200.497 ms 64 bytes from 72.163.4.161: icmp_seq=2 7l=239 cme=197.125 ms 64 bytes from 72.163.4.161: icmp_seq=3 7l=239 cme=197.620 ms 64 bytes from 72.163.4.161: icmp_seq=4 7l=239 cme=197.553 ms -‐-‐-‐ cisco.com ping stacsccs -‐-‐-‐ 5 packets transmi7ed, 5 packets received, 0.0% packet loss round-‐trip min/avg/max/stddev = 195.865/197.732/200.497/1.519 ms
exping の実装 • 複数の宛先のping到達率を測定
Python 入門 173
import commands def ping(dest): result = commands.getoutput("ping -‐c 5 {}".format(dest)) lines = result.split("\n") length = len(lines) packetLoss = lines[length -‐2].split()[6] r7 = lines[length -‐1].split()[3].split("/")[1] return (dest, packetLoss, r7) dests = ["cisco.com", "google.com", "yahoo.com"] for dest in dests: print(ping(dest))
('cisco.com', '0.0%', '196.756') ('google.com', '0.0%', '13.317') ('yahoo.com', '0.0%', '262.060')
Linuxホスト上でのping • 特定IPアドレスのホスト間のみ通信できない • 事象発生確立は 1/16 程度
Python 入門 174
Nexus2000 FEX L2 Switch
Source Dest
Nexus5500 Primary
Nexus5500 Secondary
VPC(VSS)
ホスト50台 ホスト50台
50 x 50 -‐> 2500 flow
VLAN X VLAN Y
ping 到達率の測定プログラム
Python 入門 175
演習
Python 入門 176
演習
• 未完成のログ抽出プログラムを完成させる • バグは3箇所にある
Python 入門 177
yuichi$ python log.py -‐f log.log -‐s "2014 Jan 28 12:30:50" -‐e "2014 Jan 29 16:06:15" -‐k "port-‐channel" -‐f : ファイル名 -‐s : スタート時刻 -‐e : 終了時刻 -‐k : キーワード
dhcp-‐10-‐141-‐56-‐250:Documents yuichi$ python log.py -‐f log.log -‐s "2014 Jan 28 12:30:50" -‐e "2014 Jan 29 16:06:15" -‐k "port-‐channel" 2014 Jan 29 12:14:20 N6K-‐2 %ISIS_FABRICPATH-‐5-‐ADJCHANGE: isis_fabricpath-‐default [5150] P2P adj L1 n7kb-‐MKI-‐FP over port-‐channel50 -‐ DOWN (New) on MT-‐0 2014 Jan 29 12:14:20 N6K-‐2 %ISIS_FABRICPATH-‐5-‐ADJCHANGE: isis_fabricpath-‐default [5150] P2P adj L1 n7kb-‐MKI-‐FP over port-‐channel50 -‐ INIT on MT-‐0 2014 Jan 29 12:14:20 N6K-‐2 %ISIS_FABRICPATH-‐5-‐ADJCHANGE: isis_fabricpath-‐default [5150] P2P adj L1 n7kb-‐MKI-‐FP over port-‐channel50 -‐ UP on MT-‐0 2014 Jan 29 12:24:58 N6K-‐2 %ISIS_FABRICPATH-‐5-‐ADJCHANGE: isis_fabricpath-‐default [5150] P2P adj L1 n7kb-‐MKI-‐FP over port-‐channel50 -‐ DOWN (Delete All) on MT-‐0 2014 Jan 29 12:27:01 N6K-‐2 %ISIS_FABRICPATH-‐5-‐ADJCHANGE: isis_fabricpath-‐default [5150] P2P adj L1 n7kb-‐MKI-‐FP over port-‐channel50 -‐ DOWN (New) on MT-‐0 2014 Jan 29 12:27:01 N6K-‐2 %ISIS_FABRICPATH-‐5-‐ADJCHANGE: isis_fabricpath-‐default [5150] P2P adj L1 n7kb-‐MKI-‐FP over port-‐channel50 -‐ INIT on MT-‐0 2014 Jan 29 12:27:01 N6K-‐2 %ISIS_FABRICPATH-‐5-‐ADJCHANGE: isis_fabricpath-‐default [5150] P2P adj L1 n7kb-‐MKI-‐FP over port-‐channel50 -‐ UP on MT-‐0
コード(バグは2箇所)
Python 入門 178
import sys, os def getTimeString(line): return line[0:20] def parseTime(string): monthDict = {"Jan":1, "Feb":2, "Mar":3, "Apr":4, "May":5, "Jun":6, "Jul":7, "Aug":8, "Sep":9, "Oct":10, "Nov":11, "Dec":12} d1 = string.split(" ") d2 = d1[3].split(":") return (int(d1[0]), monthDict[d1[1]], int(d1[2]), int(d2[0]), int(d2[1]), int(d2[2])) def printFile(fname, start, end, keyword): if(not os.path.isfile(fname)): print("File \"{}\" isn't exist. exit".format(fname)) sys.exit() f = open(fname) for line in f.readlines(): line = line.rstrip() t = parseTime(getTimeString(line)) within = (start <= t) and (t <= end) contains = keyword in line if(within and contains): print(line)
fname = "" start = "1900 Jan 1 00:00:00" end = "2100 Jan 1 00:00:00" keyword = "" for index in range(len(sys.argv)): arg = sys.argv[index] if(arg == "-‐f"): fname = sys.argv[index + 1] elif(arg == "-‐s"): start = sys.argv[index + 1] elif(arg == "-‐k"): keyword = sys.argv[index + 1] if(fname == ""): s1 = "syntax: python log.py -‐f FILENAME -‐s " s2 = "START_TIME -‐e END_TIME -‐k KEYWORD" print(s1 + s2) print("START_TIME, END_TIME, KEYWORD are opcon.") else: printFile(fname, start, end, keyword)
ログの一行から時間の文字列を抽出
抽出した文字列を比較可能なタプルに変換
条件にマッチした行をprint出力
コマンド引数を読み取る
コマンド引数の初期値
ファイル名が与えられていたらプログラムを実行
演習
Python 入門 179
発展編
キーワードを正規表現で指定できるようにする 正規表現にマッチした場合のみ表示を行う オプション -‐r に続けて正規表現を指定
演習 • ラボのLinux(10.71.224.172)にログイン(guest, c1sco123)し、以下のスクリプトを作成し実行する
• ファイル名は <cecid>.py とする
Python 入門 180
以下の機器の開いているポート一覧を取得する -‐ rws1 (1.0.0.1) -‐ gateway (1.0.0.100) -‐ linux( 1.110.120.173) -‐ esxi ( 10.71.224.170) -‐ Nexus7000 (1.110.8.1)
利用するコマンド
Python 入門 181
yuichi$ ssh -‐l guest 10.71.224.172 pass: c1sco123 [guest@fedora172 ~]$ nmap 127.0.0.1 Starcng Nmap 6.01 ( h7p://nmap.org ) at 2014-‐02-‐08 13:05 EST Nmap scan report for fedora172.localdomain (127.0.0.1) Host is up (0.0016s latency). Not shown: 995 closed ports PORT STATE SERVICE 22/tcp open ssh 23/tcp open telnet 25/tcp open smtp 111/tcp open rpcbind 631/tcp open ipp
表示形式
Python 入門 182
アドレス1 TCP: xxx, yyy, zzz, ... UDP: xxx, yyy, zzz, ... アドレス2 TCP: xxx, yyy, zzz, ... UDP: xxx, yyy, zzz, ... ... アドレス3 TCP: xxx, yyy, zzz, ... UDP: xxx, yyy, zzz, ...
次回予告
• オブジェクト指向 • クラス • アルゴリズム • pexpect によるCisco機器のプログラム操作
Python 入門 183
Python 入門 184
Python 入門 Learning to Program
For Cisco Network Engineers Day 3/4
Japan TAC DC Team.
Yuichi Ito
Agenda Day 3
• クラスの概念 • クラスの作成 • 演習 • ドラクエの実装で学ぶオブジェクト指向 • 演習1-‐3 • pexpect による Cisco 機器の操作法 • 演習
186 Python 入門
プログラミング言語の流派
Python 入門 187
プログラミングの流派
• 手続き型 • 関数型 • オブジェクト指向型
Python 入門 188
オブジェクト指向の概念
Python 入門 189
なぜオブジェクト指向が必要か
あえてコード記述作法にルールを課すことでプログラムのカオス化を防ぐ -‐ 実装の境界が明確になる
Python 入門 190
ルールにしたがって プログラムを整理
なぜオブジェクト指向が必要か
オブジェクト指向の機能で開発効率を向上させる -‐ 継承による機能の引き継ぎ -‐ ポリモーフィズム
Python 入門 191
Bu7on
Hello World
ボタンを押したら hello world と 表示するプログラム 10数行で作れる(継承のおかげ)
人間の思考方法 • 「誰が何をするか」が重要 • 誰にどういう「メッセージ」を送ったら、どういう「結
果」が得られるか
Python 入門 192
[内部処理] 仕事をする
[メッセージ] この仕事をしてください
[結果] 成果物はこれです。 上司 部下
関数 vs メソッド
Python 入門 193
関数チックな考え方
オブジェクト指向の考え方
「仕事をさせる」(部下A、仕事)
「部下A」に「仕事をさせる」(仕事)
関数 vs メソッド • 関数: オブジェクトを「引数として」使う • メソッド: オブジェクト「から」処理を呼び出す
Python 入門 194
>>> string = "hello world python" # 関数 >>> length = len(string) # メソッド >>> words = string.split()
文字列の処理
関数 vs メソッド
• 関数: データと処理の結びつきが弱い • メソッド: データと処理の結びつきが強い
Python 入門 195
len( ) string.split() 処理にデータを与える データに処理をさせる
関数 メソッド
len は何にでも使える split は string にしか使えない
オブジェクト指向
• 誰(インスタンス)にメッセージを送る(メソッドを呼び出す)とどういう結果(返り値)が得られるか
• 細かい内部の仕組みは気にしない
Python 入門 196
返り値 = 誰.メソッド(パラメータ) 記述ルール
• 内部の細かい挙動は考えず状態のみを意識する
データをHDDに書き込みたい (1) Block N に書き込み
書き込み処理 (1)Block N のDisk を選択 (2)Block N のセクタを指定 (3)書き込みを行なう
ストレージオブジェクト (専門家がプログラムを作成) ユーザアプリケーションの
オブジェクト (ストレージを使うだけ)
処理、情報の隠蔽
※ 現在のヘッド位置などは気にしない
まとめ
• オブジェクト指向のコンセプト – 「誰が何をするか」 – 「誰が誰とどう協力するか」 – 「複雑な処理、データは外部に見せない」
• コードの秩序化 – 権限を持たないものは操作ができない
Python 入門 198
クラスの概念
Python 入門 199
クラス
• オブジェクトの種類 • クラスは属性としてデータと処理を持つ
Python 入門 200
属性 [データ] -‐ ガソリンの量 [処理] -‐ 走る -‐ 止まる -‐ 曲がる
インスタンス • クラス自体で処理するのではなく、クラスから作るイ
ンスタンスで処理をする • 部下という概念(クラス)ではなく部下の◯◯(インスタ
ンス)が仕事をする
Python 入門 201
[内部処理] 仕事をする
[メッセージ] この仕事をしてください
[結果] 成果物はこれです。 クラス: 上司
インスタンス: BOB クラス: 部下
インスタンス: TOM
クラスとインスタンスの関係 • クラス: インスタンスを作るための雛形 • インスタンス: クラスから作られるオブジェクト • 型(クラス) とその値(インスタンス)という理解でOK
Python 入門 202
クラス インスタンス インスタンス化
型(Class) String 値(Instance) "Hello Python", "Hello Cisco"
コンポジション
• クラスは別のクラス(インスタンス)を持つことがある
Python 入門 203
クラス車は -‐ エンジンクラスを1インスタンス -‐ タイヤクラスを4インスタンス -‐ ハンドルクラスを1インスタンス 持っている
クラスの作成
Python 入門 204
クラスの宣言 • 以下の要件を満たすクラスを作る
Python 入門 205
string: 保持する文字列
set_string: stringをセット
get_string: stringを取得
double: stringを繰り返す
クラス: MyClass
属性: インスタンス変数
属性: メソッド
データとして文字列を内部に持つ その文字列を設定するメソッド その文字列を取得するメソッド 文字列を「文字列+文字列」にするメソッド
クラスの宣言 • クラスの宣言は class クラス名: • メソッドの宣言: 第一引数が self として定義 • 属性へのアクセスは self.属性 とする
Python 入門 206
class MyClass: string = "" def set_string(self, string): self.string = string def get_string(self): return self.string def double(self): self.string *= 2
class の宣言
メソッドの宣言 第一引数を self とするのは python のルール
class が保持するデータの宣言
属性へのアクセス self.属性 とする (そうしないと関数だけのローカル変数となる)
インスタンスの利用
• クラス名() でインスタンス化 • メソッドを呼び出す際は第一引数(self)は指定しない
Python 入門 207
class MyClass: string = "" def set_string(self, string): self.string = string def get_string(self): return self.string def double(self): self.string *= 2
# インスタンス化 >>> mc = MyClass() # メソッド呼び出し >>> mc.set_string("hello ") >>> print(mc.get_string()) hello >>> mc.double() >>> print(mc.get_string()) hello hello
演習
• class Counter を作成 • 内部にカウンタを持つ。初期値は1 • get_value メソッドで現在のカウンタ値を返す • count_up メソッドで現在のカウンタ値を +1 する • clear_counter メソッドでカウンタを1に戻す
Python 入門 208
コンストラクタの利用 • コンストラクタ: インスタンス作成時に呼び出されるメソッド • コンストラクタの引数はインスタンス作成時に指定
Python 入門 209
class MyClass: string = "" def __init__(self, string): print("__init__ is called") self.string = string def set_string(self, string): self.string = string def get_string(self): return self.string def double(self): self.string *= 2
# インスタンス化 >>> mc = MyClass("python ") __init__ is called # メソッド呼び出し >>> print(mc.get_string()) python >>>
演習
• class Counter を改良 • 内部にカウンタを持つ。初期値はコンストラクタで指定 • clear_counter メソッドでカウンタを初期値に戻す
Python 入門 210
補足)動的なメンバ変数
• 動的にメンバ要素を追加できる • Javaとは異なるので要注意!!
Python 入門 211
class MyClass: def func(self): print("func")
>>> mc = MyClass() >>> mc.a = "Hello World" >>> mc.a 'Hello World'
定義されていない メンバ変数 a に代入
メンバ変数 a は 定義されていない
クラスサンプル
Python 入門 212
# bingo.py import random class BingoMachine: ballList = [] def __init__(self, min, max): self.ballList = range(min, max + 1) def getBall(self): if(len(self.ballList) == 0): return -‐1 index = int(random.uniform(0, len(self.ballList))) return self.ballList.pop(index)
# 実行結果 >>> from bingo import * >>> b = BingoMachine(1,5) >>> b.getBall() 1 >>> b.getBall() 5 >>> b.getBall() 2 >>> b.getBall() 3 >>> b.getBall() 4 >>> b.getBall() -‐1
ビンゴマシーン 数字 n – m の間の数字をランダムに返す 一度返した値は返さない
クラスまとめ1
• メソッドの第一引数は自分自身のオブジェクト • メンバ変数やメソッドを呼ぶにはこのオブジェクトの指定が必要
Python 入門 213
class MyClass: var = 0 def method1(self): self.var = 1 print("method1") def method2(self): print(self.var) self.method1() print("method2")
メソッド定義の第一引数が self になっている
メソッド呼び出しは定義の第一引数は飛ばす
メンバ変数やメソッドを self.名前 として 呼び出している
クラスまとめ2
Python 入門 214
class MyClass2: var = 0 def __init__(self, var): self.var = var def method(self, var): print(str(self.var) + " " + str(var)) print("method") m2 = MyClass2(5) m2.method(8) -‐-‐> 5 8 method
• インスタンス化: クラス名(コンストラクタに渡す引数) • コンストラクタ: __init__ というメソッド名
ドラクエで理解するオブジェクト指向
Python 入門 215
演習の目的
Python 入門 216
• 大きなプログラムの作成方法を学ぶ – 複数のクラスの連携 – どのようにしてコードを広げていくか – 題材はドラクエ1
ドラゴンクエスト
Python 入門 217
主人公
全てを作るのは無理なので Map のみ実装する
町人
前後左右に動く ただし、マップの外には出ない 町人に話しかける
主人公に応答する
AA版ドラゴンクエスト(完成図)
Python 入門 218
a:Left, s:Right, w:Up, x:Down d:Talk, Ctrl-‐C:Exit +-‐-‐-‐-‐-‐-‐-‐-‐+ | | | | | B< | | | | M| | | | | | P | +-‐-‐-‐-‐-‐-‐-‐-‐+ ####################### Hello I'm Bukiya #######################
主人公
メッセージ欄
前後左右に動く ただし、マップの外には出ない 町人に話しかける 向いている向きでアイコンが変わる
主人公に応答する 町人
マップ
作成プログラムの要素
Python 入門 219
町人(複数) 主人公 (イベント処理)
マップ(全体の管理)
入力
キー入力管理クラス (実装済み)
クラス町人
Python 入門 220
クラス: 町人 メンバ変数 x座標 y座標 外見の種類 セリフ メソッド: respond: 主人公に応答する
外見 B: 武器屋 M: 商人 P: 神父
クラス町人のインスタンス
Python 入門 221
インスタンス 町人A メンバ変数 x: 8 y: 4 外見: 男A セリフ: "こんにちは" メソッド: respond: 主人公に応答する
インスタンス 町人B メンバ変数 x: 4 y: 2 外見: 魔法使い セリフ: "いい天気じゃのう" メソッド: respond: 主人公に応答する
主人公
Python 入門 222
主人公のアイコン < : 左向き > : 右向き ^ : 上向き V : 下向き
クラス: 主人公 メンバ変数 x座標 y座標 向いている方向 セリフ メソッド: run: キー入力を待つ(標準入力の無限ループ) move: キー入力で上下左右に動く talk: 向いている方向の町人と話す
マップ
Python 入門 223
クラス: Map メンバ変数 -‐ 主人公のインスタンス -‐ 町人たちのインスタンス -‐ マップ情報 メソッド: -‐ render: マップを書き出す -‐ 主人公と町人を管理するメソッド群
+-‐-‐-‐-‐-‐-‐-‐-‐+ | | | | | B< | | | | P | +-‐-‐-‐-‐-‐-‐-‐-‐+
外枠 主人公 町人(複数)
クラスとクラスの相互関係
• 十字キーを操作すると勇者が動く • 地図はその情報を反映しなければならない
Python 入門 224
インスタンス 勇者
インスタンス Map
入力(keyInput)
自分のX,Y情報を アップデート
描画を依頼 画面出力
マップ描画の全体の流れ
Python 入門 225
Class: Field 変数: hero 変数: peopleMap 変数: 地形情報 renderMap()
描画(render) 地形情報 + 勇者 + 町人達
Class: Hero move() talk() getXY() getImage() keyInputEvent()
Class: People respond() getImage()
入力(keyInput)
x,y座標や見た目 などの情報を取得
ディスプレイに出力
① ②
③ ④ +-‐-‐-‐-‐-‐-‐-‐-‐+ | | | | | B< | | | | P | +-‐-‐-‐-‐-‐-‐-‐-‐+
演習1
Python 入門 226
マップ
• 何もないマップを描画 • 縦と横の長さを指定可能 • 行(row)指向で描画する
227
+-‐-‐-‐-‐-‐-‐-‐-‐+ | | | | | | +-‐-‐-‐-‐-‐-‐-‐-‐+
4隅は 「+」 y座標0, 最大値は 「–」 x座標0, 最大値は 「|」 それ以外は空白
class DQMap: max_x = 0 max_y = 0 def __init__(self, max_xy): (self.max_x, self.max_y) = max_xy def render_map(self): map_string_list = [] for y in range(self.max_y): for x in range(self.max_x): if(y == 0 or y == self.max_y -‐ 1): if(x == 0 or x == self.max_x -‐ 1): map_string_list.append("+") else: map_string_list.append("-‐") else: if(x == 0 or x == self.max_x -‐ 1): map_string_list.append("|") else: map_string_list.append(" ") map_string_list.append("\n") map_string = "".join(map_string_list) print(map_string.rstrip())
主人公 • キー入力で主人公のx,y座標と「向き」をアップデートする • Ctrl-‐C でプログラムを終了する
228
import getch, sys class Hero: x = 0 y = 0 direccon = "N" def __init__(self, x, y): self.x = x self.y = y self.direccon = "N" def run(self): gc = getch.Getch() while(True): keycode = ord(gc()) self.move(keycode) print("x:{}, y:{}, dir:{}".format(self.x, self.y, self.direccon)) if(keycode == 3): sys.exit()
def move(self, keycode): if(keycode == 97): #Le� self.x -‐= 1 self.direccon = "W" elif(keycode == 115): #Right self.x += 1 self.direccon = "E" elif(keycode == 119): #Up self.y -‐= 1 self.direccon = "N" elif(keycode == 122): #Down self.y += 1 self.direccon = "S" hero = Hero(10,10) hero.run()
マップと主人公(課題) • マップの中に主人公を描画 • 向いている方向で主人公のアイコンを変える • マップの範囲外に主人公がアクセスしない • プログラムの起点は main.py
229
実装のヒント DQMap: -‐ DQMap が Hero のインスタンスを持つ -‐ 主人公と同じ x, y 座標の場合は主人公のアイコンを表示 (主人公のインスタンスからアイコンを取得) Hero: -‐ 新しい座標が Map 範囲内である場合だけ、x,y をアップデート (範囲外へのアクセスは x,y をアップデートしない) -‐ 表示する主人公のアイコンは主人公の向きで決まる (向きの応じたアイコンを返すメソッドを実装)
演習2
Python 入門 230
町人の実装
Python 入門 231
class People: icon = "" x = 0 y = 0 comment = "" def __init__(self, icon, x, y, comment): self.icon = icon self.x = x self.y = y self.comment = comment def get_icon(self): return self.icon def get_xy(self): return (self.x, self.y) def get_response(self): return self.comment
町人は -‐ アイコン(一文字) -‐ x,y -‐ セリフ をコンストラクタで設定 「ゲッターメソッド」を実装 ※ パラメータを取得するためのメソッド
マップと町人の実装
Python 入門 232
class DQMap: ... people_map = {} def __init__(self, max_xy, hero_xy, people_paramList): .... for params in people_paramList: (icon, x, y, comment) = params self.people_map[(x,y)] = people.People(icon, x, y, comment)
def render_map(self, comment): map_string_list = [] (hx, hy) = self.hero.get_xy() for y in range(self.max_y): for x in range(self.max_x): if((x,y) == (hx, hy)): map_string_list.append(self.hero.get_icon()) elif(self.people_map.has_key((x, y))): p = self.people_map[(x, y)] map_string_list.append(p.get_icon())
コンストラクタで町人の 情報をリスト形式で指定
町人のインスタンスを作成 (x,y)をキーにし辞書で町人を管理
描画の際に、x, y に町人がいるか確認 もしいれば町人を描画する
主人公と町人の実装(課題)
• 主人公が町人と被らないようにする
Python 入門 233
Hint 次に動こうとする先に町人がいるか確認 いる場合は主人公の x, y をアップデートしない
演習3
Python 入門 234
会話機能の実装
• 主人公が向いている方向に町人がいるか確認 • 町人がいれば町人のレスポンスを取得し表示 • いなければ誰もいない旨のメッセージを表示 • DQMapのメソッドrender_mapにコメントを渡して描画(実装済)
Python 入門 235
+-‐-‐-‐-‐-‐-‐-‐-‐+ | | | | | B | | | | >M| | | | | | P | +-‐-‐-‐-‐-‐-‐-‐-‐+ ####################### Hello I'm Merchant #######################
+-‐-‐-‐-‐-‐-‐-‐-‐+ | | | | | B | | ^ | | M| | | | | | P | +-‐-‐-‐-‐-‐-‐-‐-‐+ ####################### No one exists on that way #######################
その他
Python 入門 236
__name__
• __name__ • __name__ が "__main__" のときは起点
Python 入門 237
import uclity def func(): print("func on main.py") print("global on main.py") func() uclity.func() if __name__ == "__main__": print("__name__ is __main__ on main.py")
def func(): print("func() on uclity.py") print("global on uclity.py") func() if __name__ == "__main__": print("__name__ is __main__ on uclity.py")
main.py
uclity.py
__name__
Python 入門 238
def func(): print("func() on uclity.py") print("global on uclity.py") func() if __name__ == "__main__": print("__name__ is __main__ on uclity.py")
# python uclity.py global on uclity.py func() on uclity.py __name__ is __main__ on uclity.py
True
実行結果
__name__
Python 入門 239
import uclity def func(): print("func on main.py") print("global on main.py") func() uclity.func() if __name__ == "__main__": print("__name__ is __main__ on main.py")
def func(): print("func() on uclity.py") print("global on uclity.py") func() if __name__ == "__main__": print("__name__ is __main__ on uclity.py")
main.py
uclity.py True
False
#python main.py global on uclity.py func() on uclity.py global on main.py func on main.py func() on uclity.py __name__ is __main__ on main.py
実行結果
コピーと参照
• コピー: 異なるオブジェクト • 参照: 同一オブジェクトを複数箇所で利用 • 参照に対しては副作用の注意が必要
Python 入門 240
>>> a = [1,2,3,4] >>> b = a >>> b.append(5) >>> a [1, 2, 3, 4, 5]
PEXPECT
Python 入門 241
Pexpect
• EXPECT を Python で使うためのパッケージ • Ciscoの機器やLinuxといったCLI操作を必要とする機器を
プログラミングで規定通りに動かすために利用
Python 入門 242
Python Script
Expect
• LinuxなどのインタラクティブなCLIを制御するためのプログラム
• pexpect の元になったプログラム • Teraterm Macro に似ている
Python 入門 243
The Expect Home Page
h7p://expect.sourceforge.net/
Expect Script
Expect の動作
• spawn: セッションを開始 • expect: 期待する標準入力(from リモート)を待つ • send: リモートに出力を送る
Python 入門 244
dhcp-‐10-‐141-‐56-‐250:~ yuichi$ telnet -‐l yuiito rws1 Trying 10.71.231.245... Connected to rws1.cisco.com. Escape character is '^]'. Password: cisco123 Last login: Thu Jan 23 14:53:37 from dhcp-‐10-‐141-‐56-‐250.cisco.com unknown terminal "xterm-‐256color" unknown terminal "xterm-‐256color" [yuiito@rws1 yuiito]$ echo hello > test [yuiito@rws1 yuiito]$ ls snmpget.sh test
青がリモートからの標準入力 赤がリモートへの標準出力
expect サンプル
• Linux へのログインと操作
Python 入門 245
Password:
cisco123
yuiito$
echo hello > test
#! /usr/bin/expect spawn telnet -‐l yuiito rws1 expect "Password:" send "cisco123\n" expect "yuiito\]\\\$" send "touch hello\n" interact
yuichi$ expect login-‐to-‐rws1.exp
login-‐to-‐rws1.exp
呼び出し
Pexpect
• pip を使ってインストール • コード pexpect.py 同一フォルダに配置(利用) • import pexpect
Python 入門 246
Pexpect の作法
• spawn session = pexpect.spawn("telnet等のコマンド")
• expect session.expect("入力されるのを待つ文字列")
• sendline session.sendline("送信する文字列。自動で改行コードがつく")
Python 入門 247
pexpect
• マッチした文字列当を抽出
Python 入門 248
session.a�er: マッチした文字列 session.before: マッチした文字列までの文字列 session.buffer:
pexpect のサンプル
• rws1 にログインしてファイル pexpect を作成
Python 入門 249
import pexpect child = pexpect.spawn("telnet -‐l yuiito rws1") child.expect("Password:") child.sendline("cisco123") child.expect("yuiito\]\$") child.sendline("touch pexpect")
pexpextのサンプル • Nexusの設定をファイル名を日時にしてFTPサーバに保存
Python 入門 250
import pexpect, datecme def getLines(string): lines = lines = [x.strip() for x in string.split("\n")] return filter(lambda x: x!= '', lines) def getHostName(exp): exp.sendline("show hostname") exp.expect("#") return getLines(exp.before)[1] def getToDay(): d = datecme.datecme.today() return "{}_{}_{}-‐{}_{}_{}".format(d.year, d.month, d.day, d.hour, d.minute, d.second) def makeFileName(exp): hostName = getHostName(exp) today = getToDay() return "{}-‐{}.conf".format(hostName, today)
def login(exp, password): exp.expect("Password:") exp.sendline(password) exp.expect("#") exp.sendline("terminal length 0") exp.expect("#") def saveFile(exp, saveFrom, saveTo, vrf, username, password): command = "copy {} {}".format(saveFrom, saveTo) exp.sendline(command) exp.expect("Enter vrf") exp.sendline(vrf) exp.expect("Enter username:") exp.sendline(username) exp.expect("Password:") exp.sendline(password) exp.expect("#") child = pexpect.spawn("telnet -‐l admin 10.71.156.229") login(child, "cisco") saveTo = "�p://10.71.224.115/yuiito/" + makeFileName(child) saveFile(child, "running-‐config", saveTo, "management", "anonymous", "") child.close() ファイル名やコマンドの生成が python 任せ
pexpect
• showコマンドや設定変更はほぼ同じような手順で実装される
• 継承やクロージャを使うことで各処理を実装すれば簡単に機能を増やせる
Python 入門 251
演習
• Nexusの現在のバージョンを取得するプログラムを作成する
Python 入門 252
Nexus ip: 1.110.8.1 user-‐name: admin pass: cisco
踏み台 ip: 10.71.224.172 user-‐name: guest pass: c1sco123
Python 入門 253
Python 入門 Learning to Program
For Cisco Network Engineers Day 4/4
Japan TAC DC Team.
Yuichi Ito
Agenda Day4 • Pythonのプログラム配布手法 • Tkinter (GUI) • Tkinter で学ぶ継承 • 例外処理 • NETWORKプログラミング • Pexpect
Python 入門 255
プログラム配布手法
Python 入門 256
ソースコードの配布 • 長所: 配布が簡単 • 短所: ライブラリに依存。コードが丸見え
Python 入門 257
python ソースコード
python ソースコード
python ソースコード
開発者 利用者 利用者
バイナリ化して実行環境ごと配布 • 長所: 利用者が使いやすい。 • 短所: 作成が難しい
Python 入門 258
python ソースコード
開発者 利用者(Windows) 利用者(Mac)
.exe .app
関数(メソッド)の引数
Python 入門 259
関数とパラメータ • 関数(メソッド)にはデフォルト値を使える • 呼び出し側で引数を好きな順序で指定可能
Python 入門 260
def func(a, b="python", c="cisco"): print("hello {} {} {}".format(a,b,c)) func("world", "PYTHON", "CISCO") # => "hello world PYTHON CISCO" func("world") # => "hello world python cisco" func(b="world", a="python") # => "hello python world cisco"
引数 b, c にはデフォルト値を指定
引数 a,b,c に全て任意の値を設定
引数 a のみ指定して呼び出し
任意の順序で引数を指定
GUI TKINTER
Python 入門 261
GUI の学習目的
• アプリケーションにGUI一般的 • 見た目に分かりやすいので中級者向き • 継承の概念などを理解するのに最適な題材
Python 入門 262
Tkinter
• Python の標準的なGUIライブラリ • インストールなしに利用可能 • 他のGUIライブラリ(Qt, wxpython)と概念が似ている
Python 入門 263
Hello Tkinter
Python 入門 264
import Tkinter as tk font=("Helevecca", 32, "bold") label = tk.Label(text="Hello Cisco", font=font, bg="red") label.pack() label.mainloop()
Hello Tkinter
• GUI 作成の流れ – Widgetと呼ばれるパーツを初期化 – パーツを配置 – GUIアプリケーションを起動
Python 入門 265
Widget はGUIの部品。 ボタンやテキストボックスなどの全てのパーツはWidget
Hello Tkinter の解説
Python 入門 266
import Tkinter as tk font=("Helevecca", 32, "bold") label = tk.Label(text="Hello Cisco", font=font, bg="red") label.pack() label.mainloop()
Tkinter というモジュールをインポート。呼び出し時は tk という別称を使う Widget の Label を作成する。引数として文字列とフォント、背景色を指定 pack() メソッドで配置 mainloop() メソッドを呼び出すことでアプリケーションを実行
演習1 • ラベルに表示する文字列を Hello Python に変更 • フォントサイズを 44 にする • 背景色を blue にする
発展 • アンダーラインを付ける • 幅を50にする
Python 入門 267
An IntroducYon to Tkinter (Work in Progress) h7p://e�ot.org/tkinterbook/
Frame • Widget を配置するための Widget • Frame in Frame も可能(複雑な構成を作れる)
Python 入門 268
Frame1
Frame2
Frame3
widget 1
widget 2
widget 3
widget 4
widget 5
widget 6
Frame1 | |-‐-‐ Frame 2 | | -‐-‐ Widget1 | | -‐-‐ Widget2 | | -‐-‐ Widget3 | | -‐-‐ Widget4 | | -‐-‐ Frame3 | -‐-‐ Widget5 | -‐-‐ Widget5
Hello Frame
Python 入門 269
import Tkinter as tk frame = tk.Frame() font=("Helevecca", 32, "bold") label1 = tk.Label(frame, text="Hello Cisco", font=font, bg="red") label2 = tk.Label(frame, text="Hello Python", font=font, bg="blue") label1.pack() label2.pack() frame.pack() frame.mainloop()
Hello Frame の解説 • Frame のインスタンスを作成 • Frame の子要素(中にある)は第一引数を Frame の
インスタンスにする • トップレベルの widget で mainloop() を呼び出す
Python 入門 270
import Tkinter as tk frame = tk.Frame() font=("Helevecca", 32, "bold") label1 = tk.Label(frame, text="Hello Cisco", font=font, bg="red") label2 = tk.Label(frame, text="Hello Python", font=font, bg="blue") label1.pack() label2.pack() frame.pack() frame.mainloop()
演習 • 3つのラベルが表示されるように改造する • 3番目の背景色は green
Python 入門 271
• Hello Cisco, Hello World の横の空白を埋める • Hint: widgetの pack に fill=tk.X を指定
発展編
Frame の縦横 • Frameが並ぶのはデフォルト縦方向 • 子要素の pack() メソッドの引数に方向を指定 • 横方向は side=LEFT を指定
272
import Tkinter as tk frame = tk.Frame() font=("Helevecca", 32, "bold") label1 = tk.Label(frame, text="Hello Cisco", font=font, bg="red") label2 = tk.Label(frame, text="Hello Python", font=font, bg="blue") label1.pack(side=tk.LEFT) label2.pack(side=tk.LEFT) frame.pack() frame.mainloop()
演習
• Frame に Frame を入れて以下のウィンドウを作成する
Python 入門 273
演習の解答例
Python 入門 274
import Tkinter as tk frame1 = tk.Frame() frame2 = tk.Frame() font=("Helevecca", 32, "bold") label1 = tk.Label(frame2, text="Hello Cisco", font=font, bg="red") label2 = tk.Label(frame2, text="Hello Python", font=font, bg="blue") label1.pack(side=tk.LEFT) label2.pack(side=tk.LEFT) label3 = tk.Label(frame1, text="Hello Wodld", font=font, bg="green") label3.pack() frame2.pack() label3.pack() frame1.pack() frame1.mainloop()
継承の概念
Python 入門 275
継承
Python 入門 276
• クラスが別のクラスの特性を引き継ぐための機能 • 元のクラスをスーパークラス(親クラス)と呼ぶ • 派生されたクラスをサブクラス(子クラス)と呼ぶ
親クラス(車)
子クラス(ジープ)
子クラス(スーパーカー)
TKinter の継承 • Label や Bu7on は継承で作成されている • 継承元で根本の描画処理などを実装 • Label や Bu7on は差分のみ実装
Python 入門 277
Widget
Bu7on
Hello World 描画の仕組みの実装
ボタン特有の機能の実装
ラベル特有の機能の実装
継承のメリット
• 子クラスの実装を単純化できる • クラス自体もそれを使う側もコードの保守性が増す
Python 入門 278
Label も Bu7on も Frame の 子要素となることができる
Label と Bu7on のスーパークラスが Frame の子要素となることができる
Tkinter の自作クラス
• 継承してクラスを作ると、Frameにいれることができるパーツが作れる
Python 入門 279
Frame Frame
継承していない場合 Frame にいれられない
継承している場合 Frame にいれられる
継承されたクラスの特徴
• 親クラスのメンバ変数にアクセス可能 • 親クラスのメソッドにアクセス可能 • 子クラスのコンストラクタ内で親クラスのコン
ストラクタを呼び出す
Python 入門 280
親クラス 子クラス 継承のイメージ
継承クラスの作成
Python 入門 281
クラス継承の作法 クラスの宣言 class 子クラス(親クラス): 子クラスのコンストラクタで親クラスのコンストラクタを呼び出す 親クラスのコンストラクタの引数は全て指定。 子クラスは親クラスの変数とメソッドにアクセス可能 (上書きも可能)
Python 入門 282
class Parent: text = "" def __init__(self, text): print("Parent.__init__()") self.text = text def get_text(self): return self.text class Child(Parent): def __init__(self, text): print("Child.__init__()") Parent.__init__(self, text) def double(self): self.text *= 2
演習 • クラス Counter を継承して Counter2 を作る • Counter2 に以下のメソッドを実装する -‐ double: 現在のカウンターの値を2倍する -‐ clear_counter: カウンターを初期化する
Python 入門 283
class Counter: value = -‐1 def __init__(self, value): self.value = value def get_value(self): return self.value def count_up(self): self.value += 1
Tkinter の Bu7on
• ボタンを押された際に特定の関数(メソッド)を呼び出す • Bu7on(command=「呼び出す関数(メソッド)名」)
Python 入門 284
import Tkinter as tk def clicked(): print("clicked") bu7on = tk.Bu7on(text="Click", command=clicked) bu7on.pack() bu7on.mainloop()
>>> clicked
click する
表示される
継承を使わないカウンターの実装
Python 入門 285
import Tkinter as tk class Counter: label = None value = -‐1 def clicked(self): # Click時に呼ばれる self.value += 1 self.label.configure(text=self.getText()) def getText(self): # 表示する文字を作成 return "Count:{}".format(self.value)
def __init__(self, value): self.value = value frame = tk.Frame() font = ("Helevecca", 32, "bold") self.label = tk.Label(frame, text=self.getText(), font=font, bg="red") bu7on = tk.Bu7on(frame, text="Click", command=self.clicked) self.label.pack() bu7on.pack() frame.pack() frame.mainloop() c = Counter(0)
継承を使うカウンタの実装
Python 入門 286
import Tkinter as tk class Counter(tk.Frame): label = None value = -‐1 def clicked(self): self.value += 1 self.label.configure(text=self.getText()) def getText(self): return "Count:{}".format(self.value)
def __init__(self, master=None, value=0): tk.Frame.__init__(self, master) self.value = value font = ("Helevecca", 32, "bold") self.label = tk.Label(self, text=self.getText(), font=font, bg="red") bu7on = tk.Bu7on(self, text="Click", command=self.clicked) self.label.pack() bu7on.pack() c = Counter(value=0) c.pack() c.mainloop()
• クラス Counter 自身も Widget
Python 入門 287
継承を使うカウンタの実装
frame = tk.Frame() c1 = Counter(master=frame, value=0) c2 = Counter(master=frame, value=5) c1.pack(side=tk.LEFT) c2.pack(side=tk.LEFT) frame.pack() frame.mainloop()
演習
• 継承を使ったクラス Counter に Clear ボタンを追加する
• Clear ボタンをクリックするとカウンタを初期値に戻す
Python 入門 288
例外処理
Python 入門 289
例外 • プログラムの継続実行を妨げる意図しない挙動
Python 入門 290
1: 2 Traceback (most recent call last): File "/Users/yuichi/Documents/python-‐training/excepcon.py", line 3, in <module> b = 5 / 0 ZeroDivisionError: integer division or modulo by zero
a = 1 + 1 print("1: " + str(a)) b = 5 / 0 print("2: " + str(b)) c = a + 1 print("3: " + str(c))
0 での割り算。ここで例外が発生し、処理を中断!!
0 での割り算は数学的に許容されない Python が例外を発生させる
例外 • プログラム自身に起因する例外 – 0での割り算 – 配列長外にアクセス
• プログラムの外の環境に起因する例外 – ネットワークに接続できない – ユーザからの入力が不正な値
Python 入門 291
例外の対処方法
• 例外の「キャッチ(補足)」
Python 入門 292
a = 1 + 1 print("1: " + str(a)) b = 5 / 0 print("2: " + str(b)) c = a + 1 print("3: " + str(c))
例外を補足した場合の処理
例外が発生
例外を補足
try/except
• 例外の補足には try, except を利用 • 補足する Except は例外の種類に対応してい
る必要がある
Python 入門 293
try: 例外が発生する可能性のある処理 except エラークラス: 例外処理 ※ エラークラスは先の「例外の種類」のクラス
try/except
Python 入門 294
try: a = 1 + 1 print("1: " + str(a)) b = 5 / 0 print("2: " + str(b)) c = a + 1 print("3: " + str(c)) except Excepcon: print("Error happens") print("Done")
1: 2 Error happens Done
NETWORK(おまけ)
Python 入門 295
通信の手順
Python 入門 296
ソケットをオープン 通信を待つ ソケットをオープン
通信を開始 Hello
相互にやりとり
ソケットをクローズ
クライアント サーバ
ソケットを使ったサーバ • ソケットをオープンしてデータを待つ • ソケットには受信IPとポートを指定する
Python 入門 297
import socket IP = socket.gethostbyname(socket.gethostname()) PORT = 12345 BUFF_SIZE = 2048 ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.bind((IP, PORT)) ssock.listen(10)
while(True): (csock, ip) = ssock.accept() while(True): data = csock.recv(BUFF_SIZE) if(not data): break print(data)
ソケットを使ったクライアント
• ソケットをオープンしてデータを送る • ソケットには宛先IPとPORTを指定
Python 入門 298
import socket IP = socket.gethostbyname(socket.gethostname()) PORT = 12345 BUFF_SIZE = 2048 csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) csock.connect((IP, PORT)) csock.send("Hello World") csock.close()
サンプルプログラム
• チャット用アプリケーション
Python 入門 299
自分のIP
宛先IPとメッセージ
送信元IPとメッセージ
演習
• チャット用のアプリケーションには送信先に問題があるとクラッシュするという問題がある。これを解決する。
• ヒント: クラッシュは例外に対処できていないため発生している
Python 入門 300
Python 入門 301
Next Plan
• Network Programming • GUI Programming(tkinter, wxPython, Qt) • Big Data Broker • Python on Nexus • XML, json, Rest API • Advanced Object Oriented Programming • Funcconal Programming • Algorithm
Python 入門 302
ボツ スライド
Python 入門 303
メソッドの属性
• メソッドには複数種類ある • 通常のメソッド: 第3回で扱ったもの • クラスメソッド • スタティックメソッド
Python 入門 304
クラスメソッド
Python 入門 305
スタティックメソッド
Python 入門 306
クラスの継承
Python 入門 307
名前空間(NAMESPACE)
Python 入門 308
名前空間
• 変数名などの重複を防ぐための仕組み
Python 入門 309
10万行以上の巨大なプログラム
.... global 変数 index .... global 変数 index
違う目的の変数を複数回定義(名前が衝突) 意図せず変数の中身が書き換わる
名前空間 • 変数名などの重複を防ぐための仕組み
Python 入門 310
10万行以上の巨大なプログラム
.... global 変数 index1 .... global 変数 index2 .... global 変数 index3
10万行以上の巨大なプログラム
.... 変数 index ....
.... 変数 index ....
.... 変数 index ....
.... 変数 index ....
名前空間なし: 変数名で衝突を防ぐ 名前空間あり: 衝突範囲が狭い
名前空間A 名前空間B
名前空間C 名前空間D
名前空間
• スコープ • 代表的なスコープ
1. ビルトインスコープ 2. グローバルスコープ 3. Classや関数のスコープ 4. for文などのローカルスコープ
Python 入門 311
クロージャ • 関数を生成する関数。別名はファクトリ関数 • 似たような関数を大量生成する際に使う
Python 入門 312
def getPrintFunccon(string): def f(): print(string) return f hello = getPrintFunccon("hello") python = getPrintFunccon("python") cisco = getPrintFunccon("cisco") hello() python() cisco()
try/except
• 複数の except 節を使って細かい制御が可能
Python 入門 313
try: 例外が発生する可能性のある処理 except エラークラス1: 例外処理1 except エラークラス2: 例外処理2 ※ エラークラスは先の「例外の種類」のクラス
try/except/else • else節で「例外がない場合の処理」を書ける
Python 入門 314
try: a = 1 + 1 print("1: " + str(a)) b = 5 / 0 print("2: " + str(b)) c = a + 1 print("3: " + str(c)) except Excepcon: print("Error happens") else: print("No problem") print("Done")
try: a = 1 + 1 print("1: " + str(a)) #b = 5 / 0 #print("2: " + str(b)) c = a + 1 print("3: " + str(c)) except Excepcon: print("Error happens") else: print("No problem") print("Done")
1: 2 Error happens Done
1: 2 3: 3 No problem Done
例外を発生させる
Python 入門 315
try: print(1) raise Excepcon("Hello") print(2) except Excepcon: print("Error happens") print(3)
1 Error happens 3
デバッグ
Python 入門 316
デバッグの役割
• プログラムの問題を発見する – プログラムを運用させる前に発見し修正するため – 運用後に見つかった問題を修正するため
Python 入門 317
プリントデバッグ
• 一番手軽にできる基本的な debug 手法
Python 入門 318
モジュールレベルのデバッグ
if __name__ == "__main__": モジュールのテストコード
Python 入門 319
assercon
• デバッグに特化した rise 処理
Python 入門 320
PyUnit によるユニットテスト
Python 入門 321
pdbによるデバッグ
Python 入門 322
デバッグ処理
Python 入門 323
オブジェクト指向(発展編)
Python 入門 324
オブジェクトツリー
Python 入門 325
ガーベジコレクション
Python 入門 326
型チェック
Python 入門 327
ポリモーフィズム
Python 入門 328
ダックタイピング
Python 入門 329
リスト発展編
Python 入門 330
map 関数
• List の各要素に対して関数を適用する関数
Python 入門 331
N = 10 def double(x): return x * 2 list1 = map(double, range(N)) list2 = [] for i in range(N): list2.append(i*2) print(list1) => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] print(list2) => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
map(関数, リスト)
filter 関数
• List の各要素に対して関数を適用し、True になった要素のみで List を作る
Python 入門 332
def isOver0(x): return x > 0 list1 = range(-‐5,5) list2 = filter(isOver0, list1) print(list1) => [-‐5, -‐4, -‐3, -‐2, -‐1, 0, 1, 2, 3, 4] print(list2) => [1, 2, 3, 4]
lamda式
• 関数をその場で定義するための手法 • 関数を返す
Python 入門 333
lamda(引数): 処理
>>> power = lambda x: x**2 >>> list1 = range(10) >>> map(power, list1) [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> map(lambda x: x**2, range(10)) [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
reduce
• 「たたみ込み」と呼ばれる処理
Python 入門 334
>>> getSum = lambda x, y: x+y >>> reduce(getSum, range(10)) 45
yield
Python 入門 335
リスト内包表記
• if 文を使って要素を絞れる
Python 入門 336
>>> [x**2 for x in range(10) if x % 2 == 0] [0, 4, 16, 36, 64] 1. リストから偶数のみ抜き出す 2. 偶数のリストに対して 2乗する処理を適用
[処理 for 要素 in リスト if 条件式] リストの要素から条件式がTrueになるもののみ抽出 それに対して処理を適用
Python 入門 337
ドキュメント
Python 入門 338
dir
• クラスやパッケージの中身を確認する関数
Python 入門 339
>>> dir(str) ['__add__', '__class__', '__contains__', '__dela7r__', '__doc__', '__eq__', '__format__', '__ge__', '__geta7ribute__', '__gectem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__seta7r__', '__sizeof__', '__str__', '__subclasshook__', '_forma7er_field_name_split', '_forma7er_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'isctle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'parccon', 'replace', 'rfind', 'rindex', 'rjust', 'rparccon', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'ctle', 'translate', 'upper', 'zfill']
help • man コマンドに近い関数
Python 入門 340
>>> help(int) Help on class int in module __builcn__: class int(object) | int(x=0) -‐> int or long | int(x, base=10) -‐> int or long | | Convert a number or string to an integer, or return 0 if no arguments <省略> | Methods defined here: | | __abs__(...) | x.__abs__() <==> abs(x) | | __add__(...) | x.__add__(y) <==> x+y
PYTHON ON NEXUS
Python 入門 341
Python Script on Nexus
• Nexus 内で特定の処理をさせることが可能 • 機器の状態に応じたコマンドの発行 • 定期的な複雑なログの取得
Python 入門 342
強力なEEMとしての利用
• 従来の EEM のイベントで python script を呼び出す • show コマンドの発行、解析を python にさせる • 結果に応じてclear mac や、shut/no shutコマンドの発行
Python 入門 343
Python Script on Nexus VPC+ 障害時における RIB と FIB Inconsistency 解消
Python 入門 344
N7K-‐1 SID:71
N6K-‐1 SID:61
N7K-‐2 SID:72
N6K-‐2 SID62
Z
Z
N7K-‐A(OK)
N7K-‐B(OK)
IXIA-‐1
IXIA-‐2
Z
Z
Z
Z
FP NETWORK
keep alive
keep alive
secondary primary 事象(2013年末に発生) N7K1-‐2 においてVPC+の冗長構成が 壊れた際にFabricPath Route の FIB が 更新されない。 その結果として間違ったスイッチに 転送を継続し続けて Dropが発生 解消法 VPC+ の secondary 側のFP網に接する 物理リンクを shut させることで FIBを 強制的に更新させる
Python 入門 345
MKI-‐VDC2# show file boomlash:if-‐mon.py # VPC の状態確認コマンドを発行。VPC のステートを得る shVpc = cli ("show vpc") shVpcLines = shVpc.splitlines() stateList = shVpcLines[11].split() vpcState = stateList[len(state_list)-‐1] # もしVPCが secondary なら、特定インタフェースを落とす if (vpcState == "secondary"): print("shutdown fp interface") cli("conf t ; int e7/9,e9/9 ; shut")
EEM で peer-‐link のダウンを検出 (コンソール表示をチェック) 検出したら、以下のスクリプトを実行
検証での python script の利用
• 多量のコマンドの出力結果を何度も得る • 定期的にコマンドを発行
Python 入門 346
手元のメモ帳に取得コマンド一覧を作って流し込む -‐ 作るのが簡単 -‐ 融通が効かない(書き出しファイル名の変更などが大変) python script を呼び出す -‐ 作るのが若干大変 -‐ 修正が簡単 -‐ 融通が効く
FPの検証プログラム
• 実際に検証で使ったプログラムのCSC版
Python 入門 347
Python Script を利用したログの取得方法 h7ps://suppormorums.cisco.com/docs/DOC-‐39662
Python 入門 348
if __name__ == "__main__": if(len(sys.argv) == 4): fileName = sys.argv[1] sleepInterval = int(sys.argv[2]) sleepCount = int(sys.argv[3]) print("taking Rate logs") writeTime(fileName) writeRateLogs(fileName, sleepInterval, sleepCount) print("taking FP logs") writeTime(fileName) writeFPLogs(fileName) writeOldLogs(fileName)
def makeEcho(string, fileName): return 'echo \"' + string + '\" >> boomlash:' + fileName def makeShow(string, fileName): return string + " >> boomlash:" + fileName def makeCombi(string, fileName): #コマンドの時刻, 名前, 結果を書く return makeShow("show clock", fileName) + " ;" + makeEcho("#" + string, fileName) + " ;" + makeShow(string, fileName) + " ;" + makeEcho("", fileName) def writeRateLogs(fileName, interval, n): # Rate を定期測定 for i in range(0, n): cli(makeCombi(SHOW_RATE_BETWEEN_N6K_AND_N7K, fileName)) cli(makeCombi(SHOW_RATE_AT_PEER_LINK, fileName)) cli(makeEcho("", fileName)) cme.sleep(n) def writeFPLogs(fileName): # コマンドの羅列 cli(makeCombi(SHOW_FP_ISIS_ROUTE, fileName)) cli(makeCombi(SHOW_FWM_L2MP_NEXT_HOP, fileName)) cli(makeCombi(SHOW_FWM_HWSTM, fileName)) cli(makeCombi(SHOW_FWM_L2MP_ROUTE, fileName)) cli(makeCombi(SHOW_FWM_MAC_HSRP_VMAC, fileName)) cli(makeCombi(SHOW_FWM_L2MP_VLAN, fileName)) cli(makeCombi(SHOW_FWM_L2MP_TOPO_0, fileName))
ライブラリ紹介
Python 入門 349
日付
Python 入門 350
数学
Python 入門 351
スレッド
Python 入門 352
データベース
Python 入門 353
ネットワークプログラミング
Python 入門 354
webアプリケーション
Python 入門 355
GUI
Python 入門 356