7
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ざっくりPythonのおさらい その2。おもにデータ保存

Last updated at Posted at 2018-08-31

前回に引き続いて、データ保存周りのおさらいです。
その他のおさらい事項にについては、以下ご参照ください。

[ざっくりPythonのおさらい その1。基本文法など]
https://qiita.com/48hands/items/31b7ac2b49addbb8658c

[ざっくりPythonのおさらい その2。おもにデータ保存]
https://qiita.com/48hands/items/ab86b7463268e669d216

[ざっくりPythonのおさらい その3。Numpy, Pandas]
https://qiita.com/48hands/items/b1a71000533e129b27f2

PythonからRDBを扱う

事前にmysql-connector-python, sqlalchemy, pymysqlをパッケージインストールしておく。

pip install mysql-connector-python sqlalchemy pymysql

ORMを使わない実装例

SQLite3

import sqlite3

# コネクション生成 ファイルに永続化したい場合
conn = sqlite3.connect('my_sqlite.db')

# コネクション生成 メモリ上で完結させる場合はこっちを使う。
# conn = sqlite3.connect(':memory:')

# カーソルの生成
cursor = conn.cursor()

# テーブルの作成
sql = """
CREATE TABLE IF NOT EXISTS persons(
  id INTEGER PRIMARY KEY AUTOINCREMENT
 ,name STRING
)
"""
cursor.execute(sql)
conn.commit()

# データのINSERT
cursor.execute("INSERT INTO persons(name) VALUES('Hanako')")
cursor.execute("INSERT INTO persons(name) VALUES('Taro')")
cursor.execute("INSERT INTO persons(name) VALUES('Yuichi')")
cursor.execute("INSERT INTO persons(name) VALUES('Makio')")
conn.commit()

# データのSELECT
sql = "SELECT * from persons"
cursor.execute(sql)

# カーソルに結果が入っているので受け取る
# fetchall()メソッドを使う。
result = cursor.fetchall()
for user_id, name in result:
    print(user_id, name)

# コネクションのクローズ
conn.close()

MySQL

ほとんどSQLiteと同じ。

import mysql.connector

# コネクションの設定
conn = mysql.connector.connect(host='127.0.0.1', port=3306, user='root',
    # password='password',
    # database='my_db'
)

# カーソルの生成
cursor = conn.cursor()

# データベース作成
cursor.execute('CREATE DATABASE IF NOT EXISTS my_db')

# テーブル作成
cursor.execute('CREATE TABLE IF NOT EXISTS my_db.persons('
               'id int NOT NULL AUTO_INCREMENT,'
               'name varchar(20) NOT NULL,'
               'PRIMARY KEY(id)'
               ')')


# データのINSERT
for i in range(10):
    cursor.execute(
        'INSERT INTO my_db.persons(name) values("Robot-{}")'.format(str(i + 1)))

# コミット
conn.commit()


# データのSELECT
cursor.execute('SELECT * FROM my_db.persons')

for user_id, name in cursor.fetchall():
    print(user_id, name)


# カーソルのクローズ
cursor.close()

# コネクションクローズ
conn.close()

SQLAlchemyを使った実装

データベースをSQLiteのメモリに設定する場合は以下。
personsテーブルを作成してPersonオブジェクトとして扱えるようにしている。

import sqlalchemy
import sqlalchemy.ext.declarative
import sqlalchemy.orm

# sqliteを使ってメモリで完結させる。
# echoをTrueにすると、実際に発行したSQLを確認できる
engine = sqlalchemy.create_engine('sqlite:///:memory:', echo=True)

# おまじない
Base = sqlalchemy.ext.declarative.declarative_base()


# Personモデルの定義
class Person(Base):
    __tablename__ = 'persons'

    id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True,
        autoincrement=True)

    name = sqlalchemy.Column(sqlalchemy.String(20))


# これを実行するとメモリ上にpersonsテーブルが作成される。
Base.metadata.create_all(engine)

# セッションの作成
Session = sqlalchemy.orm.sessionmaker(bind=engine)
session = Session()

# データの登録
person_list = [Person(name='Hanako'), Person(name='Taro'), Person(name='Makio')]
for p in person_list:
    # addメソッドを使って登録する
    session.add(p)
    session.commit()

# データを取得してnameを変更してupdateする。
p1 = session.query(Person).filter_by(name='Taro').first()
p1.name = 'Mike'
session.add(p1)
session.commit()

# データを削除する
p1 = session.query(Person).filter_by(name='Hanako').first()
session.delete(p1)
session.commit()

# 一覧を取得する
persons = session.query(Person).all()
for person in persons:
    print(person.id, person.name)

データベースをSQLiteのファイルに永続化する場合はengineを以下のように設定する。

engine = sqlalchemy.create_engine('sqlite:///dev_db_sqlalchemy', echo=True)

データベースをMySQLにしたい場合は、以下のように設定する。

# mysql+pymysqlを指定する
url = 'mysql+pymysql://username:password@127.0.0.1/my_db'
engine = sqlalchemy.create_engine(url, echo=True)

PythonのDBMを扱う

DBMはPythonの標準ライブラリ。
簡単にキャッシュの保存などを使いたい場合に利用するらしい。

import dbm


with dbm.open('cache', 'c') as db:
    # 文字列をキャッシュとして保存する
    db['key1'] = 'value1'
    db['key2'] = 'value2'

    # NOTE: 数値はそのまま保存するとエラーになる。文字列のみ対応している。
    # db['key3'] = 123


with dbm.open('cache', 'r') as db:
    print(db.get('key1')) # b'value1'となり、バイナリ型になって表示される
    print(db.get('key2').decode('utf-8')) # utf-8でデコードするとvalue2として表示される
b'value1'
value2

PythonからMemchachedを扱う

Memchachedは、データをメモリ上に保存しておいてデーベースへのアクセスの前に参照してデータベースにアクセスする回数を減らしたりするために使ったりするソフトウェア。

[Memcheched]
http://memcached.org/

Memchachedのインストール

# Linuxの場合
wget https://www.memcached.org/files/memcached-1.5.10.tar.gz
tar -zxvf memcached-1.5.10.tar.gz
cd memcached-1.5.10
./configure && make && make test && sudo make install

# Macの場合はbrew installでできる。
brew install memcached
brew services start memcached


# python-memchacheパッケージをインストールしておく
pip install python-memcached

Pythonから扱うためのコードは以下。

import memcache

# memcacheのクライアント設定
db = memcache.Client(['127.0.0.1:11211'])

# キーバリューの形式でセットする。
db.set('key1', 'value1')
db.set('key2', 'value2')

# データの取得
print(db.get('key1'))

保持する時間を設定できる。
以下のようにtimeに値をセットする。

import time

import memcache

# memcacheのクライアント設定
db = memcache.Client(['127.0.0.1:11211'])

# キーバリューの形式でセットする。
db.set('key1', 'value1', time=1)

# データの取得
print(db.get('key1'))

# sleepする
time.sleep(2)

# データ取得できないことを確認する。
# 値が取れない場合はNoneが返却される
print(db.get('key1'))

実行すると、以下のようになる。

value1
None

RDBとMemcacheの併用

準備として、SQLiteにデータを入れておく。

import sqlite3


cursor.execute('CREATE TABLE IF NOT EXISTS persons'
               '(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING)')
cursor.execute('INSERT INTO persons(name) VALUES("Makio")')
conn.commit()
conn.close()

この状態でMemchacheから値が取れなかった場合に、DBアクセスする。

import sqlite3
import memcache

# memcacheのクライアント設定
mem = memcache.Client(['127.0.0.1:11211'])

# SQLiteのコネクション、カーソル設定
conn = sqlite3.connect('test_sqlite_memcached.db')
cursor = conn.cursor()


def get_person_id(name):
    """
    nameからidを返却するメソッド
    """
    # Memcachedからidがとれればそのまま返却
    # DBにアクセスしない。
    person_id = mem.get(name)
    if person_id:
        return person_id

    # 以下はMemchachedにデータがない場合
    # DBに対して検索処理
    sql= 'SELECT * FROM persons WHERE name = "{}"'.format(name)
    cursor.execute(sql)

    person = cursor.fetchone()
    if not person:
        raise Exception('No person data')

    person_id, name = person # アンパッキング

    # 60秒以内だったらMemchacheから値を参照できるように
    # Memchachedに値を設定しておく。
    mem.set(name, person_id, time=60)

    return person_id

print(get_person_id("Makio"))

Pickleを使ったオブジェクトの保存

Pythonのデータをそのまま保存するライブラリ

import pickle

class Person(object):
    def __init__(self, name):
        self.name = name

obj = {
    'key1': [1,2,3,4,5],
    'key2': ['dev', 'prod', 'test'],
    'key3': Person('Makio')
}

# データの保存処理。
# バイナリ形式でオブジェクトを保存する。
with open('obj.pickle', 'wb') as f:
    pickle.dump(obj, f)

# pickleファイルからデータを読み込む。
with open('obj.pickle', 'rb') as f:
    read_obj = pickle.load(f)

# 読込できたか確認する。
print(read_obj)
print(read_obj['key1'])
print(read_obj['key2'])
print(read_obj['key3'].name)

実行結果。

{'key1': [1, 2, 3, 4, 5], 'key2': ['dev', 'prod', 'test'], 'key3': <__main__.Person object at 0x10e328588>}
[1, 2, 3, 4, 5]
['dev', 'prod', 'test']
Makio
7
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?