やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

GitHubAPIのリクエスト部分を実装する方法について考える

前回からの続き。

前回まで

http://ytyaru.hatenablog.com/entry/2017/03/02/000000
http://ytyaru.hatenablog.com/entry/2017/03/03/000000
http://ytyaru.hatenablog.com/entry/2017/03/04/000000

  • 案1:総合リクエスト関数を用意する
    • 例:github.request('username', 'api_name', params={"key": "value"})
  • 案2:APIごとに関数を用意する
    • 例:github.repo.create(name='', description='', homepage='')
  • 案3:動的にインタフェースを用意する
  • 案4:オブジェクト指向デザインパターンでどうにかする
    • 要学習。具体案なし。できるかどうかも不明。

案1:総合リクエスト関数を用意する

import github
g = github.GitHub()
g.request('username', 'api_name', params={"key": "value"})
  • GitHubユーザ名
  • GitHubAPI名
  • GitHubAPI引数

これら3つの引数から、HTTPリクエスト情報を作成する。 キーになる他の情報はすべてデータベースに登録する。

DBを作成するのが相当大変そう。 パスワードやTokenだけでなく、APIエンドポイントのURLや、HttpMethod(GET,POST,...)、API引数など数々の情報が必要になる。

また、インタフェースが微妙。'api_name'ではどのAPIかわかりにくい。また、どんなAPIがあるのかわからない。Pythonの関数にすればインテリセンスで参照できるだろうからそのほうが良い。また、引数はAPIごとに異なるのに、何を引数にするのか見えない。

しかし、HTTPリクエスト生成処理コードは重複せずに実装できると思う。

案2:APIごとに関数を用意する

以下のように、APIごとにメソッドを持たせたほうが見やすい。

import github
g = github.GitHub()
g.account.insert(
    username='username',
    password='',
    two_factor_secret='')
g.set_account('username')
g.auth.create(note='', scopes=['repo', 'delete_repo'])
import github
g = github.GitHub('username')
auths = g.auth.list()
auth = g.auth.get(id='')
auth = g.auth.create(note='', scopes=['repo', 'delete_repo'])
g.auth.delete(id='')
grants = g.auth.grant.list()
grant = g.auth.grant.get(id='')
g.auth.grant.delete(id='')

repos = g.repo.list(type='owner', sort='create_at', direction='asc')
repo = g.repo.create(name='repo_name', description='', homepage='')
repo = g.repo.edit(name='repo_name', description='', homepage='')
g.repo.delete(name='repo_name')
lang = g.repo.lang.list(name='repo_name')
g.repo.contributors.list(anon='true')
g.repo.teams.list(name='repo_name')
g.repo.tags.list(name='repo_name')

クラスかモジュールか

クラスにすべきかモジュールにすべきか。

APIの一部はユーザごとにエンドポイントやTokenが変わる。 クラスにして別インスタンスを生成できるようにすべきか。 そこでAPIメソッドのルートであるGitHubをクラスにする。

しかし、API実装コードはユーザごとに変わることはない。同一である。 異なるインスタンスを持ってもメモリの無駄である。システムで1つのみにしたい。

API実装コードは別ファイルに分離したいので別ファイル(モジュール)に定義したい。 インスタンス生成の必要はないからモジュールにすべきか。

APIを個別のモジュール(ファイル)で定義する。 モジュール内にクラスは作らず、モジュール関数を定義する。 これらの各関数を、GitHubクラスが参照し、呼び出すようにする。

これにて別ファイルに定義しつつ、無駄なインスタンス生成をせずに実装できると思う。試したことがないので、できるかどうかわからない。

案3:動的にインタフェースを用意する

GitHubのインタフェースが変更される可能性がある。または、違う名前で呼び出したくなるかもしれない。インタフェースを動的に変更できるほうが望ましい。

"python 関数 動的生成"で検索すると、こちらがヒット。感謝。

詳細はURL先に任せる。パッと見、私には難しい。また、ここまでやるようなことか疑問。その判断ができるほど知識がない。

案4:オブジェクト指向デザインパターンでどうにかする

オブジェクト指向デザインパターンPython言語仕様を学習する必要がある。

Pythonに関してはこちらを参照し、オブジェクト指向に関してはこちらを参照した。感謝。

PythonC#と比べるとかなりユルい。

こちらによるとオブジェクト指向のキモはカプセル化らしい。しかし、Pythonはprivateにできないためカプセル化できない。

Pythonオブジェクト指向プログラミング言語らしいが、C#Javaほどの機能はなさそう。プロトタイプベースなオブジェクト指向言語であるJavaScriptに近いように見える。

カプセル化できない

#!python3
#encoding:utf-8
class TestClass:
    def __init__(self):
        pass
        self._private_field = 100
        self.__private_field = 101
    def _private_method(self):
        print("this is private method 1.")
    def __private_method(self):
        print("this is private method 2.")

if __name__ == "__main__":
    c = TestClass()
    print(str(c._private_field))
#    print(str(c.__private_field))
    c._private_method()
#    c.__private_method()
    print(str(c._TestClass__private_field))
    c._TestClass__private_method()

アンダーバーを先頭に2つ付与するとprivateになる。

上記コードでコメントアウトした2行の方法ではアクセスできない。WindowsXPのPython3.4.4でのエラーメッセージは以下のようになる。ここまではいい。

>c:/python34/python test.py
100
Traceback (most recent call last):
  File "test.py", line 16, in <module>
    print(str(c.__private_field))
AttributeError: 'TestClass' object has no attribute '__private_field'

>c:/python34/python test.py
100
this is private method 1.
Traceback (most recent call last):
  File "test.py", line 18, in <module>
    c.__private_method()
AttributeError: 'TestClass' object has no attribute '__private_method'

しかし、最後の2行のようにすることで、アクセスできてしまう。 よってPythonではprivateにはできない。

ポリモーフィズム

ポリモーフィズム(多態性)とは、同じメソッド名だが異なる実装コードを呼び出す方法。

using System;
interface IHuman { void Speak(); }
class Japanese : IHuman { void Speak() { System.Console.WriteLine("私は日本人です。"); } }
class European : IHuman { void Speak() { System.Console.WriteLine("I am European."); } }
class Main
{
    public static void Main(string[] args)
    {
        List<IHuman> humans = new List<IHuman> { new Japanese(), new European() };
        humans.ForEach(x => x.Speak());
    }
}

x.Speak()でSpeak()メソッドを呼び出している。しかしその実装はクラスごとに異なっている。呼出元はおなじでも、呼び出す内容を別物にすることができる。これをポリモーフィズム(多態性)というらしい。

C#の場合、継承したときにメソッドの実装を義務付けることができる。もし実装されていなければコンパイルエラーになる。コンパイル時点でエラーになる箇所を発見できる。

対してPythonはinterfaceがない。継承ならあるらしいが、メソッド実装の義務付けはできないらしい。呼出元の名前のメソッドがなければ実行エラーになる。テストケースで網羅確認しておかないと、本番で実行エラーになる可能性がある。テストケース漏れが生じたら本番でのエラーになってしまう。

所感

C#で書きたくなった。しかしWindowsXPではAPIを叩けない。XPでは.NET4.0までしか使えず、HttpWebRequestクラスしか使えない。そのクラスではGitHubAPIが叩けなかった。SSL/TSLのバージョンが古いためと思われる。WindowsVista以降なら更新パッチがあったが、XPにはなかった。結果、XP上のC#ではGitHubAPIを叩けない。

LinuxC#開発できたら最高かもしれない。