CUBE SUGAR CONTAINER

技術系のこと書きます。

SQL: 内部的なコードを人間に分かりやすいラベルに変換して表示する

RDB のスキーマには、たまに対応表などを参照しながらでないと分からないような内部的なコードが使われていることがある。 大抵はアプリケーションの中で変換して表示するだろうけど、これを直接 SELECT とかで確認しようとすると分かりにくい。 今回は、それを見やすくするためのテクニックについて。

使った環境は次の通り。

$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.12.5
BuildVersion:   16F73
$ mysqld --version
mysqld  Ver 5.7.18 for osx10.12 on x86_64 (Homebrew)
$ mysql --version
mysql  Ver 14.14 Distrib 5.7.18, for osx10.12 (x86_64) using  EditLine wrapper

まずは MySQL のインタラクティブシェルに入る。

$ mysql -u root

そしてサンプル用のテーブルを用意しておく。 内部的なコードに対応するのは users.position_code だ。

mysql> DROP TABLE IF EXISTS users;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> CREATE TABLE users (
    ->   id integer,
    ->   name varchar(255),
    ->   age integer,
    ->   position_code integer
    -> );
Query OK, 0 rows affected (0.04 sec)

そして、いくつかレコードを追加しておこう。

mysql> INSERT INTO users
    -> VALUES
    ->   (1, 'Alice', 20, 1),
    ->   (2, 'Bob', 25, 2),
    ->   (3, 'Carol', 30, 3);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

こんな感じでデータが入る。 この状態では position_code に一体どんな意味があるのか分からない。

mysql> SELECT *
    -> FROM users;
+------+-------+------+---------------+
| id   | name  | age  | position_code |
+------+-------+------+---------------+
|    1 | Alice |   20 |             1 |
|    2 | Bob   |   25 |             2 |
|    3 | Carol |   30 |             3 |
+------+-------+------+---------------+
3 rows in set (0.00 sec)

そこで CASE ~ END を用いて分かりやすいラベルに変換する。 条件を WHEN で指定したら、対応する値を THEN で返せば良い。 どれにも該当しない場合には ELSE を使う。

mysql> SELECT
    ->   id,
    ->   name,
    ->   CASE
    ->     WHEN position_code = 1 THEN '一般職員'
    ->     WHEN position_code = 2 THEN '主任'
    ->     WHEN position_code = 3 THEN '課長'
    ->     ELSE '不明'
    ->   END as position
    -> FROM users;
+------+-------+--------------+
| id   | name  | position     |
+------+-------+--------------+
|    1 | Alice | 一般職員     |
|    2 | Bob   | 主任         |
|    3 | Carol | 課長         |
+------+-------+--------------+
3 rows in set (0.00 sec)

これで分かりやすくなった。

もちろんこのテクニックはコードの変換だけでなく特定の条件に一致するか、みたいな確認にも使える。 次の例は年齢が 30 を超えているかを misoji カラムに表示している。

mysql> SELECT
    ->   id,
    ->   name,
    ->   CASE
    ->     WHEN age >= 30 THEN 'YES'
    ->     ELSE 'NO'
    ->   END as misoji
    -> FROM users;
+------+-------+--------+
| id   | name  | misoji |
+------+-------+--------+
|    1 | Alice | NO     |
|    2 | Bob   | NO     |
|    3 | Carol | YES    |
+------+-------+--------+
3 rows in set (0.00 sec)

めでたしめでたし。

ビッグデータ分析・活用のためのSQLレシピ

ビッグデータ分析・活用のためのSQLレシピ

  • 作者: 加嵜長門,田宮直人,丸山弘詩
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2017/03/27
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

ファイルから SQL を読み込む (MySQL, PostgreSQL, SQLite3)

色々な RDBMS で SQL の書かれたファイルを読み込む方法について調べた。

まずはSQL の書かれたテキストファイルを用意しておく。

$ cat << 'EOF' > sample.sql
DROP TABLE IF EXISTS users;
CREATE TABLE users (
  id integer,
  name varchar(255),
  age integer
);
INSERT INTO users
VALUES
  (1, 'Alice', 20),
  (2, 'Bob', 30),
  (3, 'Carol', 40);
EOF

こんな感じ。 users テーブルを作って、そこにレコードを追加している。

$ cat sample.sql
DROP TABLE IF EXISTS users;
CREATE TABLE users (
  id integer,
  name varchar(255),
  age integer
);
INSERT INTO users
VALUES
  (1, 'Alice', 20),
  (2, 'Bob', 30),
  (3, 'Carol', 40);

TL;DR

早見表

RDBMS コマンドライン インタラクティブシェル
MySQL mysql -D [database] < [filepath] source [filepath]
PostgreSQL psql -d [database] -f [filepath] \i [filepath]
SQLite3 sqlite3 [database] < [filepath] .read [filepath]

MySQL

まずは MySQL から。 使ったバージョンは次の通り。

$ mysqld --version
mysqld  Ver 5.7.18 for osx10.12 on x86_64 (Homebrew)

最初に、読み込む先となる sample データベースを用意しておく。

$ mysql -u root -e "CREATE DATABASE IF NOT EXISTS sample"

コマンドラインから読み込む

まずは通常のシェルから mysql コマンドを使って読み込む方法について。

mysql コマンドを使って読みこむにはリダイレクトを使うだけで良い。

$ mysql -u root -D sample < sample.sql

これだけでテーブルができてレコードが追加されている。

$ mysql -u root -D sample -e "SELECT * FROM users"
+------+-------+------+
| id   | name  | age  |
+------+-------+------+
|    1 | Alice |   20 |
|    2 | Bob   |   30 |
|    3 | Carol |   40 |
+------+-------+------+

シェルから読み込む

次に mysql コマンドのインタラクティブシェルに落ちて読み込む方法について。

$ mysql -u root -D sample

ひとまず、先ほど読み込んだ内容は一旦削除しておく。

mysql> DROP TABLE users;

mysql コマンドのシェルから SQL の書かれたファイルを読み込むには source コマンドを使う。

mysql> source sample.sql

これでファイルに書かれた SQL が読み込まれる。

mysql> SELECT * FROM users;
+------+-------+------+
| id   | name  | age  |
+------+-------+------+
|    1 | Alice |   20 |
|    2 | Bob   |   30 |
|    3 | Carol |   40 |
+------+-------+------+
3 rows in set (0.00 sec)

あるいは上記の代わりに \. を使っても構わない。

mysql> \. sample.sql

PostgreSQL

次に PostgreSQL の場合。 使ったバージョンは次の通り。

$ psql --version
psql (PostgreSQL) 9.6.3

まずは読み込む先のデータベースを用意しておく。

$ psql -c "DROP DATABASE sample"
$ psql -c "CREATE DATABASE sample"

コマンドラインから読み込む

psql コマンドで読み込む場合には、読ませたいファイルを -f オプションで指定する。

$ psql -d sample -f sample.sql

これで内容が読み込まれた。

$ psql -d sample -c "SELECT * FROM users"
 id | name  | age
----+-------+-----
  1 | Alice |  20
  2 | Bob   |  30
  3 | Carol |  40
(3 rows)

シェルから読み込む

次は psql コマンドのシェルに入って読み込む方法について。

まずはデータベースを指定してインタラクティブシェルに落ちる。

$ psql -d sample

先ほど作ったテーブルは一旦削除しておこう。

sample=# DROP TABLE users;

SQL の書かれたファイルを読み込むには \i を使う。

sample=# \i sample.sql

これで内容が読み込まれた。

sample=# SELECT * FROM users;
 id | name  | age
----+-------+-----
  1 | Alice |  20
  2 | Bob   |  30
  3 | Carol |  40
(3 rows)

SQLite3

次は SQLite3 の場合。 使ったバージョンは次の通り。

$ sqlite3 --version
3.16.0 2016-11-04 19:09:39 0e5ffd9123d6d2d2b8f3701e8a73cc98a3a7ff5f

コマンドラインから読み込む

SQLite3 でコマンドラインから読み込むにはデータベースのファイルを指定しながら MySQL と同様にリダイレクトを使うだけで良い。

$ sqlite3 sample.db < sample.sql

これで読み込まれた。

$ sqlite3 sample.db "SELECT * FROM users"
1|Alice|20
2|Bob|30
3|Carol|40

シェルから読み込む

次にインタラクティブシェルから読み込む方法について。 まずはデータベースのファイルを指定して sqlite3 コマンドを実行することでシェルに落ちる。

$ sqlite3 sample.db

先ほど作ったテーブルは一旦削除しておこう。

sqlite> DROP TABLE users;

SQLite3 のシェルでファイルを読み込むには .read を使う。

sqlite> .read sample.sql

これで内容が読み込まれた。

sqlite> SELECT * FROM users;
1|Alice|20
2|Bob|30
3|Carol|40

いじょう。

Apache Spark を完全分散モードの YARN クラスタで動かす

Apache Spark を使って複数ノードで分散並列処理をする場合、まずは動作させるためのクラスタマネージャを選ぶことになる。 Apache Spark では以下のクラスタマネージャに対応している。

  • Apache Spark 組み込み (これはスタンドアロンモードと呼ばれる)
  • Apache Hadoop YARN
  • Apache Mesos

今回は、その中で二番目の Apache Hadoop の提供する YARN を使ってみる。 また、なるべく実環境に近いものを作りたいので Apache Hadoop は完全分散モードを使うことにした。 そのため、まず前提として次のエントリを元に Hadoop クラスタが組まれていることが前提となる。

blog.amedama.jp

Apache Hadoop を設定する

Apache Spark のクラスタマネージャに YARN を使うときのポイントは次の環境変数が設定されていること。

$ echo $HADOOP_HOME
/home/vagrant/hadoop-2.8.0
$ echo $HADOOP_CONF_DIR
/home/vagrant/hadoop-2.8.0/etc/hadoop

また、上記の手順で構築した場合、メモリが少ないと Apache Spark を動作させたときエラーになってしまう。 これは、実行前のチェックによってマシンに積まれているメモリを一定の割合以上使うことができないため。 そこで、次のようにして設定ファイルを編集することで、そのチェックを無効にしてやる。

$ cat << 'EOF' > /tmp/yarn-site.xml.property
  <property>
    <name>yarn.nodemanager.pmem-check-enabled</name>
    <value>false</value>
  </property>
  <property>
    <name>yarn.nodemanager.vmem-check-enabled</name>
    <value>false</value>
  </property>
EOF
$ sed -i -e '
  /^<configuration>$/r /tmp/yarn-site.xml.property
  /^$/d
' $HADOOP_HOME/etc/hadoop/yarn-site.xml

設定ファイルを編集したので、それを各ノードに配布する。 そして NodeManager のプロセスも設定を読み直すために再起動が必要になるのでプロセスを止める。

$ for node in node1 node2
do
  scp $HADOOP_HOME/etc/hadoop/yarn-site.xml $node:$HADOOP_HOME/etc/hadoop/
  ssh $node 'pkill -f NodeManager'
done

そして、改めて各ノードの NodeManager を起動してやる。

$ $HADOOP_HOME/sbin/start-yarn.sh
starting yarn daemons
resourcemanager running as process 19527. Stop it first.
node1: starting nodemanager, logging to /home/vagrant/hadoop-2.8.0/logs/yarn-vagrant-nodemanager-node1.out
node2: starting nodemanager, logging to /home/vagrant/hadoop-2.8.0/logs/yarn-vagrant-nodemanager-node2.out

あとはユーザのホームディレクトリを作成しておこう。

$ $HADOOP_HOME/bin/hdfs dfs -mkdir -p .

これで Hadoop クラスタ側の準備はできた。

Apache Spark を設定する

次に Apache Spark をインストールした上で設定する。

まずは公式サイトからバイナリをダウンロードして解凍する。

$ wget https://d3kbcqa49mib13.cloudfront.net/spark-2.1.1-bin-hadoop2.7.tgz
$ tar xf spark-2.1.1-bin-hadoop2.7.tgz

上記のディレクトリを環境変数 SPARK_HOME に設定しておこう。

$ cat << 'EOF' >> ~/.bashrc
export SPARK_HOME=~/spark-2.1.1-bin-hadoop2.7
EOF
$ source ~/.bashrc

これだけで Apache Spark を使う準備は整った。

サンプルコードを動かしてみる

準備ができたので、試しに Apache Spark に同梱されているサンプルコードを動かしてみよう。 例えば、次のようにすると円周率を計算するプログラムを起動できる。

$ $SPARK_HOME/bin/spark-submit \
  --class org.apache.spark.examples.SparkPi \
  --master yarn \
  $SPARK_HOME/examples/jars/spark-examples_*.jar \
  1000
...(snip)...
Pi is roughly 3.1417053514170536
...(snip)

エラーにならず円周率は大体 3.1417… ということが計算できた。

PySpark を使ってみる

次は Apache Spark を PySpark と呼ばれる Python で使えるインタラクティブシェルから使ってみる。 そして、分散並列処理のハローワールドとも言えるワードカウントを書いてみることにしよう。

ワードカウントの対象としては Apache Spark の README ファイルを使うことにした。 次のようにして、まずは HDFS にファイルをコピーする。

$ $HADOOP_HOME/bin/hdfs dfs -put $SPARK_HOME/README.md .

分散並列処理をするには、対象のファイルが全てのノードから参照できる状態にないといけない。 今回は HDFS にコピーしたけど Amazon S3 とか対応しているファイルシステムは色々とある。

pyspark コマンドを使って PySpark のインタラクティブシェルを起動しよう。 ここでポイントとなるのは --master オプションで yarn を指定すること。 これでローカルではなく YARN を使った分散実行がされるようになる。

$ $SPARK_HOME/bin/pyspark --master yarn
...(snip)...
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 2.1.1
      /_/

Using Python version 2.7.5 (default, Nov  6 2016 00:28:07)
SparkSession available as 'spark'.
>>>

HDFS に先ほどコピーしたファイルを読み込もう。 ファイルの読み込みは SparkContext#textFile() を使う。

>>> textfile = sc.textFile('hdfs://master:9000/user/vagrant/README.md')

これで HDFS のファイルのデータが各ノードに読み込まれた。

>>> textfile.first()
u'# Apache Spark'

まずはテキストを RDD#flatMap() を使ってスペースで分割する。 こうすると map した結果から得られたリストを展開してくれる。

>>> words = textfile.flatMap(lambda line: line.split(' '))

すると、こんな風になる。

>>> words.take(5)
[u'#', u'Apache', u'Spark', u'', u'Spark']

上記には空白のように意味のない単語も含まれるので、それを RDD#filter() で取り除く。

>>> valid_words = words.filter(lambda word: word)
>>> valid_words.take(5)
[u'#', u'Apache', u'Spark', u'Spark', u'is']

次に、各単語を RDD#map() を使ってキー・バリュー形式にする。 キーは単語でバリューは登場回数になる。

>>> keyvalues = valid_words.map(lambda word: (word, 1))
>>> keyvalues.first()
(u'#', 1)

最後に、各キーごとに RDD#reduceByKey() を使ってバリューを集計する。 これで各単語の出現回数が集計できる。

>>> word_count = keyvalues.reduceByKey(lambda a, b: a + b)
>>> word_count.take(3)
[(u'storage', 1), (u'"local"', 1), (u'including', 4)]

あとは集計結果を HDFS に書き出そう。

>>> word_count.saveAsTextFile('hdfs://master:9000/user/vagrant/output')

使い終わったらインタラクティブシェルから抜ける。

>>> exit()

HDFS に書き出された内容を確認してみよう。 どうやら、ちゃんと単語ごとの出現回数がカウントできているようだ。

$ $HADOOP_HOME/bin/hdfs dfs -cat output/*
(u'storage', 1)
(u'"local"', 1)
(u'including', 4)
...(snip)...
(u'<class>', 1)
(u'learning,', 1)
(u'latest', 1)

PySpark で Python スクリプトを実行する

先ほどはインタラクティブシェルを使って PySpark を使ったけど、もちろんスクリプトファイルから実行することもできる。

一旦、先ほど書き出した HDFS のディレクトリは削除しておこう。

$ $HADOOP_HOME/bin/hdfs dfs -rm -r -f output

次のスクリプトは、先ほどのワードカウントをスクリプトファイルにしたもの。 ポイントとしては、インタラクティブシェルでは最初からインスタンス化されていた SparkContext を自分で用意しなきゃいけないところ。 インタラクティブシェルではオプションで実行するモードを YARN にしていた代わりに、この場合は SparkConf で設定する必要がある。 とはいえ、他の部分は特に変わらない。

$ cat << 'EOF' > /var/tmp/wc.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pyspark import SparkConf
from pyspark import SparkContext


def main():
    conf = SparkConf()
    conf.setMaster('yarn')
    conf.setAppName('WordCount')

    sc = SparkContext(conf=conf)

    textfile = sc.textFile('hdfs://master:9000/user/vagrant/README.md')

    words = textfile.flatMap(lambda line: line.split(' '))
    valid_words = words.filter(lambda word: word)
    keyvalues = valid_words.map(lambda word: (word, 1))
    word_count = keyvalues.reduceByKey(lambda a, b: a + b)

    word_count.saveAsTextFile('hdfs://master:9000/user/vagrant/output')


if __name__ == '__main__':
    main()
EOF

実行する

スクリプトファイルを実行するときは spark-submit コマンドを使う。 このとき --py-files オプションを使って実行に必要なスクリプトファイルを、あらかじめノードに配布しておく。

$ $SPARK_HOME/bin/spark-submit \
  --master yarn \
  --py-files /var/tmp/wc.py \
  /var/tmp/wc.py

上記の実行が上手くいったら結果を出力したディレクトリを確認してみよう。

$ $HADOOP_HOME/bin/hdfs dfs -cat output/*
(u'storage', 1)
(u'"local"', 1)
(u'including', 4)
...(snip)...
(u'<class>', 1)
(u'learning,', 1)
(u'latest', 1)

実行したアプリケーションの確認

ちなみに Apache Spark が YARN 経由で実行したアプリケーションの情報は Hadoop の管理画面から状態を確認できる。

http://192.168.33.10:8088/

上記クラスタの構築を自動化する

ちなみに上記を全て手動で構築するのは手間がかかるので Vagrantfile を書いてみた。 次のようにして実行する。 メモリを 2GB 積んだ仮想マシンを 3 台起動するので、最低でも 8GB できれば 16GB のメモリを積んだ物理マシンで実行してもらいたい。

$ git clone https://gist.github.com/fefb9831e9f032ef264d8d517df57cb4.git spark-on-yarn
$ cd spark-on-yarn
$ sh boot.sh

vagrant ssh コマンドでマスターサーバにログインできる。

$ vagrant ssh

いじょう。

参考

Apache Spark については次の本を読んで勉強してみた。

初めてのSpark

初めてのSpark

  • 作者: Holden Karau,Andy Konwinski,Patrick Wendell,Matei Zaharia,Sky株式会社玉川竜司
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2015/08/22
  • メディア: 大型本
  • この商品を含むブログ (4件) を見る

Python: pep8 は pycodestyle になったし pep257 は pydocstyle になった

意外とまだあんまり知られていないような気がしたので、このブログにも書いておく。

PEP8 と pep8 と pycodestyle

Python には PEP8 という有名なコーディングスタイルガイドラインがある。

www.python.org

そして、そのコーディングスタイルに沿ったコードになっているのかをチェックするツールとして pep8 というパッケージがあった。

pypi.python.org

過去形にするのは半分正しくなくて、上記のように今もある。 ただ、これは後方互換のために残されているだけで、もうバージョンアップはされないだろう。

今後は代わりに pycodestyle というパッケージを使うことになる。

pypi.python.org

これは単にパッケージとコマンドの名前が変わっただけ。 とはいえ、こちらはバージョンアップが続くので最新の PEP8 に追従していくしチェックできる範囲も増えていくはず。 (PEP に書かれている内容は必要に応じて更新される)

試しに使ってみることにしよう。 まずは Python のパッケージマネージャである pip でインストールする。

$ pip install pycodestyle

サンプルコードとして PEP8 違反がいくつか含まれるものを用意した。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 改行が足りない
def greeting(name = 'World'):  # 余分なスペースがある
    print('Hello, {name}'.format(name=name))


def main():
  greeting()  # インデントが 4 スペースでない


if __name__ == '__main__':
    main()

# 改行が多い

pycodestyle コマンドで上記のコードをチェックしてみる。

$ pycodestyle sample.py
sample.py:5:1: E302 expected 2 blank lines, found 1
sample.py:5:18: E251 unexpected spaces around keyword / parameter equals
sample.py:5:20: E251 unexpected spaces around keyword / parameter equals
sample.py:10:3: E111 indentation is not a multiple of four
sample.py:17:1: W391 blank line at end of file

色々と PEP8 に準拠していない箇所が見つかった。

PEP257 と pep257 と pydocstyle

同じことが pep257pydocstyle にも起きている。

PEP257 は docstring のフォーマットを規定したドキュメントの名前を指している。

www.python.org

そして、PEP8 と同じように pep257 というチェックツールがあった。

pypi.python.org

そして、同じように pydocstyle という名前に変更されている。

pypi.python.org

こちらも試しに使ってみることにしよう。 pip でさくっと入る。

$ pip install pydocstyle

そして PEP257 に違反している箇所を含むサンプルコードを用意した。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# そもそも docstring がない


def greeting(name='World'):
    '''ダブルクォートを使っていない'''
    print('Hello, {name}'.format(name=name))


def main():
    """
        改行が多い
    """
    greeting()


if __name__ == '__main__':
    main()

pydocstyle コマンドで上記のコードをチェックしてみよう。

$ pydocstyle sample.py
sample.py:1 at module level:
        D100: Missing docstring in public module
sample.py:7 in public function `greeting`:
        D300: Use """triple double quotes""" (found '''-quotes)
sample.py:7 in public function `greeting`:
        D400: First line should end with a period (not '')
sample.py:12 in public function `main`:
        D200: One-line docstring should fit on one line with quotes (found 3)
sample.py:12 in public function `main`:
        D208: Docstring is over-indented
sample.py:12 in public function `main`:
        D400: First line should end with a period (not '')

PEP257 に違反している箇所が見つかった。

周辺ツールの対応

リネームされたパッケージを依存パッケージに持つ主要なパッケージはどうなっているかなー、というのも一応は確認しておく。 例えば flake8 とか autopep8 について pipdeptree で見てみよう。

$ pip install flake8 autopep8 pipdeptree

flake8pycodestyle を使うようになっている。

$ pipdeptree | grep -A 3 flake8
flake8==3.3.0
  - mccabe [required: <0.7.0,>=0.6.0, installed: 0.6.1]
  - pycodestyle [required: >=2.0.0,<2.4.0, installed: 2.3.1]
  - pyflakes [required: >=1.5.0,<1.6.0, installed: 1.5.0]

autopep8pycodestyle を使うようになっていた。

$ pipdeptree | grep -A 1 autopep8
autopep8==1.3.2
  - pycodestyle [required: >=2.3, installed: 2.3.1]

間接的に使っている場合には特に対応する必要はなさそうだ。

まとめ

  • pep8pycodestyle に名前が変わった
  • pep257pydocstyle に名前が変わった
  • flake8 とかで間接的に使っている分には特に対応する必要はない

参考

事の発端は、このチケットらしい。

Please rename this tool · Issue #466 · PyCQA/pycodestyle · GitHub

いじょう。

Python: ... (Ellipsis) は任意の処理を示すのにも便利かも

PEP 484 – Type Hints を読んで「なるほど、こういう使い方もあるのか」と気づかれたのでブログに書いておく。 尚、このエントリの内容を実行するには Python 3 以降が必要となる。

使った Python のバージョンは次の通り。

$ python --version
Python 3.6.1

Ellipsis について

Python 3 には Ellipsis というオブジェクトがある。 これはドットを三つ連続させたもので得られる。

>>> ...
Ellipsis

これの使いみちとしてはコンテナオブジェクトでスライスと共に用いられることが多い。 Ellipsis 自体の解説は以前こちらのエントリで紹介している。

blog.amedama.jp

任意の処理を示すためのコードについて

ところで、これまで概念的な説明をするときに書くコードも、なるべく実行できるように書くようにしていた。

例えば、関数ならこんな感じで書くことが多かったように思う。 ここには任意の処理が入りますよ、みたいなのを説明するのにはコメントを使って、中身は pass にするとか。

def f():
    # Do something
    pass

これは、コメントだけだと文法的に正しくないため。

>>> def f():
...     # Do something
...
  File "<stdin>", line 3

    ^
IndentationError: expected an indented block

あるいは docstring を利用して、こんな風にすることもできるかもしれない。

def f():
    """Do something"""

docstring は実のところ単なる文字列なので、それが式になることでこの場合は前述したようなエラーにならない。

任意の処理を … (Ellipsis) で表現する

上記でも伝わりさえすれば問題はないんだけど Ellipsis を使えばさらに分かりやすくなる気がする。

def f():
    # Do something
    ...

Ellipsis 単独でもちゃんとした式なので、上記は Python 3 の処理系がちゃんと解釈できる。

あるいは「この変数には何かが入るよ」みたいな処理も Ellipsis で表現できる。

>>> variable = ...

これも variable に Ellipsis を代入しているだけなので、もちろん有効な Python コードになる。

問題は … が Ellipsis オブジェクトになるのが Python 3 から、っていうところかな。 上記は Python 2 では使うことができない。

Ubuntu 16.04 LTS に最新の VirtualBox をインストールする

apt-get で入る VirtualBox は古いので新しいソフトウェアと組み合わせると支障が出る場合もある。 なので Oracle が公開しているリポジトリを使って最新版をインストールする方法について書いておく。

インストール方法は以下の公式サイトに載っている。 Debian 系のシステムは全て同じらしい。

Linux_Downloads – Oracle VM VirtualBox

使った環境は次の通り。

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
$ uname -r
4.4.0-79-generic

まずは APT パッケージシステムのソースリストに Oracle のリポジトリを登録する。 使う Ubuntu のバージョンによって xenial のところにあるコードネームを書き換える。

$ cat << EOF | sudo tee -a /etc/apt/sources.list > /dev/null
deb http://download.virtualbox.org/virtualbox/debian $(lsb_release -cs) contrib
EOF

このままだとパッケージの検証ができずエラーになるので認証鍵をインストールする。

$ wget -q https://www.virtualbox.org/download/oracle_vbox_2016.asc -O- | sudo apt-key add -

あとは APT の設定を更新したら VirtualBox をインストールするだけ。

$ sudo apt-get update
$ sudo apt-get install virtualbox-5.1

ちなみにバージョンが上がるとカーネルモジュールもリビルドが必要になるみたいなので dkms も入れておいた方が良さそう。

$ sudo apt-get install dkms

Nested Virtualization するなら qemu/KVM 一択だけど、しないときは VirtualBox もお手軽で良いね。

Ubuntu 16.04 LTS をパスワードなしで sudo できるようにする

毎回なんとなくで設定している気がするので一つのコマンドで実行できるようにした。

使った環境は次の通り。

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
$ uname -r
4.4.0-79-generic

あとは以下のコマンドを実行するだけ。

$ cat << EOF | sudo tee /etc/sudoers.d/$(logname) >> /dev/null
$(logname) ALL=(ALL) NOPASSWD:ALL
EOF

これで /etc/sudoers.d 以下に自分のアカウント名で設定ファイルが作られる。

$ cat /etc/sudoers.d/$(logname)
amedama ALL=(ALL) NOPASSWD:ALL

めでたしめでたし。