お茶漬けびより

"あなたに教わったことを、噛んでいるのですよ" 五等分の花嫁 7巻 「最後の試験が五月の場合」より

Python 3.6 から MySQL データベースへ SSH 接続を試みる

f:id:pickles-ochazuke:20171210160653j:plain

仕事中は、いろいろ書きたい記事を思いつくのですが、いざ書こうとするとネタが思いつかないことがよくあります。
メモ代わりにここに書きますが、カテゴリとしては、Python, MySQL, グラフ描画, ウィンドウハンドルを使った自動化。みたいな感じです。忘れないうちに書かないと……。

そうそう、Google Home を買ったので、それを使って遊んだりもしたい。今は、Python, MySQL が優先なので、そちらを身につけつつその間に Google Home について調べておこうかなと。

無駄話はこれぐらいにして、本題に入ります。
前回は、ローカル(PC 内に入っているデータベース(DB)への接続)な環境の話でしたが、今回は、ネットワーク上の DB への接続を試してみます。

以下を参考にしました。

blog.honjala.net

環境の準備

まずは、今回使うモジュールを落としてきます。

pip install sshtunnel

ちなみにモジュールは、Github に公開されています。

github.com

これで SSH 接続するための環境は出来ました。 次は、実際にコードを書いて接続します。

SSH 接続を試みる

以下のコードを実行すると、SSH 接続するためのポート番号(何のポート番号かは分かっていません……)が出力されます。

# -*- coding: utf-8 -*-
from sshtunnel import SSHTunnelForwarder
import mysql.connector

port_num = 22
with SSHTunnelForwarder(
    ('ip_address', port_num),
    ssh_username='user_name',
    ssh_password='user_pass',
    remote_bind_address=('127.0.0.1', 3306)
) as server:

print(server.local_bind_port)

ip_address には、接続したい DB がある IP アドレスを。port_num には、SSH 接続のポート番号を(通常は、22)。remote_bind_address には、DB がある IP アドレスとポート番号を指定するのですが、通常は、SSH 接続先にあると思うので、上記で問題ないと思います。

上手くいけば、ポート番号が表示されていると思います。 問題なければ SSH 接続が成功しています。with を使わずに open(), close() のような方法もあります。以下のようにして、接続します。

port_num = 22
server SSHTunnelForwarder(
    ('ip_address', port_num),
    ssh_username='user_name',
    ssh_password='user_pass',
    remote_bind_address=('127.0.0.1', 3306)
)
server.start()

# 処理

server.stop()

start()open()stop()close() の役割をしています。 では、次に出力したポート番号を使って DB へ接続します。 ここでは、前回使用したモジュール(mysql.connector)を使って接続しています。

前回 pickles-ochazuke.hatenablog.com

conn = mysql.connector.connect(
    host = 'localhost',
    port = server.local_bind_port,
    user = 'user_name',
    password = 'user_pass',
    database = 'db_name',
)

前回とほぼ変わりません。port のところに先ほど出力したポート番号を使っています。

おわり

以上です。とても簡単にできたと思います。

今回の SSH 接続の部分とは関係ありませんが、クエリを実行して、結果を取得する際に単純に受け取るとリストになると思います。一応これでも扱えるのですが、pandas のデータフレームというのを使うともっと便利に扱えるようになります。そこら辺も近いうちに書きたいと思います。

Python 3.6 で MySQL データベースに接続

f:id:pickles-ochazuke:20171209164122j:plain

お久しぶりです。 9月に就職が決定して、そこから空いた一週間で北の方を旅行して、仕事が始まって最初の一ヶ月は給料なくて、11月に初というか3,4ヶ月ぶりの給料でなんとか生活を凌いでいます。
前の仕事は、C言語だったんですけど、今の場所はすることによって言語が変わるそうで、今は Python をメインに触っています。あと初めて SQL を仕事で使っていまして、知らないことだらけで初めてコンピュータサイエンスを学んだ時のようで楽しいです。

さて、今月はなるべく更新をしたいなと思っていますので、小さい内容でも更新していきたいと思っています。主に仕事で学んだことをまとめる方向にしたいので、PythonSQL の話になるかなーと。

では、本題です。 以下を参考にしています。

qiita.com

環境構築

Python のバージョンは、3.6.3 です。 バージョンの確認は、コマンドプロンプトpython --version を実行すれば確認できます。できなければ、パスが通ってないので、通しましょう。

まずは、必要なパッケージをインストールします。

pip install mysql-connector-python-rf

会社の環境だとプロキシの問題で、pip が上手くインストールしてくれないんですよね。そこら辺も覚えている範囲でまとめたい……。

mysql-connector-python も必要っぽいので、以下から落としてきます。
https://pypi.python.org/pypi/mysql-connector-python
こだわりがなければ最新のやつを落としてください。私は、mysql-connector-python-8.0.5.tar.gz を落としました。
落としたファイルの場所でコマンドプロンプトを開いて、以下を実行します。

pip install mysql-connector-python-8.0.5.tar.gz

そしたらインストールが成功するはずです。しなければ管理者権限でコマンドプロンプトを開いて、実行してみてください。 終わったら、pip list で出力されたパッケージリストに今入れた二つがあるか確認します。

MySQL をインストールしていなければ、インストールしましょう。ここでは、その説明は除きます。MySQL サーバを立ち上げて、データベースとテーブルを作成して、準備完了です。

MySQL データベース(DB)への接続

まずは、接続です。各引数は、自身の環境に合わせて変えてください。

import mysql.connector

conn = mysql.connector.connect(
    host = 'localhost',
    port = 3306,
    user = 'root',
    password = 'pass',
    database = 'test',
)

とくにエラーが発生しなければ成功しているはずです。この conn を使って、データベースに対していろいろ操作します。次に、接続が可能か調べるためには、以下を実行します。

connected = conn.is_connected()
print(connected)
if (not connected):
    conn.ping(True)

conn.is_connected() によって接続が可能か真偽値で返ってきます。True の場合は、接続が可能で、False の場合は、接続が不可能な状態です。conn.ping(True) は再接続を試みているのですが、conn.is_connected() が False なら再接続で解決はしなさそうですね……環境に合わせた対処が必要になると思います。

conn.ping(True) は、長い時間接続していると DB との接続が切れることがあるそうなので、その対処のために使うそうです。つまり、定期的に接続して切られないようにしている感じです。

クエリの実行

次にカーソルオブジェクトを作成します。カーソルというのは、データベースで使われる用語のようです。詳しくは知らないので、ここでは、クエリの実行と結果を受け取るために必要な DB への命令方法だと思えば問題ないと思います。

cur = conn.cursor()
cur.execute('select * from member')
table = cur.fetchall()
print(table)

conn.cursor() で、カーソルオブジェクトを作成し、cur.execute() で文字列をクエリとして DB に送ります。その結果を cur.fetchall() で受け取り、print() 出力しています。

ちなみに cur.fetchall() は、例えば SELECT の結果を全て受け取りますが、 cur.fetchone() は結果を一つだけ受け取ります。fetchone() は、何度も実行することで続きのデータを受け取ることができます。

例として、SELECT * FROM Table; というクエリを実行して、レコードが 3 つ現れるとします。fetchall() の場合は、この結果を全て受け取りますが、fetchone() では、レコードを 1 つだけ受け取ります。全て受け取る場合は、3 回実行する必要があるわけです。

それぞれにメリット、デメリットがあるようですがここでは説明しません(というか知りません……)。ちなみに、レコードを全て受け取った状態で fetchone()print() で出力すると None と出力されるので、 for 文などで簡単に回せそうです。

以上が簡単な MySQL データベースへの接続方法です。重要なのは、 mysql.connector.connect(), connect.cursor(), cursor.execute(), cursor.fetchall() ですかね。

最後に一通り実行できるコードを載せておきます。

# -*- coding: utf-8 -*-
import mysql.connector

conn = mysql.connector.connect(
    host = 'localhost',
    port = 3306,
    user = 'root',
    password = 'pass',
    database = 'test',
)

connected = conn.is_connected()
print(connected)
if(not connected):
    conn.ping(True)

cur = conn.cursor()

cur.execute('SELECT * FROM member')

# table = cur.fetchall()
# print(table)

print(cur.statement)
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())

参考

今回出てきた API のドキュメントを載せておきます。

MySQL :: MySQL Connector/Python Developer Guide :: 7.1 Connector/Python Connection Arguments

MySQL :: MySQL Connector/Python Developer Guide :: 10.2.25 MySQLConnection.is_connected() Method

MySQL :: MySQL Connector/Python Developer Guide :: 10.2.27 MySQLConnection.ping() Method

googletest を使ってみた

f:id:pickles-ochazuke:20170828184058j:plain

北海道の土産用お菓子は、どれもおいしくて最高です。

趣味でコードを書いてるけど、テストコードは全く書いてこず、仕事でテストコードを初めて書いて、良いテストコードを書くのは難しいなぁと実感し、ちょっとこれは練習しないとダメだなということで、テストコードを書く環境を作り始めました。

googletest を見つけたので、これでいいかと選択。使う理由は、情報が多そうなのと使いやすそうな感じが理由。

Windows 版の Visual Studio 2017 上で動かしたので、そのまとめを書いておきます。

googletest を落とす

まずは、googletest を落としてこないと始まりません。

github.com

落としたら解凍します。

Google Test を追加

google-master/googletest/msvc/2010/ の中に、gtest.sln というソリューションファイルがあるので、Visual Studio で開きます。 開くと、セキュリティ警告をしてくると思うので、内容を読んで、問題なければ OK を押します。そのあとプロジェクトの再ターゲットが出るので、設定に問題なければ OK を押します。

f:id:pickles-ochazuke:20170828180315p:plain

完了したら gtest.sln を閉じます。次にテスト対象のプロジェクトを用意します。今回は、TestProject というプロジェクトを作成しました。次に、ソリューションに googletest のプロジェクトを追加します。 ソリューションエクスプローラからソリューションを右クリックし、追加 > 既存のプロジェクト を選び、

f:id:pickles-ochazuke:20170828180445p:plain

先ほど解凍した googletest/msvc/2010/gtest.vcxproj を追加します。追加すると以下のようになります。

f:id:pickles-ochazuke:20170828180553p:plain

環境設定

TestProject には、何もコードが書かれていないので追加します。ここでは、main.cpp を追加しました。追加したら、まずは googletest を使えるようにしましょう。TestProjectプロパティを開き、構成プロパティ > C/C++ > 全般 のなかの追加のインクルードディレクトリに新しいパスを追加します。

f:id:pickles-ochazuke:20170828180835p:plain

そこに googletest/include を追加します。もちろん、googletest より前は、googletest を置いている場所のパスが入ります。追加したら OK をクリックし、設定を反映させます。次に、TestProject の参照を追加します。参照を右クリックすると参照の追加が出るので、それをクリックし、gtest にチェックを入れます。

f:id:pickles-ochazuke:20170828180933j:plain

最後に、gtest プロジェクトのプロパティを表示し、構成プロパティ > C/C++ > コード生成 からランタイムライブラリを環境に合わせて設定します。よく分からない場合は、テスト対象のプロジェクトと同じにすれば問題ないはずです。ここでは、構成Debug に変更し、ランタイムライブラリマルチスレッドデバッグDLL(/MDd) にしました。OK をクリックして、プロパティを閉じます。

f:id:pickles-ochazuke:20170828181004p:plain

テストコードの作成

最後にテストコードを作成します。今回は、main.cpp に直接テストコードを作成します。

#include "gtest\gtest.h"

TEST()
{
    EXPECT_EQ(0, 1 - 1);
}

int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

main 関数内の ::testing::InitGoogleTest(&argc, argv)RUN_ALL_TEST() がテスト実行のために必要な処理です。TEST(){} 内の処理が実際にテストしたい内容になります。

これを実行すると以下のように表示されます。

f:id:pickles-ochazuke:20170828181137p:plain

以上で基本的な設定と使い方は、完了です。 詳しく知りたい方は、

入門ガイド — Google Test ドキュメント日本語訳

あたりを参照するといいでしょう。

おまけ

googletest は、include ディレクトリと msvc ディレクトリ、src ディレクトリが同じ階層にあれば問題なさそうなので、この三つをコピーして、ソリューションごとに置くと良さそうです。

おわり

以上が googletest の使い方でした。Visual Studio に googletest を入れていると Visual Studio さんに Test Adapter for Google Test をお勧めされたので、気が向いたら試してみようと思います。Community でも使えるんですかね……?

marketplace.visualstudio.com

JSON for Modern C++ を使ってみたメモ

メモ書き

github.com

JSON for Modern C++ とは?

以下、参照。

mattn.kaoriya.net

使い方

Github のページの json/src/json.hpp というファイルがあるので、これを include する。

#include "json.hpp"
using json = nlohmann::json;

この json クラスを使って、JSON ファイルを読んだり書いたりする。

以下のような JSON ファイルを使って、例を示していく。 ファイル名は、test.json として進めていく。

{
  "pi": 3.141,
  "happy": true,
  "name": "Niels",
  "nothing": null,
  "answer": {
    "everything": 42
  },
  "list": [1, 0, 2],
  "object": {
    "currency": "USD",
    "value": 42.99
  }
}

JSON ファイルの読み込み

JSON ファイルを読む必要があるので、まずはファイルを読む。

std::ifstream reading("test.json", std::ios::in);

ファイルの入出力は以下を参照。

qiita.com

ファイルを読んだら以下のようにして取り出す。

json j;
reading >> j;

これだけで、ファイルの中身がすべて json j に入っている。 試しに以下のように出力すると

reading >> j;
std::cout << j << std::endl;

以下のように出力される。

{"answer":{"everything":42},"happy":true,"list":[1,0,2],"name":"Niels","nothing":null,"object":{"currency":"USD","value":42.99},"pi":3.141}

改行はしてくれないっぽい。

このデータだけ取り出したいってときは、以下のようにする。

std::cout << j["pi"] << std::endl;
// 出力結果は、3.141

begin(), end() を使って取り出すことができる。

std::for_each(j.begin(), j.end(), [](auto it) {
    std::cout << it << std::endl;
});

結果は、以下のようなる。

{"everything":42}
true
[1,0,2]
"Niels"
null
{"currency":"USD","value":42.99}
3.141

begin, end を使うとアルファベット順になる。

C++17 だと以下のように書ける。

for (auto& elem : j) {
    std::cout << elem << std::endl;
}

JSON ファイルへ書き込み

未調査…

おわり

とても使いやすい。まだまだ調べ切れていないので、分かり次第更新していく予定。

簡単に Pimpl を使ってみる

名前は知っていたけど、使ったことがなかったので、試しに使ってみた。

Pimpl とは

にきび(pimple)ではありません。Pointer to Implementation (実装へのポインタ)を略して、Pimpl です。
このテクニックによって、プライベートな詳細を開示せずに済みます。つまりインタフェースと実装を切り離す方法です。

これは、実際にコードを見たほうが早いと思うので、先にコードを出します。

実装

とりあえず以下のようなコードを書いてみました。これは、まだ Pimpl を使っていません。

// Widget.h
#pragma once
#include <stdint.h>

class Widget
{
public:
    Widget();
    ~Widget();

    int32_t getNum();
private:
    int32_t num_ = 32;
};

// Widget.cpp
#include "Widget.h"
#include <iostream>

Widget::Widget()
{
    std::cout << "Constructor" << std::endl;
}

Widget::~Widget()
{
    std::cout << "Destructor" << std::endl;
}

int32_t Widget::getNum()
{
    return num_;
}

Widget::getNum() を呼ぶことで、内部の num のデータを取れるクラスです。このクラスを使う側(ユーザ)は、データの取り方が分かればいいので、変数 num があることを知らせる必要はありません。なので、これを実装ファイル(Widget.cpp)に入れてやります。

// Widget.h
#pragma once
#include <stdint.h>

class Widget
{
public:
    Widget();
    ~Widget();

    int32_t getNum();
private:
    class Impl;
    Impl* impl_;
};

// Widget.cpp
#include "Widget.h"
#include <iostream>

class Widget::Impl
{
public:
    Impl() { std::cout << "Impl Constructor" << std::endl; }
    ~Impl() { std::cout << "Impl Destructor" << std::endl; }

    int32_t num = 32;
};

Widget::Widget()
    : impl_(new Impl())
{
    std::cout << "Constructor" << std::endl;
}


Widget::~Widget()
{
    std::cout << "Destructor" << std::endl;
    delete impl_;
    impl_ = nullptr;
}

int32_t Widget::getNum()
{
    return impl_->num;
}

まずヘッダから見ていきます。private にメンバ変数 num がありましたが、それが消えて、代わりに クラス Impl の前方宣言とそのクラス Impl のポインタを追加しました。

次に実装側ですが、初めに クラス Imple の定義を行っています。その中にさっきは、Widget.h にあったメンバ変数が Widget::Impl に移動しています。

Widget のコンストラクタの初期化子リストに impl_ が追加され、デストラクタでは、impl_ の開放を行っています。最後に、Widget::getNum() は、impl_のメンバ変数 num をアクセスして、値を返すように変更されています。

このように、詳細を実装ファイル内に隠してしまうのが、Pimpl です。

Pimpl を実装するときの注意

Pimpl を書くときに、いくつか注意することがあります(C++ に慣れている方は大したことではないですが)。

まず、Pimpl となる変数(今回だと impl_)の型は、ポインタ(Impl*)でないといけません。当たり前ですが、ヘッダ側は、Impl の実装内容を知らないので、Impl のオブジェクトを持てません。

Impl の詳細を書くときは、必ず Impl を利用する処理よりも先に書くようにします。今回だと Widget::Widget() よりも先に書く必要があります。これも Impl の詳細を知らないと使えないためです。

最後に、デストラクタでは必ず Impl の変数を開放するようにします。ポインタなので、当然ですね。

Pimpl をスマートポインタにする

Pimpl をスマートポインタに変更することで、デストラクタ内で Pimpl を開放処理を書かなくて済みます。例えば以下のようにします。

std::unique_ptr<Impl> impl_;

デメリット

Pimpl を使うことでいくつかデメリットがあります。

  • コンストラクタでオブジェクトの割当、デストラクタでオブジェクトを破棄する必要がある
  • すべてのプライベートメンバは、Pimpl を通じてアクセスしないといけない
  • Pimpl をプライベートにすると、Pimpl を持つクラス(Widget)しか Pimpl のメンバにアクセスできない
  • プライベート仮想メソッドを作れない。オーバライドする場合、パブリックにして、派生クラスがオーバライドできるようにしないといけない
  • Pimpl(impl_) から、それを持つクラス(Widget)のパブリックなメンバ変数にアクセスするには、Pimpl にそのクラスへのポインタを追加するか、そのメソッドにクラスを渡す必要がある
  • const メソッド内で Pimpl(impl_)のメンバ変数を変更してもコンパイラは検知することができない

いくつかデメリットはありますが、実装する側のデメリットであり、利用者に対してのデメリットはないので、使わない理由にはならないでしょう。

メリット

次は、メリットです。

  • ユーザ側が見ることになるヘッダファイルの内容がすっきりする。
  • 実装の詳細をユーザ側に見られることがなくなり、不正な方法でアクセスすることもできなくなる。
  • ヘッダのインクルードファイルが減るので、結合度が減る。
  • コンパイル時間が高速化される
  • バイナリ互換性が向上する
  • Pimpl は要求時に作ることもできるので、ネットワーク接続など制限のあるリソースやコストの高いリソースの割当のときに役に立つ。

などなどあります。詳細は、参考にした本を見てください(最後に載せています)

コピーセマンティクス

Pimpl を持つクラスをコピーした場合、当然 Pimpl もコピーされる。この状態で、コピー元か、コピー先のオブジェクトを開放したとき、もう片方のオブジェクトが Pimpl にアクセスすると Pimpl はすでに開放されているため、エラーが発生します。

これを防ぐためには、3つの方法があります。

  • コピーコンストラクタ、代入演算子delete(暗黙定義の禁止) する
  • コピーコンストラクタ、代入演算子を明示的に定義する
  • スマートポインタを利用する

個人的には、スマートポインタが楽だと思います。コピーを禁止したい場合は、std::unique_ptr, コピーを許可するなら std::shared_ptr でいいと思います。 スマートポインタを使えば、コピーセマンティクスを書く必要もなくなります。

おわり

Pimpl は、有効に使えると強力な反面、注意する点が多い方法だと感じました。デメリットは小さいので、積極的に使っていきたいですが、プライベートをなんでもかんでも Pimpl に実装するのも違うと思うので、よく考えて使いたいと思います。

Pimpl の書き方をメインに書いたので、どう活用するかは分かりにくかったかもしれません。そういった内容は、参考にした本に載っているのでそちらを参照するといいでしょう。

今回のコードは、以下に上げています。

github.com

参考にした資料です。

C++のためのAPIデザイン

C++のためのAPIデザイン