Python 2.5、クラスを飾るデコレーターを作成する方法はありますか?具体的には、デコレーターを使用してメンバーをクラスに追加し、コンストラクターを変更して値を取得しますそのメンバー。
次のようなものを探します( 'class Foo:'に構文エラーがあります:
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 __== '__main__':
foo1 = Foo(5,1)
print foo1.value1, foo1.getId()
foo2 = Foo(15,2)
print foo2.value1, foo2.getId()
私が本当に望んでいるのは、PythonのC#インターフェイスのようなことをする方法だと思います。私は私のパラダイムを切り替える必要があると思います。
私は、あなたが概説したアプローチの代わりにサブクラスを検討したいと思うかもしれないという考えを第二に思います。ただし、特定のシナリオ、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
コンストラクターの置き換えはおそらく少し劇的ですが、この言語はこの種の深い内省と動的な変更をサポートします。
クラスデコレータが問題の正しい解決策であるかどうかという質問とは別に:
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__
。
クラスを動的に定義できると説明した人はいません。したがって、サブクラスを定義する(および返す)デコレーターを使用できます。
_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
_
ケーキを食べることができますand食べる-継承andデコレーター!
それは良い習慣ではなく、そのためのメカニズムはありません。目的を達成する正しい方法は、継承です。
クラスのドキュメント をご覧ください。
ちょっとした例:
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)
このようにして、ボスはEmployee
が持つすべてのものと、自分の__init__
メソッドと独自のメンバー。
実際には、クラスデコレータのかなり良い実装があります:
https://github.com/agiliq/Django-parsley/blob/master/parsley/decorators.py
実際、これはかなり興味深い実装だと思います。デコレートするクラスをサブクラス化するため、isinstance
チェックなどでこのクラスとまったく同じように動作します。
さらに利点があります:__init__
ステートメントのカスタムDjangoを変更または追加するためのフォームself.fields
したがって、self.fields
が発生するafterすべての__init__
は、問題のクラスに対して実行されました。
非常に賢い。
ただし、クラスでは実際にデコレーションでコンストラクターを変更する必要がありますが、これはクラスデコレーターの良い使用例ではないと思います。
私は、継承が提起された問題により適していることに同意します。
この質問は、クラスを装飾する際に非常に便利でした。ありがとう。
Python 2.7、(および @ wraps 、元の関数のdocstringを維持するなど) ):
_def dec(klass):
old_foo = klass.foo
@wraps(klass.foo)
def decorated_foo(self, *args ,**kwargs):
print('@decorator pre %s' % msg)
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
@dec # No parentheses
class Foo...
_
多くの場合、デコレーターにパラメーターを追加します。
_from functools import wraps
def dec(msg='default'):
def decorator(klass):
old_foo = klass.foo
@wraps(klass.foo)
def decorated_foo(self, *args ,**kwargs):
print('@decorator pre %s' % msg)
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
return decorator
@dec('foo decorator') # You must add parentheses now, even if they're empty
class Foo(object):
def foo(self, *args, **kwargs):
print('foo.foo()')
@dec('subfoo decorator')
class SubFoo(Foo):
def foo(self, *args, **kwargs):
print('subfoo.foo() pre')
super(SubFoo, self).foo(*args, **kwargs)
print('subfoo.foo() post')
@dec('subsubfoo decorator')
class SubSubFoo(SubFoo):
def foo(self, *args, **kwargs):
print('subsubfoo.foo() pre')
super(SubSubFoo, self).foo(*args, **kwargs)
print('subsubfoo.foo() post')
SubSubFoo().foo()
_
出力:
_@decorator pre subsubfoo decorator
subsubfoo.foo() pre
@decorator pre subfoo decorator
subfoo.foo() pre
@decorator pre foo decorator
foo.foo()
@decorator post foo decorator
subfoo.foo() post
@decorator post subfoo decorator
subsubfoo.foo() post
@decorator post subsubfoo decorator
_
関数デコレータを使用しましたが、より簡潔であることがわかりました。クラスを装飾するクラスは次のとおりです。
_class Dec(object):
def __init__(self, msg):
self.msg = msg
def __call__(self, klass):
old_foo = klass.foo
msg = self.msg
def decorated_foo(self, *args, **kwargs):
print('@decorator pre %s' % msg)
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
_
これらの括弧をチェックし、装飾されたクラスにメソッドが存在しない場合に機能する、より堅牢なバージョン:
_from inspect import isclass
def decorate_if(condition, decorator):
return decorator if condition else lambda x: x
def dec(msg):
# Only use if your decorator's first parameter is never a class
assert not isclass(msg)
def decorator(klass):
old_foo = getattr(klass, 'foo', None)
@decorate_if(old_foo, wraps(klass.foo))
def decorated_foo(self, *args ,**kwargs):
print('@decorator pre %s' % msg)
if callable(old_foo):
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
return decorator
_
assert
は、括弧なしでデコレータが使用されていないことを確認します。存在する場合、装飾されるクラスは、デコレータのmsg
パラメータに渡され、AssertionError
が発生します。
_@decorate_if
_は、decorator
がcondition
と評価される場合にのみTrue
を適用します。
getattr
、callable
テスト、および_@decorate_if
_は、foo()
メソッドが存在するクラスに存在しない場合にデコレータが破損しないように使用されます。装飾されています。