152

Python 2.5では、クラスを装飾するデコレータを作成する方法はありますか?具体的には、デコレータを使用してメンバーをクラスに追加し、コンストラクタを変更してそのメンバーの値を取得したいと思います。

次のようなものを探しています(「classFoo:」で構文エラーがあります:

def getId(self): return self.__id

class addID(original_class):
    def __init__(self, id, *args, **kws):
        self.__id = id
        self.getId = getId
        original_class.__init__(self, *args, **kws)

@addID
class Foo:
    def __init__(self, value1):
        self.value1 = value1

if __name__ == '__main__':
    foo1 = Foo(5,1)
    print foo1.value1, foo1.getId()
    foo2 = Foo(15,2)
    print foo2.value1, foo2.getId()

私が本当に求めているのは、PythonでC#インターフェイスのようなことをする方法だと思います。私は自分のパラダイムを切り替える必要があると思います。

4

8 に答える 8

220

クラスデコレータが問題の正しい解決策であるかどうかという質問は別として、次のようになります。

Python 2.6以降では、@-syntaxを使用したクラスデコレータがあるため、次のように記述できます。

@addID
class Foo:
    pass

古いバージョンでは、別の方法でそれを行うことができます。

class Foo:
    pass

Foo = addID(Foo)

ただし、これは関数デコレータの場合と同じように機能し、デコレータは新しい(または変更された元の)クラスを返す必要があることに注意してください。これは、例で行っていることではありません。addIDデコレータは次のようになります。

def addID(original_class):
    orig_init = original_class.__init__
    # Make copy of original __init__, so we can call it without recursion

    def __init__(self, id, *args, **kws):
        self.__id = id
        self.getId = getId
        orig_init(self, *args, **kws) # Call the original __init__

    original_class.__init__ = __init__ # Set the class' __init__ to the new one
    return original_class

次に、上記のようにPythonバージョンに適切な構文を使用できます。

しかし、オーバーライドしたい場合は継承がより適しているという他の人たちに同意します__init__

于 2009-03-25T15:58:22.050 に答える
86

あなたが概説したアプローチの代わりに、サブクラスを検討したいかもしれないという考えを支持します。ただし、特定のシナリオを知らない場合、YMMV :-)

あなたが考えているのはメタクラスです。メタクラスの__new__関数には、クラスの提案された完全な定義が渡され、クラスが作成される前に書き直すことができます。その時点で、コンストラクターを新しいコンストラクターにサブアウトできます。

例:

def substitute_init(self, id, *args, **kwargs):
    pass

class FooMeta(type):

    def __new__(cls, name, bases, attrs):
        attrs['__init__'] = substitute_init
        return super(FooMeta, cls).__new__(cls, name, bases, attrs)

class Foo(object):

    __metaclass__ = FooMeta

    def __init__(self, value1):
        pass

コンストラクターの置き換えはおそらく少し劇的ですが、言語はこの種の深い内省と動的な変更をサポートしています。

于 2009-03-25T15:14:34.433 に答える
28

クラスを動的に定義できるとは誰も説明していません。したがって、サブクラスを定義 (および返す) デコレータを使用できます。

def addId(cls):

    class AddId(cls):

        def __init__(self, id, *args, **kargs):
            super(AddId, self).__init__(*args, **kargs)
            self.__id = id

        def getId(self):
            return self.__id

    return AddId

これは Python 2 で使用できます (2.6 以降でこれを継続する必要がある理由を説明する Blckknght からのコメント) は、次のようになります。

class Foo:
    pass

FooId = addId(Foo)

Python 3 では次のようになります (ただし、クラスでの使用には注意しsuper()てください)。

@addId
class Foo:
    pass

ケーキを持って食べることができます - 継承デコレータ!

于 2013-09-21T21:47:44.297 に答える
14

それは良い習慣ではなく、そのため、それを行うメカニズムはありません。あなたが望むことを達成する正しい方法は継承です。

クラスのドキュメントを参照してください。

ちょっとした例:

class Employee(object):

    def __init__(self, age, sex, siblings=0):
        self.age = age
        self.sex = sex    
        self.siblings = siblings

    def born_on(self):    
        today = datetime.date.today()

        return today - datetime.timedelta(days=self.age*365)


class Boss(Employee):    
    def __init__(self, age, sex, siblings=0, bonus=0):
        self.bonus = bonus
        Employee.__init__(self, age, sex, siblings)

このように、Boss にはすべてEmployeeがあり、独自の__init__メソッドと独自のメンバーも備えています。

于 2009-03-25T15:01:38.100 に答える
5

実際には、クラス デコレータの非常に優れた実装がここにあります。

https://github.com/agiliq/Django-parsley/blob/master/parsley/decorators.py

実際、これはかなり興味深い実装だと思います。isinstance修飾するクラスをサブクラス化するため、チェックなどではこのクラスとまったく同じように動作します。

__init__追加の利点があります。カスタム django フォームのステートメントが変更または追加されることは珍しくありません。そのため、問題のクラスに対してすべてが実行されたself.fieldsに変更が行われる方が適切ですself.fields__init__

非常に賢い。

ただし、クラスでは、実際には装飾によってコンストラクターを変更する必要があります。これは、クラスデコレーターの適切な使用例ではないと思います。

于 2013-01-28T15:05:42.810 に答える
0

クラスのパラメーターを返すという質問に答える例を次に示します。さらに、継承の連鎖も尊重します。つまり、クラス自体のパラメータのみが返されます。関数get_paramsは簡単な例として追加されていますが、検査モジュールのおかげで他の機能を追加できます。

import inspect 

class Parent:
    @classmethod
    def get_params(my_class):
        return list(inspect.signature(my_class).parameters.keys())

class OtherParent:
    def __init__(self, a, b, c):
        pass

class Child(Parent, OtherParent):
    def __init__(self, x, y, z):
        pass

print(Child.get_params())
>>['x', 'y', 'z']

于 2019-12-20T10:16:09.477 に答える