Pythonのコードが実行されている環境を識別する

# 概要
シェルから実行した場合とJupyterから実行した場合で実行するコードを分けたいという要望に応えるために，Pythonコードが実行されている環境を識別する関数を作りました．
類似の記事がないか検索してもPython環境構築に関する記事ばかりが引っかかったので，備忘録も兼ねて投稿します．

# 背景
私たちのサークルでは，Pythonで書かれたプログラムの開発環境として，主にVisual Studio + Python Tools for Visual Studioを利用しています．試行錯誤的にPythonコードを試す場合はJupyter Notebookも併用します．その際，Visual Studio用のコードとJupyter用のコードを切り替える必要が出てきます．
具体的には，アニメーション表示の処理です．我々の作るコードは時間発展する物理現象を解く事が主であり，アニメーション表示は何ステップかに一度呼び出され，結果を確認するために利用されます．そのため，アニメーションの表示にはmatplotlibの```FuncAnimation```ではなく，```canvas.blit```を利用しています．Juypterで表示をする際は，```display.display```と```display.clear_output```を併用しています．
いちいちコメントアウトやアンコメントを繰り返すのが億劫になったので，Pythonコードが実行される環境をプログラム内部から識別できればコードの共通化が実現できると考えました．

# 実装
参考にしたのはstackoverflowの次の記事です．
[How can I check if code is executed in the IPython notebook?](https://stackoverflow.com/questions/15411967/how-can-i-check-if-code-is-executed-in-the-ipython-notebook)
ページ中程のGustavo Bezerra氏の回答によると，次の関数？が利用できると書かれていました．

```python
get_ipython().__class__.__name__
```

この関数の挙動は

-  IPython（terminal）の場合は戻り値が'TerminalInteractiveShell'
-  Juypter（notebookとqtconsole）の場合は戻り値が 'ZMQInteractiveShell'
-  失敗する（シェルから実行されている）とNameErrorが投げられる

となっているようです．

回答例では，IPythonから実行されているかどうかをTrue/Falseで返していましたが，私たちの要求に応えるために，実行環境の名前を文字列で返すように修正しました．関数名は安直に```getEnvironment```としています．

```python
def getEnvironment():
    try:
        env = get_ipython().__class__.__name__
        if env == 'ZMQInteractiveShell':
            return 'Jupyter'
        elif env == 'TerminalInteractiveShell':
            return 'IPython'
        else:
            return 'OtherShell'
    except NameError:
        return 'Interpreter'
```
　
```get_ipython().__class__.__name__```の戻り値を```env```に代入し，それが```'ZMQInteractiveShell'```と一致すれば文字列'Jupyter'を返し，```'TerminalInteractiveShell'```と一致すれば文字列'IPython'を返します．それ以外の場合には'OtherShell'という文字列を返すようにしましたが，JupyterおよびIPython以外の枠組みに分類されるインタラクティブ環境が存在しているかは知りません．
NameErrorが投げられた場合はOSのシェルから実行されていると判断し，文字列'Interpreter'を返しています．

# テスト
以下の環境でテストし，所望の値が返ってくるかを確認しました．

 - Anaconda3 4.2.0.0 64bit
 - Python 3.5.2
 - Jupyter 4.2.0
 - IPython 5.1.0
 - Python Tools for Visual Studio 2.2.31124.00

## Jupyterでのテスト
![jupyter_test.png](https://qiita-image-store.s3.amazonaws.com/0/224040/66072ac1-4ff5-d76f-de9f-65f91f452cf6.png)
確かに'Jupyter'と表示されています．

## IPythonでのテスト
IPythonで実行する予定はないのですが，折角なので実行しておきます．

![IPython_test.png](https://qiita-image-store.s3.amazonaws.com/0/224040/2fdb5adb-76b1-cb31-83e3-da59b525d0d0.png)
正しく'IPython'と表示されています．

## OSのシェル（Visual Studio）での動作テスト
![ptvs_source.png](https://qiita-image-store.s3.amazonaws.com/0/224040/0b63b16f-6868-3df0-c570-f6fa3c0cfb14.png)
![ptvs_test.png](https://qiita-image-store.s3.amazonaws.com/0/224040/ff508c3f-f417-5e38-f9ab-4301e257f0c9.png)

無事'Interpreter'と表示されたので，私たちの要望がかなえられそうです．

# まとめ
本記事ではPythonコードが実行されている環境を識別するための関数を作成しました．これでJupyterで開発するコードとVisual Studioで開発するコードを共通化することができそうです．
アニメーションの表示に```canvas.blit```，あるいは```display.display```と```display.clear_output```を利用することの是非については議論しませんが，機会があればその辺りも調査したいと思います．

# 関数定義
最後に，ヘルプも含めた関数の定義を示します．

```python
def getEnvironment():
    '''
    To confirm the environment on which the python script calling this function is running.
    This function returns environment's name as String
    
    Input
    None
    
    Output
    'Jupyter'     : runs on Jupyter
    'IPyhon'      : runs on IPython interactive shell
    'OtherShell'  : runs on Other type interactive shell, I don't know that such shells exist though
    'Interpreter' : runs on System shell with Python interpreter
    '''
    try:
        env = get_ipython().__class__.__name__
        if env == 'ZMQInteractiveShell':
            return 'Jupyter'
        elif env == 'TerminalInteractiveShell':
            return 'IPython'
        else:
            return 'OtherShell'
    except NameError:
        return 'Interpreter'
```