#12.1 コードのテスト
##12.1.1 pylint、pyflakes、pep8によるチェック
a=1
b=2
print(a)
print(b)
print(c)
$ pylint style1.py
************* Module style1
style1.py:1:1: C0326: Exactly one space required around assignment
a=1
^ (bad-whitespace)
style1.py:2:1: C0326: Exactly one space required around assignment
b=2
^ (bad-whitespace)
style1.py:1:0: C0114: Missing module docstring (missing-module-docstring)
style1.py:1:0: C0103: Constant name "a" doesn't conform to UPPER_CASE naming style (invalid-name)
style1.py:2:0: C0103: Constant name "b" doesn't conform to UPPER_CASE naming style (invalid-name)
#先頭がEとなっているのはエラーを示す。
style1.py:5:6: E0602: Undefined variable 'c' (undefined-variable)
-------------------------------------
Your code has been rated at -10.00/10
エラー解消のため、cの値を追加する。
a=1
b=2
c=3
print(a)
print(b)
print(c)
$ pylint style2.py
************* Module style2
style2.py:1:1: C0326: Exactly one space required around assignment
a=1
^ (bad-whitespace)
style2.py:2:1: C0326: Exactly one space required around assignment
b=2
^ (bad-whitespace)
style2.py:3:1: C0326: Exactly one space required around assignment
c=3
^ (bad-whitespace)
style2.py:1:0: C0114: Missing module docstring (missing-module-docstring)
style2.py:1:0: C0103: Constant name "a" doesn't conform to UPPER_CASE naming style (invalid-name)
style2.py:2:0: C0103: Constant name "b" doesn't conform to UPPER_CASE naming style (invalid-name)
style2.py:3:0: C0103: Constant name "c" doesn't conform to UPPER_CASE naming style (invalid-name)
------------------------------------
Your code has been rated at -1.67/10
変数名を長くしてみる。
def func():
first=1
second=2
third=3
print(first)
print(second)
print(third)
func()
$ pylist style3.py
-bash: pylist: command not found
uemuratntonoAir:bin uemura$ pylint style3.py
************* Module style3
style3.py:3:9: C0326: Exactly one space required around assignment
first=1
^ (bad-whitespace)
style3.py:4:10: C0326: Exactly one space required around assignment
second=2
^ (bad-whitespace)
style3.py:5:9: C0326: Exactly one space required around assignment
third=3
^ (bad-whitespace)
style3.py:1:0: C0114: Missing module docstring (missing-module-docstring)
style3.py:1:0: C0116: Missing function or method docstring (missing-function-docstring)
-----------------------------------
Your code has been rated at 3.75/10
##12.1.2 unittestによるテスト
- コードをソース管理システムにコミットする前に、まずテストプログラムを書いて、コードが全てのテストに合格することを確認する作業はグッドプラクティス。
def just_do_it(text):
return text.capitalize()
import unittest
import cap
class TestCap(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_one_word(self):
text="duck"
result=cap.just_do_it(text)
self.assertEqual(result, "Duck")
def test_multiple_words(self):
text="a veritable flock of ducks"
result=cap.just_do_it(text)
self.assertEqual(result, "A Veritable Flock Of Ducks")
#assertで始まる名前を持つメソッドで結果をチェックする。
if __name__=="__main__":
unittest.main()
$ python test_cap.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
$ python test_cap.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
$ python test_cap.py
..F
======================================================================
FAIL: test_words_with_apostrophes (__main__.TestCap)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_cap.py", line 25, in test_words_with_apostrophes
self.assertEqual(result, "I'm Fresh Out Of Ideas")
AssertionError: "I'M Fresh Out Of Ideas" != "I'm Fresh Out Of Ideas"
#^は実際に文字列が異なっている箇所を示している。
- I'M Fresh Out Of Ideas
? ^
+ I'm Fresh Out Of Ideas
? ^
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)
capitalize()は先頭の単語のみタイトルケースにする。
#capitalize()をtitle()に書き換え
def just_do_it(text):
return text.title()
$ python test_cap.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
import unittest
import cap
class TestCap(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_one_word(self):
text="duck"
result=cap.just_do_it(text)
self.assertEqual(result, "Duck")
def test_multiple_words(self):
text="a veritable flock of ducks"
result=cap.just_do_it(text)
self.assertEqual(result, "A Veritable Flock Of Ducks")
#assertで始まる名前を持つメソッドで結果をチェックする。
def test_words_with_apostrophes(self):
text="I'm fresh out of ideas"
result=cap.just_do_it(text)
self.assertEqual(result, "I'm Fresh Out Of Ideas")
def test_words_with_quotes(self):
text="\"You're despicable,\" said Daffy Duck"
result=cap.just_do_it(text)
self.assertEqual(result, "\"You're Despicable,\" Said Daffy Duck")
if __name__=="__main__":
unittest.main()
$ python test_cap.py
...F
======================================================================
FAIL: test_words_with_quotes (__main__.TestCap)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_cap.py", line 30, in test_words_with_quotes
self.assertEqual(result, "\"You're Despicable,\" Said Daffy Duck")
AssertionError: '"you\'re Despicable," Said Daffy Duck' != '"You\'re Despicable," Said Daffy Duck'
- "you're Despicable," Said Daffy Duck
? ^
+ "You're Despicable," Said Daffy Duck
? ^
----------------------------------------------------------------------
Ran 4 tests in 0.002s
FAILED (failures=1)
##12.1.3 noseによるテスト
- noseはサードパーティパッケージ。
import cap
from nose.tools import eq_
def test_one_word(self):
text="duck"
result=cap.just_do_it(text)
eq_(result, "Duck")
def test_multiple_words(self):
text="a veritable flock of ducks"
result=cap.just_do_it(text)
eq_(result, "A Veritable Flock Of Ducks")
def test_words_with_apostrophes(self):
text="I'm fresh out of ideas"
result=cap.just_do_it(text)
eq_(result, "I'm Fresh Out Of Ideas")
def test_words_with_quotes(self):
text="\"You're despicable,\" said Daffy Duck"
result=cap.just_do_it(text)
eq_(result, "\"You're Despicable,\" Said Daffy Duck")
unittestnでテストしたときに見つけたのと同じバグ。
$ nosetests test_cap_nose.py
E
======================================================================
ERROR: Failure: IndentationError (unexpected indent (test_cap_nose.py, line 4))
----------------------------------------------------------------------
...(省略)
def test_one_word(self):
^
IndentationError: unexpected indent
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
#12.2 Pythonコードのデバッグ
from dump1 import dump
@dump
def double(*args, **kwargs):
"Double every argument"
output_list=[2*x for x in args]
output_dict={k:2*v for k,v in kwargs.items()}
return output_list, output_dict
if __name__=="__main__":
output=double(3, 5, first=100,next=98.6, last=-40)
$ python test_dump.py
Function name: double
Input arguments: 3 5
Input keyword arguments: dict_items([('first', 100), ('next', 98.6), ('last', -40)])
Output: ([6, 10], {'first': 200, 'next': 197.2, 'last': -80})
#12.3 pdbによるデバッグ
def process_cities(filename):
with open(filename, "rt") as file:
for line in file:
line=line.strip()
if "quit" in line.lower():
return
country, city=line.split(",")
city=city.strip()
country=country.strip()
print(city.title(),country.title(), sep=",")
if __name__=="__main__":
import sys
process_cities(sys.argv[1])
$ python capitals.py cities1.csv
Paris,France
Caracas,Venuzuela
Vilnius,Lithunia
cities2.csvを実行中、15行中わずか5行出力したところで停止。
$ python capitals.py cities2.csv
Buenos Aires,Argentinam
La Paz,Bolivia
Brasilia,Brazil
Santiago,Chile
Bogota,Colombia
$ python -m pdb capitals.py cities2.csv
#cと入力すると正常終了もしくはエラーで止まる。
(Pdb) c
Buenos Aires,Argentinam
La Paz,Bolivia
Brasilia,Brazil
Santiago,Chile
Bogota,Colombia
The program finished and will be restarted
> /Users/practice/bin/capitals.py(1)<module>()
-> def process_cities(filename):
#sと入力すると行を順に実行する。
(Pdb) s
> /Users/practice/bin/capitals.py(13)<module>()
-> import sys
(Pdb) s
> /Users/practice/bin/capitals.py(14)<module>()
-> process_cities(sys.argv[1])
(Pdb) s
--Call--
> /Users/practice/bin/capitals.py(1)process_cities()
-> def process_cities(filename):
(Pdb) s
> /Users/practice/bin/capitals.py(2)process_cities()
-> with open(filename, "rt") as file:
#第6行にブレークポイントセットした。
(Pdb) b 6
Breakpoint 1 at /Users/uemura/practice/bin/capitals.py:6
#ブレークポイントに当たるか、入力行を全て読み出して終了するまでプログラム実行する。
(Pdb) c
Buenos Aires,Argentinam
La Paz,Bolivia
Brasilia,Brazil
Santiago,Chile
Bogota,Colombia
> /Users/practice/bin/capitals.py(6)process_cities()
-> return
#何を読み出したのか確認する。
(Pdb) p line
'ecuador,quito'
(Pdb) b
Num Type Disp Enb Where
1 breakpoint keep yes at /Users/uemura/practice/bin/capitals.py:6
breakpoint already hit 1 time
#1はコード行、現在位置(->)、ブレークポイント(B)を示す。
#オプションなしのlは前回のlの表示の末尾から表示を始めるのでオプションで指定する。
(Pdb) l 1
1 def process_cities(filename):
2 with open(filename, "rt") as file:
3 for line in file:
4 line=line.strip()
5 if "quit" in line.lower():
6 B-> return
7 country, city=line.split(",")
8 city=city.strip()
9 country=country.strip()
10 print(city.title(),country.title(), sep=",")
11
quitテストが行全体でquitになっているものだけマッチするように書き換える。
def process_cities(filename):
with open(filename, "rt") as file:
for line in file:
line=line.strip()
if "quit" ==line.lower():
return
country, city=line.split(",")
city=city.strip()
country=country.strip()
print(city.title(),country.title(), sep=",")
if __name__=="__main__":
import sys
process_cities(sys.argv[1])
$ python capitals2.py cities2.csv
Buenos Aires,Argentinam
La Paz,Bolivia
Brasilia,Brazil
Santiago,Chile
Bogota,Colombia
Quito,Ecuador
Stanley,Falkland Islands
Cayenne,French Guiana
Asuncion,Paraguay
Lima,Peru
Paramaribo,Suriname
Montevideo,Uruguay
Caracas,Venezuela
#12.4 エラーメッセージのロギング
- ロギングのPython標準ライブラリはloggingモジュール。
- ログに保存したいメッセージ
- ランク付けのための優先順位レベルとそれに対応する関数
- モジュールとの主要な通信経路となる一つ以上のロガーオブジェクト
- メッセージを端末、ファイル等そのほかの場所に送るハンドラ
- 出力を作成するフォーマッタ
- 入力に基づいて判断を下すフィルタ
>>> import logging
>>> logging.debug("Looks like rain")
>>> logging.info("And hill")
#デフォルトの優先順位レベルはWARNIGである。
>>> logging.warn("Did I hear thunder?")
__main__:1: DeprecationWarning: The 'warn' function is deprecated, use 'warning' instead
WARNING:root:Did I hear thunder?
>>> logging.critical("Stop fencing and get inside")
CRITICAL:root:Stop fencing and get inside
#デフォルトレベルはbasicConfig()で設定できる。
#DEBUG以上の全てのレベルがログに書かれる。
>>> import logging
>>> logging.basicConfig(level="DEBUG")
>>> logger.debug("Timber")
DEBUG:bunyan:Timber
>>> logging.info("And hill")
INFO:root:And hill
#12.5 コードの最適化
##12.5.1 実行時間の計測
#現在の時刻から処理終了時刻をひく。
from time import time
t1=time()
num=5
num*=2
print(time()-t1)
$ python time1.py
3.0994415283203125e-06
$ python time1.py
1.9073486328125e-06
$ python time1.py
1.6689300537109375e-06
from time import time, sleep
t1=time()
sleep(1.0)
print(time()-t1)
$ python time2.py
1.0051441192626953
uemuratntonoAir:bin uemura$ python time2.py
1.0001447200775146
timeitモジュールのtimeit()関数を使うと便利。
#time.timeit("code",number,count)の構文
#timeitの内部で実行するためcodeは""で囲む必要がある。
from timeit import timeit
print(timeit("num=5;num*=2", number=1))
$ python timeit1.py
1.63200000000141e-06
uemuratntonoAir:bin uemura$ python timeit1.py
1.252999999999671e-06
#repeat()関数のrepeat引数を使うことで実行回数が増やせる。
from timeit import repeat
print(repeat("num=5;num*=2", number=1,repeat=3))
$ python timeit2.py
[8.809999999977169e-07, 2.57000000000035e-07, 1.659999999993611e-07]
##12.5.2 アルゴリズムとデータ構造
リスト内包表記とforループのどちらが早いか。
from timeit import timeit
def make1():
result=[]
for value in range(1000):
result.append(value)
return result
def make2():
result=[value for value in range(1000)]
return result
print("make1 takes", timeit(make1, number=1000), "seconds")
print("make2 takes", timeit(make2, number=1000), "seconds")
リスト内包表記の方が高速である。
$ python time_lists.py
make1 takes 0.07954489899999999 seconds
make2 takes 0.035908797000000006 seconds
#12.6 Gitでのソース管理
#新しいディレクトリ作成後、移動する。
$ mkdir newdir
$ cd newdir
#カレントディレクトリのnewdirにローカルGitリポジトリを作る。
$ git init
Initialized empty Git repository in /Users/practice/bin/newdir/.git/
#Gitリポジトリにファイルを追加する。
$ git add test.py
#Gitの状態確認。
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: test.py
#-m "my first commit"というコミットメッセージをつけてコミットする。
$ git commit -m "my first commit"
[master (root-commit) e03cd1c] my first commit
1 file changed, 1 insertion(+)
create mode 100644 test.py
#変更はコミット済み。
$ git status
On branch master
nothing to commit, working tree clean
#感想
1周目ですが約1ヶ月で完走できた。ただ、アウトプットができていないのでアウトプットをメインに次の1ヶ月はやっていく。
######参考文献
「Bill Lubanovic著 『入門 Python3』(オライリージャパン発行)」