Monors Note

Pythonとそれ以外いろいろ

PythonでのEnumクラスの使用方法

Enum型ってなんのためにあるの状態だったのですが、かなり便利なことに気が付いたのでまとめます。

値クラスの作成をしたはいいが、不具合が多くてもっといい方法はないかを探したのがきっかけです。 (環境はPython 3.5.2です。)

最初に作成した値クラスは以下の通り。

class Pos(object):
    def __init__(self): raise AttributeError('this instance is not able to embody')
        
    NOUN = '名詞'
    VERB = '動詞'
    PARTICLE = '助詞'
    ADJECTIVE = '形容詞'

しかし、この方法では Pos.NOUN = '猫'でクラス属性の書き換えが可能です。 これらをいじって堅牢な定数クラスを作成するのは骨が折れそうです。

Enum型の導入

これら問題を簡単に解決する標準ライブラリがPythonにはあるようです。 また、Effective Javaでも上記の様な型クラスを作成せず、Enum型を使用することが推奨されています。 (項目30: int定数の代わりにenumを使用する)

Enum型を用いる利点

Enum型を用いる利点は以下の通りです。

  • コンパイル時に代入された値に変換されないので、デバック時に分かりやすい。
  • 異なるEnum型を比較しようとするとコンパイルエラーとなる。

Enumの使い方

Python 3.4 から標準ライブラリに入る Enum 型が今からでも便利 - Qiitaを参考に使い方をまとめます。 (IntEnumに関しては省略しています。)

enumモジュール内のEnumクラスを使用します。呼び出し方法は3種類で可能ですが、下記の方法では内部の値が何なのかは覗けません。

from enum import Enum

class Hoo(Enum):
    NOUN = '名詞'
    VERB = '動詞'
    PARTICLE = '助詞'
    ADJECTIVE = '形容詞'
    NOUNS = '名詞'

# 3つの方法でアクセス可能
## 属性
print(Hoo.NOUN ) # Hoo.NOUN

## キーワードを指定した呼び出し
print(Hoo('名詞')) # Hoo.NOUN

## 添え字
print(Hoo['NOUN']) # Hoo.NOUN

値を覗くにはnamevalue属性を使用します。

Hoo.NOUN.name # 名詞

Hoo.NOUN.value # NOUN

値クラスで問題だった値の再代入を行った場合はエラーとなります。

Hoo.NEKO = '猫' # AttributeError: Cannot reassign members.

ただし、同値比較はenum型同士しか行えません。

print(Hoo.NOUN == '名詞') # False
print(Hoo.NOUN == 'NOUN') # False

gitでbranchも含めてtree構造を出す

gitを使っていてbranchも含めてtree構造に出来ればなと思い、調べてみた。 標準のgitに機能が含まれていました。 (参考:gitのrebaseとremoteとbranchと - 日々常々

gitのバージョン

git version 2.9.0.windows.1

branchの構造

masterbugFixの2つのブランチがある。

$ git branch -a
  bugFix
* master

コマンド

$git log --graph --decorate --oneline --all
* 280350c (HEAD -> master) bbbb.txt is modigied
| * 1f8ab7a (bugFix) aaaa.txt is bugFixed
|/
* cd668e3 maked master

git logはlogを見るためのコマンド。

オプションの説明

--graph

logのグラフ化が可能

$ git log --graph
* commit 280350c6abd5fcd5f6713b7dce9736f7141c233e
| Author: user
| Date:   Tue May 1 23:49:29 2018 +0900
|
|     bbbb.txt is modigied
|
* commit cd668e39ad2c4fa3d4bc1108d1ad66567366e1eb
  Author: user
  Date:   Tue May 1 23:40:52 2018 +0900

      maked master

--oneline

ハッシュ値(一意に決まる部分まで)コメントだけのシンプルフォーマットに変換

$ git log --oneline
280350c bbbb.txt is modigied
cd668e3 maked master

--all

全てのブランチを表示する。これがないとheadの場所のlogが表示されるだけ。

commit 280350c6abd5fcd5f6713b7dce9736f7141c233e
Author: user
Date:   Tue May 1 23:49:29 2018 +0900

    bbbb.txt is modigied

commit 1f8ab7a76000ba31e6e4c99d396d624671cacf67
Author: user
Date:   Tue May 1 23:45:34 2018 +0900

    aaaa.txt is bugFixed

commit cd668e39ad2c4fa3d4bc1108d1ad66567366e1eb
Author: user
Date:   Tue May 1 23:40:52 2018 +0900

    maked master

--decorate

(HEAD -> master)など、ブランチの親子関係が表示される。

commit 280350c6abd5fcd5f6713b7dce9736f7141c233e (HEAD -> master)
Author: user
Date:   Tue May 1 23:49:29 2018 +0900

    bbbb.txt is modigied

commit cd668e39ad2c4fa3d4bc1108d1ad66567366e1eb
Author: user
Date:   Tue May 1 23:40:52 2018 +0900

    maked master

Dockerに今さら入門してみる(Part 1)~環境構築とコンテナの使用方法~

「Dockerを使えば簡単に環境構築出来るよ」と耳にタコが出来るほど聞くけど、「フーン、そうなんだ」と聞き流していました。 「新年度も始まったし新しいことに挑戦するか」ということで今さらですが、Docker入門をしてみました。

書いていることに誤りがある場合は、ご指摘頂けるとありがたいです。

Part 1では環境構築とコンテナの操作を行っています。

Dockerとは

Dockerとは、Linuxのコンテナ技術をベースに作成された、仮想化技術のことです。

  • コンテナとは?
    • ホストOS上のKernel機能を追加複数のrootファイルシステムを実行出来るようにするもの。
    • ザックリ言ってしまえば、Linux環境上で複数の環境を構築出来るということ。(多分。。)
    • コンテナはimageから生成される

また、公式ページでのコンテナの説明

A container is launched by running an image. An image is an executable package that includes everything needed to run an application--the code, a runtime, libraries, environment variables, and configuration files. コンテナはimage

何がうれしいの?

1, 軽量

Dockerが主流になる前までは、VirtualBoxなど従来のハイパーバイザ型の仮想化が主流でした。 (ハイパーバイザー)http://wa3.i-3-i.info/word12196.htmlという仮想マシンをホストOS上に作成するし、その上にホストOSを稼働させる方式です。 これによりホストOSとゲストOSの分離を実現していました。 しかし、仮想マシンとゲストOSの(イメージファイル)http://wa3.i-3-i.info/word15853.html必要になりオーバーヘッド増えます。

Dockerは各アプリケーションをLinuxのコンテナ技術によって、

ホストOSのKernelは共有しつつも、各コンテナ内のアプリケーションを分離した名前空間の中で実行できます。

さわって理解するDocker入門 第1回 | オブジェクトの広場

(コンテナで区切られた空間それぞれに別のバージョンのMySQLが存在しても競合しない) 単に名前空間を分離しているだけなので、仮想マシンのエミュレーションも行われないため、イメージサイズも抑えられ 軽量化が可能です。

f:id:jsakusan:20180429001720p:plain (https://www.ogis-ri.co.jp/otc/hiroba/technical/docker/part1.htmlより)

2. 様々な環境にデプロイできる

Windows, Macをはじめ、AWSなど様々な環境で動作させることが可能です。

環境構築

実行環境

DockerToolboxのインストール

ここから、DockerToolboxをインストール docs.docker.com

インストールのマニュアルは以下を参照 docs.docker.com

動作確認のためコマンドを入力

$ docker info
error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.37/info: open //./pipe/docker_engine: The system cannot find the file specified. In the default daemon configuration on Windows, the docker client must be run elevated to connect. This error may also indicate that the docker daemon is not running.

エラーが発生したため、、、 dockerでネットワークエラー - This is a Penを参考に解決。 1. Dockerマシンの再度作成

$ docker-machine rm default
$ docker-machine create default --driver virtualbox
  1. 環境変数を設定
eval` "$(docker-machine env default)"`

実行したら、以下の様なメッセージが出た。

Error checking TLS connection: Error checking and/or regenerating the certs: There was an error validating certificates for host "192.168.99.103:2376": x509: certificate signed by unknown authority
You can attempt to regenerate them using 'docker-machine regenerate-certs [name]'.
Be advised that this will trigger a Docker daemon restart which might stop running containers.

言われるがまま、docker-machine regenerate-certsを実行。 再度、環境変数の確認。

$ sudo eval` "$(docker-machine env default)"`
bash: export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.103:2376"
export DOCKER_CERT_PATH="C:\Users\Shusaku SAHASHI\.docker\machine\machines\default"
export DOCKER_MACHINE_NAME="default"
export COMPOSE_CONVERT_WINDOWS_PATHS="true"
# Run this command to configure your shell:
# eval $("C:\Program Files\Docker Toolbox\docker-machine.exe" env default): No such file or directory

その後、言われるがまま、

eval $("C:\Program Files\Docker Toolbox\docker-machine.exe" env default):

を実行したら治りました。

公式チュートリアルをやってみる。

Docker公式サイトのチュートリアルをこなしていきます。 docs.docker.com 公式文章を読んでいると、コンテナはimageから生成されるらしい。

Dockerのバージョンを確認する

> docker --version
Docker version 18.03.1-ce, build 9ee9f40

Dockerコンテナのインストール

Dockerのコンテナイメージのインストールを行う。 今回インストールを行うのはhello-world

> docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
9bb5a5d4561a: Pull complete
Digest: sha256:f5233545e43561214ca4891fd1157e1c3c563316ed8e237750d59bde73361e77
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more a     mbitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

Docker上でアプリケーションを動かく際の構成

Dockerでアプリケーションを作成する際には、場合により3つの階層構造を構成する必要があるみたいです。(下に行くほど低レイヤー)

  • Stack
    • サービスをどのようにインターラクションするか。
  • Service
    • プロダクト内の各コンテナをどの様に動かすかを決める。
  • Container <- ここからチュートリアルが始まります。

Dockerfileの作成

  • Dockerfileはコンテナ内の環境を定義するファイル。
  • ネットワークインターフェイスやデスクドライブはDockerホスト内にすでに存在する。
  • ポートマッピングやオレオレファイルのコピーは自分で行ってね! <- これを行うのがDockerfile
  • 一度Dockerfileを作ってしまえば、同様の設定を持つコンテナをコピーすることが可能。
  • Dockerfileで環境を作成した後に、Pythonなどのアプリ系をいれる。

Dockerfileを書いてみる。

Dockerfile リファレンス — Docker-docs-ja 17.06.Beta ドキュメントを参考にしならが、一部コマンドの解説を入れています。 下記の3ファイルを同じフォルダに格納する。

# Use an official Python runtime as a parent image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]

FROM命令で(ベースイメージ)http://docs.docker.jp/glossary.html#base-imageを指定します。ベースイメージとは、親イメージを持たないイメージのことです。

RUN命令は既存イメージ上の新しい例やで、あらゆるコマンドを実行しその結果をコミットする命令です。

CMD命令は、コンテナ実行時のデフォルトを提供します 。 CMD ["パラメータ1", "パラメータ2"...]と指定することで起動時のパラメータを定義

Dockerは、カーネルの上に多階層のImageレイヤーがあり、一番下にベースとなるImageがあります。これは概ね、DebianとかUbuntuなどのOSに相当する部分です。そして、この上に、ミドルウェアの層が重なります。たとえば、emacsApacheなどです。それぞれのレイヤーは、下位のレイヤーを参照しています。下位のレイヤーは読み取り専用です。また、最上位のレイヤーは、書き込みできるようになっていて、下位のレイヤーに機能を付け足して独自のImageになりえます。この書き込む操作がCommitです。 https://cdn-ssl-devio-img.classmethod.jp/wp-content/uploads/2016/05/screenshot-2016-05-01-2.46.19-640x359.png https://cdn-ssl-devio-img.classmethod.jp/wp-content/uploads/2016/05/screenshot-2016-05-01-2.46.19-640x359.png dev.classmethod.jpより

requirements.txt

Flask
Redis

app.py

from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

Dockerfileからimgaeファイルの作成をする。また、requirement.txtapp.pyはADDコマンドに使用する。

Docker imageを作成する。

docker build -t friendlyhello .

Dockerでアプリを立ち上げる。

docker run -p 8000:80 driendlyhello -pオプションで、外部からアクセスされるポート番号:コンテナ側のポート番号を指定 今回の場合、PCのポートを4000として、コンテナ側のポート番号を80とている。

ブラウザでlocalhost:4000へアクセスしてみたが、このサイトにアクセスできませんと出た。 これは、Dockerのアドレスにアクセスしていないから。 (VirtualBoxを使用していない場合は、localhost:4000でアクセス可能。)

qiita.com

上記を参考に、dockerのアドレスを取得

docker-machine ip
192.168.99.103

http://192.168.99.103:4000で起動を確認できた。

本来は-bオプションを付けていないのでCTRL + Cでストップするはずなのだが、 Windowsの場合は明示的にstopコマンドを入力しないとコンテナが止まらないらしい。

コンテナをバックグラウンドで実行・停止

-bコマンドでバックグラウンド実行が可能。 docker run -b -p 4000:80 friendlyhello

また、停止したいときはstopコマンドを使用する。 しかし、コマンドではcontainre idが必要なため、docker container lsでidを取得する。

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
af241aaad6d1        friendlyhello       "python app.py"     16 minutes ago      Up 16 minutes       0.0.0.0:4000->80/tcp   admiring_morse

$ docker stop af241aaad6d1
af241aaad6d1

参考

アルゴリズムの計算量を求める。

アルゴリズムの計算量は以下の様に
O(n)
O(n2)
と記述を行う。

アルゴリズムの計算量を計算できると、処理の速度の比較を行うことが出来る。 計算量を計算することで、アルゴリズムの比較を大雑把に行うことが可能になる。

具体的なことに関しては以下に具体的にまとめられていおり、かなり分かりやすい。

qiita.com

ざっくり言えば、

  • forやwhileの繰り返しの分だけnが階乗されていく。
  • i = i * 2の場合はO(log x)となる。

bash on ubuntu on windows でCドライブを扱うには

Cドライブの場所

bash on ubuntu on windows では起動時した状態ではCドライブの場所が分からなかった。 しかし、/mntにCドライブをマウントしているようだ。

qiita.com

そのため、cd /mnt/cと打てばCドライブに移動が出来る。

Windows上で簡易に操作を行うにはlnでリンクを作成しておけばいい。

Lessen6-7:Extend Prefix

ポイント

再帰検索時の検索結果の格納  再帰を用いた検索で、各末端での検索結果を最上段の関数に返す場合は、基本的にreturnを使用して値の返しを行う。

def foo(x):
   if x > 100:
      return x
   else:
      return x + foo(x + 1)

しかし、ひとつの関数で複数の結果を生成し、それを最上段に返さなければいけない時「参照渡し」を利用すれば簡単に行える。

def find_words(letters):
    extend_prefix(letters)

def extend_prefix(letters, w='', results=None):
    if results: results = set()
    if w in WORDS: results.add(w)
    if w in PREFIXES:
        for L in letters: extend_prefix(w + L, letters.replace(L, '', 1))
    return results

resultset()なので、extend_prefix()で参照渡しされる。 よって、結果は最上段のresultに格納される。

Lesson1-25:Allmax

ポイント

keyでFunctionを指定する際に、初期入力がNoneの時の対象法

Key指定時の注意点

pythonにおいてmax(a, key=function)など、基準として関数を指定することが可能。 このような関数を独自で実装するとき、key指定がない場合のfunctionをどうするかが問題になる。 この時lambda関数をデフォルト指定にしたい場合の実装をまとめる。

def allmax(iterable, key=None):
    "Return a list of all items equal to the max of the iterable."
    result, maxval = [], None
    key = key or (lambda x: x) # keyがNoneの場合、lambdaを指定する。
    for x in iterable:
        xval = key(x)
        if not result or xval > maxval:
            result, maxval = [x], xval
        elif xval == maxval:
            result.append(x)
    return result