今日の記事では、与えられたクラスのすべてのメソッドを見つける方法について見ていきます。
クラスのすべてのメソッドを直接リストアップして、特定のメソッドに基づいて何らかの前処理を行うことができれば、非常に便利な場合がよくあります。
さっそく始めてみましょう。
この方法を実現するための方法をいくつか紹介しますので、以下の方法のどれかを使ってください。
テンプレートクラスを定義する
まず、ダミーのクラスを定義して、そこから出力を検証してみましょう。
以下のようなクラスがあり、いくつかのメソッドを持っています。
class MyClass(object):
def __init__(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = a
def add(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state + a
return self.state
def subtract(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state - a
return self.state
def multiply(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state * a
return self.state
def divide(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state / a
return self.state
|
このクラスは浮動小数点数の state 属性を保持し、算術演算で操作することができます。
ここで、カスタムクラスのメソッドをリストアップする方法をいくつか見てみましょう。
方法1 – dir() 関数を使ってクラス内のメソッドをリストアップする
このクラスのメソッドをリストアップするために、Pythonのdir()関数を使うのも一つの方法です。
dir()`関数は、クラスのすべての関数とプロパティを返します。
MyClass` に対して試してみるとどうなるか見てみましょう。
print(dir(MyClass))
|
結果は以下の通りです。
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'add',
'divide',
'multiply',
'subtract']
|
OK、add、divide、subtract、multiply のメソッドがリストアップされましたね。
これらのメソッド (ダブルアンダースコアで始まるもの) はダンダーメソッドと呼ばれます。
これらのメソッドは通常ラッパー関数から呼び出されます。
例えば、 dict() 関数は __dict__() メソッドを呼び出します。
ダンダーのメソッドを出力からフィルタリングする
通常、ダブルアンダースコアの接頭辞を持つメソッドは必要ないので、以下のスニペットでフィルタリングします。
method_list = [method for method in dir(MyClass) if method.startswith('__') is False]
print(method_list)
|
結果は以下の通りです。
['add', 'divide', 'multiply', 'subtract']
|
なんと これで、必要な算術演算メソッドだけを取得できるようになりました。
しかし、今の解決策には問題があります。
dir()` がクラスのメソッドとプロパティの両方を呼び出すことを覚えていますか?
クラスのプロパティを扱う
クラスの中にプロパティがあった場合、それもリストアップされます。
次のような例で考えてみましょう。
class MyClass(object):
# MyClass property
property1 = [1, 2, 3]
def __init__(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = a
def add(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state + a
return self.state
def subtract(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state - a
return self.state
def multiply(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state * a
return self.state
def divide(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state / a
return self.state
@staticmethod
def global_method(a, b):
return a + b
@classmethod
def myclass_method(cls):
return cls
method_list = [method for method in dir(MyClass) if method.startswith('_') is False]
print(method_list)
|
さて、出力はどうなるだろうか?
結果は以下の通りです。
['add', 'divide', 'global_method', 'multiply', 'myclass_method', 'property1', 'subtract']
|
これでは、property1も出力されますが、これは私たちが欲しいものではありません。
メソッドとプロパティを区別するために、もう1つフィルタを作る必要があります。
しかし、これは本当に簡単なことです。
主な違いは、プロパティオブジェクトは呼び出すことができませんが、メソッドは呼び出すことができるということです。
Pythonでは、ブーリアン関数 callable(attribute) を使って、その属性が呼び出し可能かどうかをチェックすることができます。
では、これを古いコードに組み込んでみましょう。
method_list = [attribute for attribute in dir(MyClass) if callable(getattr(MyClass, attribute)) and attribute.startswith('__') is False]
print(method_list)
|
これを分解して、リスト内包を除いて書いてみましょう。
method_list = []
# attribute is a string representing the attribute namefor attribute in dir(MyClass):
# Get the attribute value
attribute_value = getattr(MyClass, attribute)
# Check that it is callable
if callable(attribute_value):
# Filter all dunder (__ prefix) methods
if attribute.startswith('__') == False:
method_list.append(attribute)
print(method_list)
|
また、methodをattributeに変更することで、誤解を招くような意図を排除しています!
では、テストしてみましょう。
結果は以下の通りです。
['add', 'divide', 'global_method', 'multiply', 'myclass_method', 'subtract']
|
確かに、プロパティを除いたメソッドのリストが表示されました。
この記事もチェック:Pythonのクラス属性とインスタンス属性の違いや使い方について詳しく解説する
方法2 – optparse.OptionParserの使用
さて、これは dir() を使うことにあまり抵抗がない場合に使える、別の方法です。
メソッドをリストアップするために、 inspect モジュールを使用することができます。
つまり、 inspect.getmembers(instance, predicate=inspect.ismethod) を使って、メソッドのリストを取得します。
これは自動的に作業をしてくれるので、あなたは出力を処理するだけでよいのです。
例を見てみましょう。
import inspect
class MyClass(object):
# MyClass property
property1 = [1, 2, 3]
def __init__(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = a
def add(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state + a
return self.state
def subtract(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state - a
return self.state
def multiply(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state * a
return self.state
def divide(self, a):
assert isinstance(a, float) or isinstance(a, int)
self.state = self.state / a
return self.state
@staticmethod
def global_method(a, b):
return a + b
@classmethod
def myclass_method(cls):
return cls
# Create our instanceinstance = MyClass(100)
# Get the list of functionsmethod_list = inspect.getmembers(MyClass, predicate=inspect.ismethod)
print(method_list)
|
結果は以下の通りです。
[('__init__',
<bound method MyClass.__init__ of <__main__.MyClass object at 0x000001E55E36F390>>),
('add',
<bound method MyClass.add of <__main__.MyClass object at 0x000001E55E36F390>>),
('divide',
<bound method MyClass.divide of <__main__.MyClass object at 0x000001E55E36F390>>),
('multiply',
<bound method MyClass.multiply of <__main__.MyClass object at 0x000001E55E36F390>>),
('myclass_method',
<bound method MyClass.myclass_method of <class '__main__.MyClass'>>),
('subtract',
<bound method MyClass.subtract of <__main__.MyClass object at 0x000001E55E36F390>>)]
|
各タプルの最初の要素を取得することで、メソッド名を得ることができる。
inspect モジュールを使用する際の注意点
タプルのリストが得られることに注意してください。
タプルの最初の要素は関数名で、2番目の要素はメソッドオブジェクトそのものを表しています。
これは良い解決策のように見えますが、いくつかのことに気がつくかもしれません。
-
dir()では、クラス名そのものを直接使っていました。しかし、ここではインスタンスを渡す必要があります。 - staticmethodsもリストには表示されません。ユースケースによっては、必要ないかもしれません。
以上の点から、シンプルに dir() 関数を使うことをお勧めします!
まとめ
この記事では、Pythonで与えられたクラスのすべてのメソッドをリストアップする方法を見ました。