今さらPython3 (62) - 第8章復習課題
第8章もこれで終わり。最後に復習課題をやってみる。
- 作者: Bill Lubanovic,斎藤康毅,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/12/01
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
8.1
>>> test1 = 'This is a test of the emergency text system.' >>> fout = open('test.txt', 'wt') >>> fout.write(test1) 44 >>> fout.close()
あるいは、withを使ってclose()なしのパターンでも。
>>> with open('test.txt', 'wt') as fout: ... fout.write(test1) ... 44
8.2
test.txtに何行あるか分からない体で行くと、
>>> with open('test.txt', 'rt') as fin: ... test2 = fin.readlines() ... >>> test2 ['This is a test of the emergency text system.'] >>> test2[0] == test1 True
だけど、1行だって分かりきっているんだから、こっちでいいんだよね。
>>> with open('test.txt', 'rt') as fin: ... test2 = fin.readline() ... >>> test1 == test2 True >>>
8.3
>>> text = '''author, book ... J R R Tolkien, The Hobbit ... Lynne Truss, "Eats, Shoots & Leaves" ... ''' >>> with open('books.csv', 'wt') as fout: ... fout.write(text) ... 76
まんまだね。
8.4
>>> import csv >>> with open('books.csv', 'rt') as fin: ... cin = csv.DictReader(fin) ... books = [row for row in cin] ... >>> books [{'author': 'J R R Tolkien', ' book': ' The Hobbit'}, {'author': 'Lynne Truss', None: [' Shoots & Leaves"'], ' book': ' "Eats'}] >>>
あれ?失敗している。Eatsの直後のカンマで項目が分割されている。正しく処理できていないというのが答え?んなアホな。
ということで基本に立ち返る。本の中でDictWriter()のサンプルがあったので、それを試す。
>>> books_list = [{'author': 'J R R Tolkien', 'book': 'The Hobbit'}, {'author': 'Lynne Truss', 'book':'Eats, Shoots & Leaves'}] >>> with open('books2.csv', 'wt') as fout: ... cout = csv.DictWriter(fout, ['author', 'book']) ... cout.writeheader() ... cout.writerows(books_list) ... >>>
できたファイルをさっき作った回答案で処理してみる。
>>> with open('books2.csv', 'rt') as fin: ... cin = csv.DictReader(fin) ... books = [row for row in cin] ... >>> books [{'author': 'J R R Tolkien', 'book': 'The Hobbit'}, {'author': 'Lynne Truss', 'book': 'Eats, Shoots & Leaves'}] >>>
ちゃんと出来ている。という事は原因はCSVファイルの作り方にありということかな。
これは、ちゃんと処理できた方のCSV。
author,book J R R Tolkien,The Hobbit Lynne Truss,"Eats, Shoots & Leaves"
これは、ダメだった方。
author, book J R R Tolkien, The Hobbit Lynne Truss, "Eats, Shoots & Leaves"
見た目じゃほとんど分からないと思うけど、カンマの後ろに無意識にスペースを入れていたのが原因。これによって、ダブルクオテーション(")がただの文字列として認識された模様。
8.5
さっきのトラブルシューティングで、すでにやってしまったような気がするけど、とりあえずやる。
>>> text = '''title,author,year ... The Weirdstone of Brisingamen,Alan Garner,1960 ... Perdido Street Station,China Miéville,2000 ... Thund!,Terry Pratchett,2005 ... The Spellman Files,Lisa Lutz,2007 ... Small Gods,Terry Pratchett,1992 ... ''' >>> with open('books3.csv', 'wt') as fout: ... fout.write(text) ... 202
なんか1バイト長いけど、まいっか。
8.6
この章って、いろんなものを少しずつ触ってるんで、sqliteがすでに懐かしいw。
>>> import sqlite3 >>> conn = sqlite3.connect('books.db') >>> curs = conn.cursor() >>> curs.execute('''CREATE TABLE book (title VARCHAR(30), author VARCHAR(20), year INTEGER)''') <sqlite3.Cursor object at 0x1020a7730> >>>
titleとauthorは適当な長さ付けちゃったけど収まるかな?
8.7
>>> ins = 'INSERT INTO book (title, author, year) VALUES(?, ?, ?)' >>> with open('books3.csv', 'rt') as fin: ... cin = csv.DictReader(fin) ... books = [row for row in cin] ... >>> books [{'author': 'Alan Garner', 'year': '1960', 'title': 'The Weirdstone of Brisingamen'}, {'author': 'China Miéville', 'year': '2000', 'title': 'Perdido Street Station'}, {'author': 'Terry Pratchett', 'year': '2005', 'title': 'Thund!'}, {'author': 'Lisa Lutz', 'year': '2007', 'title': 'The Spellman Files'}, {'author': 'Terry Pratchett', 'year': '1992', 'title': 'Small Gods'}]
後で使うからSQL文を先に用意して(ins)、csv.DictReader()を使って辞書のリストにしておく。
>>> for book in books: ... curs.execute(ins, (book['title'], book['author'], book['year'])) ... <sqlite3.Cursor object at 0x1020a7730> <sqlite3.Cursor object at 0x1020a7730> <sqlite3.Cursor object at 0x1020a7730> <sqlite3.Cursor object at 0x1020a7730> <sqlite3.Cursor object at 0x1020a7730> >>> conn.commit()
コミットを打っておく。
8.8
>>> curs.execute('SELECT title FROM book ORDER BY title') <sqlite3.Cursor object at 0x1020a7730> >>> rows = curs.fetchall() >>> print(rows) [('Perdido Street Station',), ('Small Gods',), ('The Spellman Files',), ('The Weirdstone of Brisingamen',), ('Thund!',)] >>>
tupleで返ってくるところが美しくないかな。
>>> for row in rows: ... print(row[0]) ... Perdido Street Station Small Gods The Spellman Files The Weirdstone of Brisingamen Thund! >>>
ちなみに本では、英語圏らしくTheを取り除いた場合のパターンを紹介していたので、試してみる。
>>> curs.execute('SELECT title FROM book ORDER BY CASE WHEN (title like "The %") THEN SUBSTR(title, 5) ELSE title END') <sqlite3.Cursor object at 0x1020a7730> >>> for row in rows: ... print(row[0]) ... Perdido Street Station Small Gods The Spellman Files The Weirdstone of Brisingamen Thund! >>>
SUBSTR(X, Y, (Z))は、Xという文字列のY文字目から読み始めるという挙動をするみたいですね。後ろを切りたい場合はZを使うこともできると。
SQLite Query Language: Core Functions
データを取得して加工する時には、どこまでSQL側でやらせるか、どこからアプリ側に任せるかというは、なかなか一概に言えない部分はあるけど、方法を知っておくことは悪いことじゃない。
8.9
>>> curs.execute('SELECT * FROM book ORDER BY year DESC') <sqlite3.Cursor object at 0x1020a7730> >>> rows = curs.fetchall() >>> for row in rows: ... print(row) ... ('The Spellman Files', 'Lisa Lutz', 2007) ('Thund!', 'Terry Pratchett', 2005) ('Perdido Street Station', 'China Miéville', 2000) ('Small Gods', 'Terry Pratchett', 1992) ('The Weirdstone of Brisingamen', 'Alan Garner', 1960) >>>
指示はないけど、敢えて降順にしてみた。
>>> for row in curs.execute('SELECT * FROM book ORDER BY year DESC'): ... print(*row, sep=' ,') ... The Spellman Files ,Lisa Lutz ,2007 Thund! ,Terry Pratchett ,2005 Perdido Street Station ,China Miéville ,2000 Small Gods ,Terry Pratchett ,1992 The Weirdstone of Brisingamen ,Alan Garner ,1960 >>>
これは、本で紹介してあった方法。これだとfetchall()する必要がないからシンプルになるね。
8.10
sqlalchemyの登場ですね。これも思い出しながら。
>>> import sqlalchemy as sa >>> conn = sa.create_engine('sqlite:///books.db') >>> rows = conn.execute('SELECT title FROM book ORDER BY title') >>> for row in rows: ... print(row) ... ('Perdido Street Station',) ('Small Gods',) ('The Spellman Files',) ('The Weirdstone of Brisingamen',) ('Thund!',) >>>
エンジンレイヤじゃなくて、SQL表現言語とかORMなんかを使った方が練習になるかなと思ったけど、テーブル定義自体を外でやっているので、そのままでは動かないと思うので、ここは断念。
8.11
Redis登場ですね。インストールはすでにやったので、まずはRedis起動。
$ redis-server /usr/local/etc/redis.conf 12362:M 25 Dec 10:48:35.603 * Increased maximum number of open files to 10032 (it was originally set to 256). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 3.0.1 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 12362 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 12362:M 25 Dec 10:48:35.609 # Server started, Redis version 3.0.1 12362:M 25 Dec 10:48:35.610 * DB loaded from disk: 0.002 seconds 12362:M 25 Dec 10:48:35.610 * The server is now ready to accept connections on port 6379
今度はPython側で。
>>> import redis >>> conn = redis.Redis() >>> conn.hmset('test', {'count':1, 'name':'Fester BesterTester'}) True >>> conn.hvals('test') [b'1', b'Fester BesterTester'] >>>
フィールドって言っているから上のでOKだと思っているけど、キーとフィールド両方なら、こっちをやれば良いのかな。
>>> conn.hgetall('test') {b'count': b'1', b'name': b'Fester BesterTester'}
8.12
>>> conn.hincrby('test', 'count') 2
これ、本の中で紹介してないじゃん。ヘルプに書いてあったので、リンクを残しておく。
Welcome to redis-py’s documentation! — redis-py 2.4.9 documentation
(つづく)