Pythonでオブジェクトの型に準備されている通常メソッドの一覧を出力する関数

Pythonを勉強していると、「この型のメソッドは何だろう?」と思う時があります。
この時、オブジェクトを obj とすると

dir(obj)

とすることで、一覧を得ることができます。

たとえば、リスト型のメソッドを知りたいとします。dir() を使うと以下のようになります。

x = [1, 2]
dir(x)

[code lang=text]
['__add__', '__class__', '__class_getitem__',
 '__contains__', '__delattr__', '__delitem__',
 '__dir__', '__doc__', '__eq__', '__format__',
 '__ge__', '__getattribute__', '__getitem__',
 '__gt__', '__hash__', '__iadd__', '__imul__',
 '__init__', '__init_subclass__', '__iter__',
 '__le__', '__len__', '__lt__', '__mul__',
 '__ne__', '__new__', '__reduce__', '__reduce_ex__',
 '__repr__', '__reversed__', '__rmul__', '__setattr__',
 '__setitem__', '__sizeof__', '__str__', '__subclasshook__',
 'append', 'clear', 'copy', 'count', 'extend',
 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
[/code]

ここで、”–” からはじまるメソッドは特殊メソッドと言われ、その型の振る舞いを細かく調整するものとのことです。今回はここには踏み込みません。

今、私は、「通常メソッドだけリストアップしたい」と思いました。どうしたらできるでしょうか?

  • short answer
  • 以下の関数で求められます。

    def get_normal_methods(obj):
        """
        指定されたオブジェクトの通常メソッドのリストを返す関数
        :param obj: 通常メソッドを取得する対象のオブジェクト
        :return: 通常メソッドの名前のリスト
        """
        normal_methods = [method for method in dir(obj) if not method.startswith('__')]
        return normal_methods
    
    def print_normal_methods(obj):
        """
        指定されたオブジェクトの通常メソッドの数と一覧を出力する関数
        :param obj: 通常メソッドを出力する対象のオブジェクト
        """
        typename = type(obj)
        normal_methods = get_normal_methods(obj)
        num_normal_methods = len(normal_methods)
        print(f'{typename} has {num_normal_methods} methods:')
        for method in normal_methods:
            print(method)
    
  • long answer
  • 上記の関数に至った考え方を書いておきます。先程の例の通り、x は [1, 2] というリストが入っているとします。

    • dir(x)の型とその要素の型
    • dir(x) はlist型です。そして、リストの中を見ると、 `[‘__add__’, ‘__class__’, …]` となっていますので、リストの要素のひとつひとつはstr型のようです。

      試しに、リストの最初の要素の型を見てみます

      type(dir(x)[0])
      <class 'str'>
      

      予想どおり、str型でした。

    • str型で「ある文字列から始まる」というメソッド
    • 今回、特殊メソッドは必ず “__” から始まるというルールがあります。なので、str型に「ある文字列から始まる」というメソッドがあるかどうかを調べてみたら、’startswith’ というメソッドがありました。

      早速試してみます。

      a = '__add__'
      a.startswith('__')
      True
      

      “__” から始まっているので、Trueとなりました。これで、判定できそうです。

    • for と if を使って”__” から始まらなければ表示
    • dir(x) はリストでした。なので、このリストを for を使って、ひとつひとつ変数 method に入れ、もし、”__” から始まっていなければ、normal_methods というリストに入れることにします。
      リストの要素である method は str型のはずですので、ここで先程の startswith() メソッドを使うことにします。

      normal_methods = []
      for method in dir(x):
        if not method.startswith('__'):
          normal_methods.append(method)
      
      normal_method
      ['append', 'clear', 'copy', 'count', 'extend',
       'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
      

      お、通常メソッドだけリストで取り出すことに成功しました。

    • リスト内包表記にトライ
    • リストで for と if があると「リスト内包表記」にトライしようと思いました。

      リスト内包表記は、いろいろやってみて、以下のように理解しました。

      • リスト内包表記は、リストを作成するための表記である
      • 一番最初に来る変数は、リスト内の変数
      • forを組み合わせることで、append を使わずにリストの中に追加されていくイメージ
      • ifを使う時は、forの後にそのまま書く

      つまり、

      [ リストに追加する変数 for 変数 in リストなど if 条件式 ]

      となるわけです。

      今、上記の for と if 文を日本語にすると、

      • dir(x) の要素を ひとつ 変数method に投入
      • method に入った文字列が、’__’ から始まらなければ、method を そのままリストに追加

      としています。なので、追加されるものは method だということになります。

      ということで、

      normal_methods = [method for method in dir(x) if not method.startswith('__')
      

      と書けると理解できました。

    • 通常メソッドの数をカウント
    • その型がいくつの通常メソッドを持っているか知りたいと思いました。これは、リストの要素を数えればいいので、len()関数を使えば大丈夫です。

      len(normal_methods)
      11
      
    • 通常メソッドをひとつずつ列挙
    • これは、リストをひとつずつ改行して表示すればいいと思いました。

      for method in normal_methods:
        print(method)
      
      append
      clear
      copy
      count
      extend
      index
      insert
      pop
      remove
      reverse
      sort
      

      列挙できました。

    • 関数にまとめる
    • そうしたらこれらを関数にまとめればおしまいです。通常メソッドを取得する関数と、それを表示する関数の2つに分けることで汎用性が高まると考えました。

      取得する関数を get_normal_methods(オブジェクト)
      表示する関数を print_normal_methods(オブジェクト)

      とします。

      def get_normal_methods(obj):
        normal_methods = [method for method in dir(obj) if not method.startswith('__')]
        return normal_methods
      
      def print_normal_methods(obj):
          typename = type(obj)
          normal_methods = get_normal_methods(obj)
          num_normal_methods = len(normal_methods)
          print(f'{typename} has {num_normal_methods} methods:')
          for method in normal_methods:
              print(method)
      

      これで無事にできました。

      実際やってみます。

      print_normal_methods([1, 2])
      <class 'list'> has 11 methods:
      append
      clear
      copy
      count
      extend
      index
      insert
      pop
      remove
      reverse
      sort
      

      [1, 2] というオブジェクトは list型であり、11個のメソッドがあるということがわかりました。

    これでメソッドの一覧をすぐに作れるようになりました。リスト内包表記の最初の変数は、appendで示す内容だという気付きも大きかったです。

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください