Pythonのトレンド動向とクラスの書き方

f:id:araemonz:20190114235453p:plain

はじめに

AIや機械学習、ディープラーニングなどのブームでPythonが人気上昇していることは知っていました。ここ数年で、本屋でもずらりとPythonの書籍が増えよく目に入っていました。しかし、自分には必要ないかなと思っていて遠ざけていましたが、遊びがてらPythonに触れてみたところ、書きやすく読みやすいことがわかり好感をもちました。他人が書いたプログラムを読むときも、インデントというルールが理解を助けてくれるようで、読みやすく感じます。そして文字列の扱いがとても便利にできているので、何も科学技術計算のためだけに使うというような言語でもなさそうです。その場しのぎでPHPを使ってきましたが、この機会にPythonに乗り換えても良さそうです。 ここでは、Pythonの導入としてクラスの書き方を調べることにしました。

各言語のトレンド動向

ところでPythonも含め、主要なプログラミング言語がどのような動向にあるのか「Google Trends」で調べてみました。 https://trends.google.co.jp/trends/?geo=JP

グラフはすべての国を対象に、過去5年間、インターネット通信事業のカテゴリーに絞って表示したものです。

まずPythonですが、予想通りの人気上昇中といったところでしょうか。

f:id:araemonz:20190115000531j:plain

同じスクリプト言語のPHPやRubyは下降気味です。

f:id:araemonz:20190115000545j:plain

f:id:araemonz:20190115000601j:plain

JavaScriptは上昇しているのかなと思いましたが、平坦な結果です。これは常に安定した人気があるということなのかもしれません。

f:id:araemonz:20190115000621j:plain

またフロントエンドのJavaScriptライブラリのReactが爆発的に人気を増やしている様子です。

f:id:araemonz:20190115000633j:plain

Android開発でも使うJavaですがどんどん人気を下げています。

f:id:araemonz:20190115000740j:plain

その代わりKotlinが上昇中です。

f:id:araemonz:20190115000752j:plain

iOS開発で使うObjective-Cですが、だんだん人気がなくなっているようです。

f:id:araemonz:20190115000812j:plain

その代わりSwiftに置き換わっています。

f:id:araemonz:20190115000825j:plain

最新版Pythonを使えるようにする

さて、Google先生に聞いてpyenvで最新版のpython3.xをMacにインストールしました。ですが、以前にbrewインストールしたpython2.xを参照してしまい、最新版のpythonが使えない状態でした。色々とためした結果、次のようにして解決することができました。

zシェルを使っているのに、bashの設定に環境変数を書き込んでいたのが原因だったようです。 ホームディレクトリに移動して.zshrcを編集してあげます。

cd ~
vi .zshrc

次のような記述を最下部に付け足すことで上手く動きました。

export PYENV_ROOT="$HOME/.pyenv"
eval "$(pyenv init -)"
alias py='python'

毎回python hoge.pyと入力するのは面倒なのでpy hoge.pyで実行できるようにエイリアスを指定しています。

保存したら、zシェルの設定をリロードします。source .zshrc py -Vと叩いてPython 3.7.1と出力されました。これで無事、最新版のPythonが使えるようになりました。 ちなみに、printenvコマンドを実行すると、設定されている全ての環境変数が確認できます。

クラスの使い方

オブジェクティブなプログラミングができるように、入門としてクラスを作ることにしました。

import glob
import os

class RecursiveFile():
    lineN = 0 #static変数
    def __init__(self, startDir, extension): #コンストラクタ
        self.extension = extension
        self.paths = glob.glob(startDir+"/**/*."+extension, recursive=True)

    def trace(self, callback): #第一引数にselfを指定する必要がある
        for path in self.paths:
            RecursiveFile.lineN += 1
            callback(RecursiveFile.lineN, path) #クロージャー関数をinvokeしている
    
    def __del__(self): #デストラクタ
        print("destroy")




def tester():
    #このようにクロージャーにすることで、RecursiveFileクラスを気軽に使いまわすことができる
    def callback(num, path): #クロージャー用関数
        print("{0}: {1}".format(num, path))

    abspath = os.path.dirname(os.path.abspath(__file__))
    recursive = RecursiveFile(abspath, "txt")
    recursive.trace(callback) #クロージャー

if __name__ == "__main__":
    tester()

ディレクトリを再帰的に探索して、指定の拡張子ファイルを全て取得するだけの単純な処理ですが、プログラミング中にはクラス定義、コンストラクタ、デストラクタ、メソッド、スタティック変数、クロージャーを含んでいます。ここら辺が使えるようになると管理、メンテナンスがグンと楽になりそうです。

それでは、詳しくプログラミングをみていきます。

class RecursiveFile():
    lineN = 0
    def __init__(self, startDir, extension):
        self.extension = extension
        self.paths = glob.glob(startDir+"/**/*."+extension, recursive=True)

まずclass RecursiveFile():でクラスを宣言しています。 次にインデントして処理を書いていきます。 lineNはstaticな変数になります。型宣言などの必要がなくこのように書けば良いみたいです。 __init__はコンストラクタメソッドになります。メソッドの第一引数にはselfを指定しなければならないようです。selfの後に続いて引数書いていけば、クラスのインスタントを作るときに、RecursiveFile(abspath, "txt")のように引数を渡して初期化することができます。 そして、glob.glob(startDir+"/**/*."+extension, recursive=True)の部分が、ディレクトリを再帰的に探索して、指定の拡張子ファイルを全て取得する処理になります。glob.globはPython3.5以降で使えるようです。特定のフォルダ以降も再帰的に処理したい場合はrecursiveTrueに設定し、/**/のように書きます。 glob.glob("./**/*.txt", recursive=True)のようにたったの一行で、再帰的にファイルが取得できてしまうなら、今回のようにクラス化する必要はなかったかもしれません。それでもここでは「クラスの使い方の勉強」と思って、このまま進めていきましょう。

RecursiveFileクラスの続きの処理をみていきます。

    def trace(self, callback):
        for path in self.paths:
            RecursiveFile.lineN += 1
            callback(RecursiveFile.lineN, path)
    
    def __del__(self):
        print("destroy")

traceメソッドは独自に実装したメソッドになります。第二引数のcallbackには関数オブジェクトが渡される設計です。 実際に次のような関数を渡してあげることで、コールバックで処理を書くことができます。

    def callback(num, path):
        print("{0}: {1}".format(num, path))

クラスの最初の方で宣言したstaticなlineN変数は、RecursiveFile.lineNのように書くことでアクセスできます。 __del__(self)というのはデストラクタです。循環参照して解放されない場合などのデバッグに使えそうです。 以上で、基本的なクラス使い方がわかりました。

それでは、このクラスを実際に使ってみます。

def tester():
    def callback(num, path):
        print("{0}: {1}".format(num, path))

    abspath = os.path.dirname(os.path.abspath(__file__))
    recursive = RecursiveFile(abspath, "txt")
    recursive.trace(callback)

if __name__ == "__main__":
    tester()

tester()メソッドを作ってif __name__ == "__main__":以降で実行しています。 このファイルを直接実行した場合にのみ、if __name__ == "__main__":以降がスルーします。つまり外部ファイルからimportされた場合には実行されないので、開発でのテスト処理を書くことができます。 実行結果はこのようになりました。

1: /somewhere/hoge.txt
2: /somewhere/dir/fuga.txt
3: /somewhere/dir/subDir/fuga2.txt

以上でPython入門ができました。もしかしたら間違いや、もっと良い書き方があるかもしれませんので、各自でPythonを勉強してみてくださいね。