お茶漬けびより

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

Python のパッケージ管理ツールの簡単なまとめ

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

最近寒すぎて歩くのがつらく、走って帰っています(これはこれでつらい)。
Python を始めてまだ1ヶ月か2ヶ月ほどですが、始めたときに???ってなったことをちまちまと整理していきます。

今回は、パッケージ管理ツールの回りをまとめたいと思います。
先に断っておくと、詳しい話は他の詳しい方に任せてます。自分と同じように Python 始めたけど専門用語が飛び交いすぎて訳が分からない!となっている人に向けて、道しるべになるような簡単な解説にとどめたいと思っています。

パッケージングツールとパッケージインストーラ

パッケージ管理ツールを大きく分けるとパッケージングツールとパッケージインストーラの二つになります。

  • パッケージングツール
    自作したライブラリを外部へ配布する際に使用するツール。PyPI で配布されているパッケージは、全てパッケージングツールを使ってパッケージ化されたものです。ツールは複数あり、 wheel, setup.py sdist(コマンド), setuptools, distutils, distribute などがこちらに当たります。現在は、wheel が普及しています。

  • パッケージインストーラ
    PyPI などで公開されているパッケージをインストールするためのツール。一度は使ったことがあるかもしれません。pip, easy_install などがこちらに当たります。現在は pip が普及しています。easy_install は、setuptools のコマンドラインツールだそうです。

パッケージングツールは複数あります。現在使われているのは、 wheel ですが、そこまで行き着くのにややこしい問題があったようです。特にややこしいのは、setuptools と distribute です。setuptools が登場し(2011年?)、そのフォーク(派生のようなもの)が distribute です(2012年?)。一時期は distribute を使うべきでしたが、2013年に setuptools にマージされます(がっちゃんこ)。

Python 3.3 までは pip がスタンダードで入っていなかったので自分で入れる必要がありました。そのため、setuptools (easy_install)を使って pip を入れていた時期があったようです。3.4 では pip は標準で入っていますので、setuptools(easy_install) を触る必要はありません。

distutils は、標準のパッケージ管理ツール(パッケージングもインストールも可能)です。setuptools や pip はこれを拡張したものだそうです。

さて、整理しましょう。最新の Python(3.4 以上)であれば、インストールするものは wheel だけです。そしてパッケージをインストールする際は、pip を使いましょう。以上です。

パッケージの配布形式

wheelegg です。
昔は egg が使われていましたが、この egg にはいろいろ使いにくいところがありました。それを解決するために現れたのが wheel です。現在では、wheel が一般的に使われています。wheel 形式でパッケージするには、同じ名前のパッケージングツール wheel を使います。

異端児 Anaconda

(実際に使ったことがありませんので、間違いがあるかも……)
さて、さっきまで pip と wheel だけを使えば問題ないよと書いてきました。しかし、この Anaconda というやつには conda という便利なツールが存在します。こいつは、パッケージの管理と 仮想環境管理とバージョン管理が出来るようです。つまり pip, pyenv, virualenv の代わりを一人で担っています。また、パッケージングも行えます。ただし、wheel との互換性はないようです。研究で使う分には問題ないですが、開発となると今後扱いづらいかもしれません。

おわりに

パッケージ管理ツールをまとめると、通常は pip, wheel を使えばいいですが、考えるのが面倒くさい、さっさとコードを書く必要があるという人は、Anaconda をインストールして conda を使うという感じでしょうか。または、Anaconda でも pip は使えるので、パッケージ管理やパッケージングは、pip, wheel を使う(それ以外の環境回りは conda を使う)というのがいいかもしれません。

以上、つたない内容でしたが理解の一助になれば幸いです。

参考資料

歴史的な詳しい解説があります。とても参考になりました。 ymotongpoo.hatenablog.com

Anaconda(conda)の使い方が解説されています。 qiita.com

ちょっと情報が古いですが、よくまとまっているので、かなり参考になります。
Python パッケージ管理技術まとめ (pip, setuptools, easy_install, etc)

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 ファイルへ書き込み

未調査…

おわり

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