molpako.py

もるぱこのブログです

Pythonの並行処理 concurrentモジュール

@molpako です!

前回 では、multiprocessingモジュールを勉強しました。 今回は、concurrentパッケージを勉強していきます!

concurrentパッケージには、一つだけモジュールがあります。それが、並列実行のための concurrent.futures です。

concurrent.futures

concurrent.futuresは、主に二つのクラスを提供しています。

  • ThreadPoolExecutor
  • ProcessPoolExecutor

この二つのクラスは前回紹介したthreadingとmultiprocessingを呼び出していて、スレッドやプロセスのプールを使用して非同期に実行します。また、両方ともExecutorのサブクラスで同じインターフェースを実装しているので、同じメソッドを提供しています。

では、 前回と前々回で書いた処理をconcurrent.futuresを使用し実装していきます!

ThreadPoolExecutor

まずは並列に実行する関数の作成。引数によって待つ時間を変えれるようにしています。

import select
import socket

def slow_syscall(timeout=1):
    """遅いシステムコールを実行する関数"""
    select.select([socket.socket()], [], [], timeout)

ThreadPoolExecutorを使用して、slow_syscall()を並列に実行します。並列処理の終了を同期するために、withステートメントを使用しています。これは、内部で shutdown(wait=True) を呼び出していて、実行されたプール内のスレッドが全て終わるまで待機させています。

from time import time
from concurrent.futures import ThreadPoolExecutor

start = time()
# with を使うことで、pool内の実行がすべて終わるまで待つ
with ThreadPoolExecutor(max_workers=10) as pool:
    for _ in range(10):
        pool.submit(slow_syscall)

print('Took %.3f seconds' % (time() - start))

>>>
Took 1.006 seconds

ProcessPoolExecutor

関数の作成。

def factorize(number):
    """素因数分解する関数"""
    for i in range(1, number + 1):
        if number % i == 0:
            yield i

def call_factorize(number):
    """イテレーターをリストに変換する"""
    return list(factorize(number))

ThreadPoolExecutorと同じインタフェースを実装しているため、同じように扱うことができます。ついでに計算した結果も出力しておきます。

from concurrent.futures import ProcessPoolExecutor

numbers = [53541233, 21235343, 11421443, 5423123]
start = time()

with ProcessPoolExecutor(max_workers=2) as pool:
    # mapは呼び出す関数をイテラブルな要素それぞれに対して実行する。
    results = pool.map(call_factorize, numbers)

    for result in results:
        print(result)

print('Took %.3f seconds' % (time() - start))

>>>
[1, 5501, 9733, 53541233]
[1, 21235343]
[1, 11, 383, 2711, 4213, 29821, 1038313, 11421443]
[1, 5423123]
Took 4.070 seconds

まとめ

  • threadingやmultiprocessingを直接扱わずとも、concurrent.futuresを使用して並列処理ができる。
  • 両方とも簡単なインターフェースを実装していてとても扱いやすい。

次回は、データのやり取りを安全に行うための queueモジュールについて勉強しますー!

参考文献

Pythonの並行処理 multiprocessingモジュール

@molpako です!

Pythonを勉強していて並行処理あたりが難しいと感じたので、Golangと比較しながらまとめていきます。

前回 では、threadingモジュールを勉強しました。 今回は、multiprocessingを勉強していきます!

multiprocessing

multiprocessingのサンプルコードをみると start() や join() というメソッドがあるしthreadingと同じじゃん!マルチスレッドとマルチプロセスはどっちを使えばいいんだ!と感じましたが、ドキュメントを見ると答えが書いてありました。multiprocessingモジュールの目的は 並列処理 ということです。

threadingの所で説明しましたが、PythonはGILという仕組みがあって、それがスレッドを同時に一つのスレッドしか動かさないようにしています。multiprocessingはその問題を解決するモジュールらしく、名の通り複数のプロセスを使いマルチコアの恩恵を受け、並列処理ができるみたいです。早速CPUバウンドな処理を並列にして高速化してみましょう。まずは並列ではなく、順番に実行します。

def factorize(number):
    """素因数分解する関数"""
    for i in range(1, number + 1):
        if number % i == 0:
            yield i

numbers = [53541233, 21235343, 11421443, 5423123]

from time import time
start = time()
for number in numbers:
    list(factorize(number))

print('Took %.3f seconds' % (time() - start))

>>>
Took 7.344 seconds

処理時間は約7秒でした。次に、プロセスクラスを作成し並列に実行していきます。複数プロセスの終了を待機するには、Threadクラスと同じように join() を使います。

import multiprocessing

class FactorizeProcess(multiprocessing.Process):
    """計算するプロセスの各処理を表すクラス"""
    def __init__(self, number):
        super().__init__()
        self.number = number
    
    def run(self):
        self.factors = list(factorize(self.number))

# プロセスの開始
start = time()
procs = []
for number in numbers:
    proc = FactorizeProcess(number)
    proc.start()
    procs.append(proc)

for proc in procs:
    proc.join()

print('Took %.3f seconds' % (time() - start))

>>>
Took 4.885 seconds

並列実行の場合の処理時間は約4.9秒!正直パフォーマンス的にもっと早くなるものかと思っていましたが、CPUバウンドな処理でも並列実行され時間が短縮できたのが確認できました。これは、多分、おそらく、予想ですが、プロセスはスレッドより重くオーバーヘッドがありメモリ使用量も多いから?と思います。

ちなみに、multiprocessingのPoolクラスを使用すると上記よりも少ないコード量でワーカープロセスのプールを制御し複数のプロセスを並列に動かすことができます。作成した素因数分解する関数 factorize() を使用して試してみましょう。

def call_factorize(number):
    """イテレーターをリストに変換する"""
    return list(factorize(number))


start = time()

# 計算する要素分プロセスを立ち上げる
with multiprocessing.Pool(len(numbers)) as pool:
    results = pool.map(call_factorize, numbers)

    for result in results:
        print(result)

print('Took %.3f seconds' % (time() - start))

>>>
[1, 5501, 9733, 53541233]
[1, 21235343]
[1, 11, 383, 2711, 4213, 29821, 1038313, 11421443]
[1, 5423123]
Took 5.252 seconds

ちなみにgolangでは、前回と同じようにgoroutine 1 を使えば計算処理もはやくなります。

メモリの共有

2つのプロセス間でデータのやり取りをするためには、Pipeクラスを使用します。(Queueクラスもありますが、別の記事で紹介します!)2

Pipe()が返すコネクションオブジェクトは send() , recv() などのメソッドがあり、socketオブジェクトに似ていますね。早速Pipeクラスを使用して2つのプロセス噛んでデータをやり取りしてみましょう。

FactorizeProcess を少し変更して、コネクションオブジェクトを扱えるようにします。プロセスが開始されると、計算をし、結果をパイプ先のプロセスへと送信します。

class PipeFactorizeProcess(multiprocessing.Process):
    """計算するプロセスの各処理を表すクラス
    結果をパイプ先のプロセスに送信する"""

    def __init__(self, numbers, conn):
        super().__init__()
        self.numbers = numbers
        self.conn = conn

    def run(self):
        for number in self.numbers:
            self.conn.send(list(factorize(number)))
        self.conn.close()

受信用のプロセスは、5秒間データが受信できるか確認します。確認ができたら受信をして、データの受信がなければコネクションを閉じます。

# Pipe()は、パイプの両端を表すConnectionオブジェクトのペアを返す!
parent_conn, child_conn = multiprocessing.Pipe()
p = PipeFactorizeProcess(numbers, child_conn)
p.start()
while True:
    if parent_conn.poll(5):
        print('receive: {}'.format(parent_conn.recv()))
    else:
        parent_conn.close()
        break
p.join()

>>>
receive: [1, 5501, 9733, 53541233]
receive: [1, 21235343]
receive: [1, 11, 383, 2711, 4213, 29821, 1038313, 11421443]
receive: [1, 5423123]

ちなみにgolangでは、プロセスやスレッドを扱わず、goroutineを扱います。goroutine間でのデータのやり取りはチャネルの通信によってデータのやり取りを行います。

// 素因数分解する関数
func factorize(numbers []int, c chan<- []int) {
    for _, number := range numbers {
        var a []int
        for i := 1; i < number+1; i++ {
            if number%i == 0 {
                a = append(a, i)
            }
        }
        c <- a
    }
    // 送信側がチャネルをクローズする
    close(c)
}


func main() {
    numbers := []int{53541233, 21235343, 11421443, 5423123}
    c := make(chan []int)
    go factorize(numbers, c)

    for i := range c {
        fmt.Printf("receive: %d\n", i)
    }
}

>>>
receive: [1 5501 9733 53541233]
receive: [1 21235343]
receive: [1 11 383 2711 4213 29821 1038313 11421443]
receive: [1 5423123]

まとめ

  • start()やjoin()などthreadingとAPIが似ている。(ので、移行がしやすい)
  • threadingと違い、マルチコア実行ができる。
  • Poolクラスにより複数プロセスの管理が簡単になる。
  • Pipeクラスにより二つのプロセスでデータをやり取りできる。

次回は 並列性 のための councurrent モジュールについて勉強しますー!

参考文献


  1. スレッドより小さい軽量スレッド。Golangで並行・並列処理する場合には、goroutineを扱う。

  2. データをやり取りするためにメモリを共有する方法があるが、デフォルトではメモリを共有しない。プロセス同士でメモリを共有したい場合は、ValueクラスやArrayクラスもしくは multiprocessing.sharedctypes を使用する。

Pythonの並行処理 threadingモジュール

@molpako です!

Pythonを勉強していて並行処理あたりが難しいと感じたので、Golangと比較しながらまとめていきます。

並行処理で使用する標準ライブラリ

Python 並行処理 1 」などで検索するといくつかのモジュールがでます。基本的には以下が出てくると思います。

このモジュールたちの使い方、使うべきタイミングなど勉強して行きましょうー!まずは、threadingから!

threading

threadingはスレッドを扱うモジュールです。正直スレッドに対しては「プロセスより軽い物でマルチスレッドとかで並行か並列処理できるんだろう」という認識しかなく、全然具体的なイメージを持っていませんでした。Pythonは並列処理 2 は向いていないということをネットでみたりしていたので「スレッド扱えるなら並列処理できるんじゃないの」と思っていましたがその曖昧な認識が間違っていたことに勉強してやっと気づけました。「実行がマルチスレッド」=「CPUのマルチコアを活用できる」=「並列処理できる」という認識は、実際C++Javaのような言語では間違ってないみたいですが、Pythonではそうではないみたい。。。それはなぜかと言うと、PythonのGIL(グローバルインタプリタロック) 3 という仕組みが同時に一つのスレッドしか進行できないようにしているからです。

つまりPythonではマルチスレッドを使用してもマルチコアの恩恵を受けられず並列処理でスピードアップできないとのことです!スピードアップできないならいつ使うの!?と思っていたのですが、使うべきタイミングはドキュメントに以下のように書いていました。

I/Oバウンドなタスクを並行して複数走らせたい場合においては、 マルチスレッドは正しい選択肢です。

と記載されている通りI/Oバウンドタスクをスレッドで実行しプログラムから隔離することによって、ブロッキングI/O 4 の処理を行いながら必要な処理ができます。それでは、 select を使用して0.1秒のI/Oイベントを発生させる関数slow_syscall()を作成し、実験してみます。

from time import time
import select
import socket

def slow_syscall():
    """遅いシステムコールを実行する関数"""
    select.select([socket.socket()], [], [], 0.1)


# メインの実行スレッドが 1秒(=0.1 * 10) ブロックされる
start = time()
for _ in range(10):
    slow_syscall()
print('Took %.3f seconds' % (time() - start))

>>>
Took 1.024 seconds

複数のシステムコールを別々のスレッドで実行します。

start = time()
threads = []
for _ in range(10):
    thread = threading.Thread(target=slow_syscall)
    thread.start()
    threads.append(thread)

# join()で全てのスレッドの処理が終了するまで待機する
for thread in threads:
    thread.join()
print('Took %.3f seconds' % (time() - start))

>>>
Took 0.103 seconds

スレッドにブロッキングI/Oを処理させることで並列に実行され処理時間が約1/10になりました。ちなみにGolangでは、goroutineという軽量スレッドと使用し複数のgoroutineにそれぞれブロッキングI/Oの処理をさせることができます。下の例では順番に slowSyscall() を実行したので約1秒かかりました。

func slowSyscall() {
    fd, _ := syscall.Socket(
        syscall.AF_INET,
        syscall.SOCK_DGRAM,
        syscall.IPPROTO_UDP,
    )

    // 0.1秒かかるようにする
    timeout := &syscall.Timeval{Sec: 0, Usec: 100000}
    syscall.Select(fd, nil, nil, nil, timeout)
}

func Successive() int {

    // 逐次的に関数を実行させる
    for i := 0; i < 10; i++ {
        slowSyscall()
    }
    return 0
}

// --- PASS: TestSuccessive/#00 (1.02s)

slowSyscall() をそれぞれgoroutineに実行してもらうと、並列に実行され処理時間が0.1秒とpythonのthreadingと同じ結果になりました。

func Concurrency() int {
    wg := &sync.WaitGroup{}
    for i := 0; i < 10; i++ {
        wg.Add(1)

        // goroutineを立ち上げ関数を実行させる
        go func() {
            slowSyscall()
            wg.Done()
        }()
    }

    // 複数のgoroutineの処理が終わるまで待機する
    wg.Wait()
    return 0
}
// --- PASS: TestCunccurency/#00 (0.10s)

スレッドは、メモリ空間を共有するので複数のスレッドがグローバルなオブジェクトを扱うときは危険です。ロックを使って回避します。10個のスレッドを並列に実行し、スレッドでカウンタを上げていくプログラムで、データ競合を起こしてみます。

まずは、変数とカウンタクラスを用意します。

thread_number = 10

# スレッドでカウンタをあげる回数
call_number = 10**5

class Counter(object):
    """カウントするクラス。スレッドにこのクラスのオブジェクトを渡す"""
    def __init__(self):
        self.count = 0
    
    def increment(self):
        self.count += 1

ここで、データ競合を起こしやすくするため Barrierを使用します。Barrierはブロックのような働きをしてくれて、Barrierオブジェクトを生成する時に指定された数分だけの wait() が呼び出されると、同時にブロックから解放されます。これにより以下ではスレッド立ち上げのオーバーヘッドのせいでデータ競合が発生しにくくなるのを防ぎます。

b = threading.Barrier(thread_number)

def syscall_worker(i, counter):
    """i回システムコールを実行し、その度にカウントを1あげる"""
    b.wait()
    for _ in range(i):
        # 実行しない方が競合が起きやすいので実際にシステムコールは実行しない
        # slow_syscall()
        counter.increment()


# スレッドの開始
threads = []
counter = Counter()
for _ in range(thread_number):
    thread = threading.Thread(
        target=syscall_worker, args=(call_number, counter))

    thread.start()
    threads.append(thread)

for thread in threads:
    thread.join()

print('want: {}, got: {}'.format(
    thread_number * call_number, counter.count))

>>>
want: 1000000, got: 466988

出力を見ると、カウンタの数がおかしくなっていました。これはスレッド同士が処理結果を上書きしあいデータの不整合が起きているからみたいでした。threadingではそうのようなデータ競合が起きさせないように、Lockクラスが用意されています。。インクリメントする時に、ロックをかけるようにしてみてもう一度実行してみます。

class LockCounter(object):
    def __init__(self):
        self.lock = threading.Lock()
        self.count = 0
    
    def increment(self):
        with self.lock:
            self.count += 1

# もう一度スレッドの実行をする
...
>>>
want: 1000000, got: 1000000

できました!ロックをとった分、実行時間は遅くなりましたがデータ競合は起きてなく求める値が取得されました!

まとめ

  • PythonのGILが、マルチスレッドを使ってもマルチコアの恩恵を受けれないようにしている。
  • I/Oバウンドなタスクを扱うときはthreadingモジュールを使う。
  • 複数スレッドで同じオブジェクトを扱うときは、threading.Lockクラスを使う。

次回は、multiprocessingを勉強していきますー!

参考文献


  1. 複数のタスクを 見かけ上 同じ時間に実行すること。OSが1コア上で実行するプロセスを切り替えている。他のタスクを待たせないのが目的。

  2. 見かけ上ではなく、実際に 複数のタスクを同じに時間に実行すること。複数のコアが別々の仕事を実行している。早くするのが目的。

  3. スレッド同士がデータ競合しないようにするロック機構のこと。

  4. I/O処理中は待機するようなI/Oのこと。同期I/O。

ペパボに入社した

4/23 初出社

面接では Ansibe が好きでInfrastructure as code に興味があると言った。コードが書けるインフラエンジニアになりたいと言ったし、本当にそう思っていた。入社後、ターミナルを前に指が動かなかった。

「◯◯して」

簡単なオペレーションを振られても、それさえできなかった。そして、今求められていることは難しいことではなくて、簡単なことというのは理解していた。だから、すごく恥ずかしくて悔しかった。

2年弱の間、自分のことをインフラエンジニアだと思って生きていた僕がなぜそれをできなかったか。

  • WindowsServer メインの案件ばかりだったから
  • 運用をしたことがなかったから
  • 構築はAnsibleでしていたから

何個か思いついたけど、どれも言い訳な気がする。と考えてたらふと、エンジニアとしてのレベルは低いだけでは?と思った。そしてそれは間違いないと思う。

本当に僕は 意識高い系 なので、やりたいこともあるし勉強したいこともある。しかもそれが自然に湧き続ける。増え続け、減らない。それをやらないし勉強しないから。

そういった毎日を繰り返して、ターミナルの前で固まる僕が形成されたのだろう。

このままだと本当に僕が僕の嫌いな人間になってしまいそうなので、ここにやるべきことを書く。

一旦この二つにする。この二つを選んだ理由は、なんか必要そうだから。

まとめ

本当に入社してよかった。そう思ったのは、↑のように思えたから。

ダックタイピングって?

こんにちわ。もるぱこです。

何気なくいつものようにPythonの記事を眺めるためにネットサーフィンしていると、以下の記事で、

動的型付けで得るもの、失うもの。
最後の指摘は、動的型付けは得ることよりも失うものの方が大きい、というものです。
まず、得るものとは何か? について考えましょう。ここで僕が言えるのは、動的型付けではなくダックタイピング(注:ダック型付けと書くべきか?)をすべきということです

という記述がありました。

www.publickey1.jp

・・・ダックタイピングって???一度、オライリーの入門Python3でチラ見はしましたが、もう忘れて(そもそも理解していなかった)しまっていました。

今日はこのPythonにおけるダックタイピングについて勉強していきます。

ダックタイピングとは

まずは、Wikipediaさん曰く、

オブジェクトがあるインタフェースのすべてのメソッドを持っているならば、たとえそのクラスがそのインタフェースを宣言的に実装していなくとも、オブジェクトはそのインタフェースを実行時に実装しているとみなせる
"If it walks like a duck and quacks like a duck, it must be a duck"
(もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルである)
ダック・タイピング - Wikipedia

らしいです・・・。つまりクラスの型が一致しているかどうかではなく、オブジェクトの関数や属性が適切かどうかで同じインターフェースを実装しているかのように振舞える。ということですかね。

それでは、サンプルコードを書いていきましょう。

書いてみる

class Dog:

    def __init__(self, name):
        self.name = name

    def sound(self):
        return 'ワンッ!'


class Duck:

    def __init__(self, name):
        self.name = name

    def sound(self):
        return 'クワッ!'


def test(animal):
    """属性nameと関数soundを使用するテストコード"""

    print('{}は、{}と鳴いた'.format(animal.name, animal.sound()))

上のクラスは「犬」のクラスです。属性は、nameをもっています。関数はsoundで引数はありません。 上のクラスは「アヒル」のクラスです。属性は、nameをもっています。関数はsoundで引数はありません。

では、上記ふたつのクラスのオブジェクトをtest()に与え実行してみましょう

実行結果

>>> test(Dog('もるぱこ'))
もるぱこは、ワンッ!と鳴いた

>>> test(Duck('もるぱこ'))
もるぱこは、クワッ!と鳴いた

OK!!

DogDuckは違うクラス(型)ですが、属性や関数が一致しているため互換性が取れていますね!

最後に

  • Python(動的型付け言語)では、ダックタイピングを行うこと
  • DogDuckは違う型なのでisinstance()type()での判別は避ける
  • 標準ライブラリabcを使用すればインタフェースの実装ができる

以上です!

最後まで見ていただきありがとうございました。これからもよろしくお願いします。

Django の User モデルを拡張する

これらのサイトを見ながらしていきます。

目標

  • User モデルを拡張して Employee モデルを作成する
  • 追加するフィールドは employee_number (社員番号)
  • employee_number は主キー

方法

ふたつあるそうです。ひとつめは、プロキシモデル を使う方法。ふたつめは、1対1リンク を使う方法。

プロキシモデル

DB に新しいテーブルを作ることなく、モデルを継承できるモデル。DB スキーマに影響を与えずに既存モデルの振る舞いを変更するために使用される。

使いどき
  • DB へ追加情報を格納する必要がないとき
  • 簡単な関数の追加やクエリマネージャーの変更するとき

1対1リンク

  • 通常の Django モデル
  • プロキシモデルとは異なり、自分のテーブルを持つ
  • OneToOneFieldを介して既存モデルと1対1の関係を持つ
使いどき

既存のモデルに関する追加情報を格納したいとき

この1対1モデルはサイトユーザーに関する、認証には関連しない情報を格納することがあるため、しばしばプロファイルモデルと呼ばれます。

今回は、既存の User モデルにフィールドを追加したいので1対1リンクを使用して拡張していきましょう!

1対1リンクを使用した User モデルの拡張

この方法では、既存の User モデルに関連する追加情報を格納する新しいモデルを作成します。

Step1. models.pyに新しいモデルの定義を行う

from django.contrib.auth.models import User

class Employee(models.Model):
    employee_number = models.IntegerField('社員番号', primary_key=True)
    user = models.OneToOneField(User, on_delete=models.CASCADE)

employee_numerが目的にあった、User に追加したい情報の社員番号です。 userauth.models.Userとの1対1リンクを持っています。

Step2. create_userし、拡張した変数を設定する

from django.contrib.auth.models import Employee, User

user = User.objects.create_user('molpako', 'molpako@mail.address', 'password')
employee = Employee.objects.create(user=user, employee_number=22)

Step3. userからemployee_numberにアクセスできるか確認する

print(user.employee.employee_numer)
22

OK!!
最後に、きちんと主キーになっているかテストコードを書いてみしょう

テストケースを書く書く

今回のテストケースは

  • employee_numberはユニークキーか
  • employee_numberからUserオブジェクトをもってこれるか

です。

テストコード(sandbox/tests.py)は以下になりました。

from django.test import TestCase
from django.contrib.auth.models import User
from django.db.utils import IntegrityError
from .models import Employee


class EmployeeModelTests(TestCase):

def test_employee_number_is_unique(self):
    """
    社員番号が同じにならないこと
    """

    user01 = User.objects.create_user('molpako01', 'molopako@mail.address', 'password')
    user02 = User.objects.create_user('molpako02', 'molopako@mail.address', 'password')

    employee01 = Employee.objects.create(user=user01, employee_number=22)

    with self.assertRaises(IntegrityError):
        Employee.objects.create(user=user02, employee_number=22)

def test_get_uesr_from_employee_number(self):
    """
    社員番号からユーザーを取得する
    """

    user = User.objects.create_user('molpako', 'molopako@mail.address', 'password')
    employee = Employee.objects.create(user=user, employee_number=22)

    self.assertEqual(Employee.objects.get(employee_number=22).user, user)

それでは実行してみます!

$ python manage.py test sandbox
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.293s

OK
Destroying test database for alias 'default'...

楽しい!

自己紹介と目的と目標

自己紹介

ざっくり

はじめまして、molpakoです。社会人二年目。インフラエンジニア一年目をしています。好きなものは、 ansible, sphinx, python です。これらを使ってもっともっと仕事を楽にしたいなと考えています。しかし、メインの仕事は大手SIerの二次請けです。なので・・・退職します。

なぜ辞めるか

一番は、元請け様に Infrastructure as Code などの言葉が通じなかったこと。主観ですが、大手SIerの方たち (少なくとも私が関わった人たち) がやることは

  • エクセルでのスケジュール作成・管理
  • エクセルでの設計書作成
  • エクセルでの報告・承認資料作成
  • 作業はほぼ下請けに丸投げ

でした。さらに技術は VB や bat で止まっている。

そんな人たちの下について何が得られるか正直わかりませんでした。私自身、管理職になっていく道もあるのかなと思いつつも、このように自分で何も作れない(しないんじゃなくて、できない)管理職には絶対になりたくないと思っていました。

それは、上記の元請け様と社内の上司を見て思ったことでもあり、このままだといずれも自分もそうなってしまうんではないかと不安がありました。

どこにいく?

自社サービスを行っているWEB系ベンチャー企業に行きます!決め手は、

  • 会社としての OSS 活動が活発なこと
  • アウトプットを大事にしていること

でした。技術のキャッチアップをせざる得ない環境に身を置いてみようと勇気を出しての転職です。

業務内容は、自社サービス環境のインフラ周りです。OpenStack が触れると聞いてわくわくしています。

目的

なぜブログを始めたのか、その理由です。

  • 今まで勉強会には参加しても発表する側になることは考えていなかったので、まずはブログからアウトプット力をつけるため
  • 今回転職活動をしてみて、ブログはWEB系企業に自分の技術をアピールする一つの手段と思ったため

目標

これから自分がどんなエンジニアになりたいか、大きい夢を語ります。

  • ソフトウェアエンジニアになる
  • Django *1 でWebアプリを作成し、人の役に立つ
  • 完全に Python を理解して、勝つ
  • Dropbox のエンジニアになる

いつか本気出す!

*1:Pythonで実装されたWebフレームワーク