web-dev-qa-db-ja.com

Pythonのパラメータによるクラスの拡張

クラスFooをクラスBarで展開したいのですが、通常の方法(class Foo(Bar))で展開できないという問題があります。クラスBarは多少動的に生成されます。

私はこの小さな例を作って、私の望ましい結果を説明しました。

class Bar:
    def super_cool_function():
        print("Cool")

class Foo:
    def __init__(self, another_class):
        # I want to extend Foo by another_class

# Desired result
foobar = Foo(Bar)
foobar.super_cool_function()

もう一度これはnotです:

class Foo(Bar):
    pass

foobar = Foo()
foobar.super_cool_function()
15
Ivy F.

「クラスBarは多少動的に生成されます」それは問題ありません...ブループリント(Fooによって拡張されるクラスの)の青写真に従っている限り、pythonクロージャはこちら。内部でクラスを作成し、関数からそれを返すことにより、新しいクラスを動的に作成します。

def get_class(superclass):
    class Foo(superclass):
        def __init__(self, ...):
           ...

    return Foo

DynamicFoo = get_class(Bar)
myobj = DynamicFoo()

これは、pythonでよく見られるパターンです。クロージャを利用して、コールバックとクラスを動的に作成します。


上記の答えは、Barが実際に定義されていない場合でも、正しく定義されていることを前提としています。 super_cool_functionにはセルフパラメータがありません。インスタンスメソッドは常に最初のパラメーター(インスタンス自体)が最初の属性として自動的に渡されて呼び出されます。

したがって、Barの正しい定義は次のようになります。

class Bar:
   def super_cool_function(self):
       print("Cool")

次に、内部クラスFooの最も単純な定義でget_classを定義します。

def get_class(superclass):
    class Foo(superclass):
        pass

    return Foo

DynamicFoo = get_class(Bar)
myobj = DynamicFoo()
myobj.super_cool_function()
# Cool
22
cs95

あなたの望ましい使い方は少し奇妙です:

foobar = Foo(Bar)

Fooクラスオブジェクトを渡してBarインスタンスを作成し、Barインスタンスのように動作するものを取得することを期待しています。通常、プロキシクラスは、引数なしでオブジェクトを構築するだけでなく、プロキシするオブジェクトを取得するか、どこかをルックアップするように設計されています。

しかし、それは__init__オブジェクトを構築するメソッド。これは単なる標準のプロキシクラスです。そう:

class Foo:
    def __init__(self, cls):
        self._inst = cls()
    def __getattr__(self, name):
        return getattr(self._inst, name)
    def __setattr__(self, name, value):
        if name in {'_inst'}:
            super().__setattr__(name, value)
        else:
            setattr(self._inst, name, value)
    def __delattr__(self, name):
        delattr(self._inst, name)

もちろん、それを呼び出すことはできませんsuper_cool_functionfoobarに対してBarインスタンスよりも多く、メソッドとして定義されており、selfパラメータがないためです。ただし、Fooインスタンスから取得したのと同じエラーがBarインスタンスから取得されます。

>>> foobar.super_cool_function
<bound method Bar.super_cool_function of <__main__.Bar object at 0x129f95080>>
>>> foobar.super_cool_function()
TypeError: super_cool_function() takes 0 positional arguments but 1 was 
4
abarnert