4

私が定義したとしましょう:

def to_class(cls):
  """ returns a decorator
  aimed to force the result to be of class cls. """
  def decorating_func(func):
    def wrapper(*args, **kwargs):
      return cls(func(*args, **kwargs))
    return wrapper
  return decorator(decorating_func)

これを使用して、関数の結果を特定のクラスのオブジェクトに変換するデコレータを作成したいと思います。ただし、これは機能しません。

class TestClass(object):
  def __init__(self, value):
    self._value = (value, value)

  def __str__(self):
    return str(self._value)

  @staticmethod
  @to_test_class
  def test_func(value):
    return value

to_test_class = to_class(TestClass)

test_funcはto_test_classを検索しますが、見つかりません。一方、TestClassはまだ定義されていないため、クラス定義の前にto_test_classへの割り当てを配置しても失敗します。

@to_class(TestClass)をtest_funcの定義の上に配置しようとすると、メソッドがクラスの前に構築されるため(私が間違っていない場合)、失敗します。

私が見つけた唯一の回避策は、to_test_classをデコレータとして手動で定義することであり、一般的な「to_class」定義から返されるものとして定義することではありません。

これは基本的な例にすぎないことに注意してください。ただし、戻り値を「クラス」コンストラクターに「プラグイン」する前に変更するなど、多くのアプリケーションでto_classを使用したいと思います。他のクラスのメソッドのデコレータとしても使用したいと思います。

「to_class」デコレータは無意味だと考える人もいると思います。代わりに、装飾されたメソッド内で操作を行うことができます。とはいえ、便利で読みやすさにも役立ちます。

最後に、これは実用的な理由で20%、勉強の理由で80%興味があることを付け加えたいと思います。これは、Pythonのデコレータ全般について完全には理解していないことです。

4

3 に答える 3

4

実際、クラスの構築時には、クラスオブジェクト自体はまだ構築されていないため、デコレータのベースとして使用することはできません。

私が考えることができる1つの回避策は、デコレータを使用しないことです。staticmethod代わりに、内部で独自のデコレータで、デコレータを再利用しますclassmethod。そうすることで、Pythonが少なくとも関連するクラスを通過するようにします。

def to_class(func):
    """ returns a decorator
    aimed to force the result to be of class cls. """
    def wrapper(cls, *args, **kwargs):
        return cls(func(*args, **kwargs))
    return classmethod(wrapper)

次に、次のように使用します。

class TestClass(object):
    def __init__(self, value):
        self._value = (value, value)

    def __str__(self):
        return str(self._value)

    @to_class
    def test_func(value):
        return value

デモンストレーション:

>>> def to_class(func):
...     """ returns a decorator
...     aimed to force the result to be of class cls. """
...     def wrapper(cls, *args, **kwargs):
...         return cls(func(*args, **kwargs))
...     return classmethod(wrapper)
... 
>>> class TestClass(object):
...     def __init__(self, value):
...         self._value = (value, value)
...     def __str__(self):
...         return str(self._value)
...     @to_class
...     def test_func(value):
...         return value
... 
>>> TestClass.test_func('foo')
<__main__.TestClass object at 0x102a77210>
>>> print TestClass.test_func('foo')
('foo', 'foo')

デコレータの一般的なバージョンは簡単ではありません。難問に対する他の唯一の回避策は、メタクラスハックを使用することです。私が方法をより詳細に説明している私の別の答えを参照してください。

基本的に、構築中のクラスの名前空間にアクセスし、一時的なメタクラスを設定してから、デコレータが機能する前に、クラスのインスタンスが少なくとも1つあることに依存する必要があります。一時的なメタクラスアプローチは、クラス作成メカニズムにフックして、後で構築されたクラスを取得します。

ただし、このデコレータを代替クラスのファクトリとして使用していることを考えると、それはおそらく理想的ではありません。誰かがあなたの装飾された関数を使用してクラスインスタンスを排他的に作成した場合、メタクラスの呼び出しは遅すぎます。

于 2012-11-16T11:33:17.290 に答える
3

さて、あなたはクラスがclassmethodで装飾されたメソッドに渡される最初のパラメーターであることを忘れたので、次のように書くことができます。

def to_this_class(func):
    def wrapped(cls, value):
        res = func(cls, value)
        return cls(res)
    return wrapped

class TestClass(object):
    def __init__(self, value):
        self._value = (value, value)

    def __str__(self):
        return str(self._value)

    @classmethod
    @to_this_class
    def test_func(cls, value):
        return value

x = TestClass('a')

print x.test_func('b')
于 2012-11-16T11:35:54.313 に答える
0

問題は、デコレータがデコレートするものを定義するtest_func()ときに評価されるため、メソッドを定義するときにデコレータto_test_classが呼び出され、デコレータがすでに存在していても、動作するもの(クラスTestClass)がまだ存在しないことです(これはすべてのメソッドが作成された後に作成されます)。

たぶん、クラスが使用されるポイントでプレースホルダーを使用し、後で(クラスが作成された後)プレースホルダーのポイントでその値(クラス)を入力することができます。

例:

lazyClasses = {}
def to_lazy_class(className):
  """ returns a decorator
  aimed to force the result to be of class cls. """
  def decorating_func(func):
    def wrapper(*args, **kwargs):
      return lazyClasses[className](func(*args, **kwargs))
    return wrapper
  return decorating_func 

class TestClass(object):
  def __init__(self, value):
    self._value = (value, value)

  def __str__(self):
    return str(self._value)

  @staticmethod
  @to_lazy_class('TestClass')
  def test_func(value):
    return value

lazyClasses['TestClass'] = TestClass

>>> TestClass.test_func('hallo')
<__main__.TestClass object at 0x7f76d8cba190>
于 2012-11-16T11:33:08.520 に答える