0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python で不要なキーワード引数をフィルタする方法

Posted at

いちごとバナナを必要とするケーキがあるとします。このケーキに白菜まで渡すとエラーになります。

エラーになる例
class Cake:
    def __init__(self, ichigo, banana):
        self.ichigo = ichigo
        self.banana = banana

Cake(ichigo=1, banana=2)  # OK
Cake(ichigo=1, banana=2, hakusai=4)  # NG
# TypeError: Cake.__init__() got an unexpected keyword argument 'hakusai'

しかし、場合によっては渡す側から何が必要かわからないのでとにかく全部渡したいということもあると思います。その場合は、ケーキ側で必要なもの一覧 argnames をもっておき、必要でないものはフィルタしてからコンストラクタを呼ぶクラスメソッド create() を用意する手があると思います (以下)。クラスメソッドは基底クラスにさえ書いておけば派生クラスでも利用できます。

エラーにならない例 (引数を手書き)
class Cake:
    argnames = {'ichigo', 'banana'}
    def __init__(self, ichigo, banana):
        self.ichigo = ichigo
        self.banana = banana
    @classmethod
    def create(cls, **kwargs):
        filtered = {k: v for k, v in kwargs.items() if k in cls.argnames}
        return cls(**filtered)

class ChocolateCake(Cake):
    argnames = {'ichigo', 'banana', 'chocolate'}
    def __init__(self, ichigo, banana, chocolate):
        super().__init__(ichigo, banana)
        self.chocolate = chocolate

Cake.create(ichigo=1, banana=2)  # OK
Cake.create(ichigo=1, banana=2, hakusai=4)  # OK
ChocolateCake.create(ichigo=1, banana=2, chocolate=3, hakusai=4)  # OK

必要なもの一覧 argnames を自分で書きたくない場合は、初回の create() 時に標準ライブラリ inspcet でコンストラクタのシグネチャ (引数情報) を取得してセットする手があります (以下)。

エラーにならない例 (引数を inspect で自動取得)
import inspect

class Cake:
    def __init__(self, ichigo, banana):
        self.ichigo = ichigo
        self.banana = banana
    @classmethod
    def create(cls, **kwargs):
        # if not hasattr(cls, 'argnames'):  # This is wrong.
        if 'argnames' not in cls.__dict__:
            cls.argnames = set(inspect.signature(cls.__init__).parameters.keys())
            cls.argnames.remove('self')
        filtered = {k: v for k, v in kwargs.items() if k in cls.argnames}
        return cls(**filtered)

class ChocolateCake(Cake):
    def __init__(self, ichigo, banana, chocolate):
        super().__init__(ichigo, banana)
        self.chocolate = chocolate

Cake.create(ichigo=1, banana=2)  # OK
Cake.create(ichigo=1, banana=2, hakusai=4)  # OK
ChocolateCake.create(ichigo=1, banana=2, chocolate=3, hakusai=4)  # OK

このとき、argnames をセット済みかは cls.__dict__ で確認する必要があります。もし hasattr() で判定すると、チョコレートケーキ用の argnames がセット済みか確認するのに、基底クラスのケーキまで見にいってしまいます (ので、ケーキが先にインスタンス化された場合にチョコレートケーキ用の argnames をセットできなくなります)。cls.__dict__ を見ればチョコレートケーキ自体にセットされているかを確認できます。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?