2017-05-11 3 views
3

私はこの問題の解決方法をウェブで検索してきましたが、エレガントなものは何も見つかりませんでした。pythonデコレータ - ベース内にクラスを登録する

from abc import ABCMeta 


class Base(object): 
    __metaclass__ = ABCMeta 

    def __init__(self, name): 
     self._name = name 
     self._content = {} 

    def run_them_all(self): 
     for key, content in self._content.items(): 
      print(key, content) 

    # This should be the registery function 
    def register(old_method, key): 
     self._content[key] = old_method() 


class Derived(Base): 
    @Base.register("some other content") 
    def do_something(self): 
     return {"name": "yes"} 

    @Base.register("some_content") 
    def do_something_else(self): 
     return {"hi": "ho"} 

    def this_should_not_be_registered(self): 
     return "yooo" 


derived = Derived("John") 
derived.run_them_all() 

がこれをachievすることが可能です:

は、私は、派生クラスがそのメソッドを登録するためにデコレータとして使用することができますレジストリを持つ基本クラスを考えてみましょうか? 通常のデコレータを使用すると、迂回路を作成するために明示的な関数呼び出しが必要になります。しかし、私はこの呼び出しを後で使用するために登録するか、または少なくともそれらのメソッドを自分で呼び出すことなく、後で使用する戻り値を登録するだけです。私は単純に、このようrun_them_allをオーバーライド避けたい

{"some other content": {"name": "yes"}} 
{"some content": {"hi": "ho"}} 

class Derived(Base): 
    ... 

    def run_them_all(self): 
     self._content["some_content"] = self.do_something_else() 
     ... 
     return self._content 
+1

あなたの説明がほとんど意味をなさない問題を明確にして、これを処理するためにデコレータクラスの代わりにデコレータ関数を使用することができない理由は何もありません。また、実際にここに何かを飾ることはありません。 – snb

+1

@snb私は仕事の装飾機能を書いていませんでした。しかし、例を見ると、関数を自分で呼び出すことなく、自分の結果を単なるdictの中に保存する自己認識デコレータを作成したいと思うのは簡単です。 1つの解決策は反射を使用することですが、私は人々がより良い方法を知っているかどうかを知りたいと思います。 – Crippin

+0

いいえ「機能を呼び出すことなく、結果をディクテーション内に保存するための自己認識デコレータを作成する」ことを理解していないと簡単に理解できません。また、あなたが何かを装飾していないと言ったとき、あなたの構文のどれも実際に装飾を意味するものではないことを意味しました。あなたの例では何も変更せずに関数を返さないというJBernardoの答えを見てください。 krzysztofzuraw.com/blog/2016/python-class-decorators.html – snb

答えて

0

は、まあ、私はこれを解決する方法を見つけた(代わりにメソッド自体の)selfkeyoutput方法からの出力が走っている:

class BlaBla(Base): 
    ... 

    @register 
    def do_something(self): 
     return {"name": "yes"} 

はまた、あなたのBase.register機能は3つの引数を受け取る必要がありますしかし、これはエレガントではありません。

私は、派生クラスが作成されたときにインスタンス変数を追加しますメタクラスを作成しました:これは私たちのデコレータである

import types 


class MetaTemplateData(type): 
    def __new__(mcs, name, base, dct): 
     orig_init = dct.get("__init__") 
     decorators = [] 
     for _, value in dct.items(): 
      if isinstance(value, types.FunctionType): 
       if value.__name__ == "_content_wrapper": 
        decorators.append(value) 
      elif isinstance(value, staticmethod): 
       function = value.__func__ 
       if function.__name__ == "_content_wrapper": 
        decorators.append(function) 

     def init_wrapper(self, *args, **kwargs): 
      if orig_init: 
       orig_init(self, *args, **kwargs) 

      # pylint: disable=protected-access 
      self._callbacks = getattr(self, "_callbacks", []) 
      self._callbacks.extend(decorators) 
     dct["__init__"] = init_wrapper 
     return type.__new__(mcs, name, base, dct) 

、これは、関数を呼び出して確認する静的であるか、ない場合にその結果を格納します新しい辞書:

:私たちは私たちのために、すべての装飾のメンバーを呼び出すの世話をされ、このように基本クラスを作成し、辞書内でその結果を保存することができるよりも

def add_content(key): 
    def register_decorator(function): 
     def _content_wrapper(self=None, *args, **kwargs): 
      num_args = function.__code__.co_argcount 
      if isinstance(function, types.FunctionType): 
       if self and num_args != 0: 
        data = function(self, *args, **kwargs) 
       else: 
        data = function(*args, **kwargs) 
      else: 
       data = function.__func__() 
      return {key: data} 
     return _content_wrapper 
    return register_decorator 

最後に、我々はコンテンツを追加するの世話をする派生クラスを出す:私たちは、派生クラスのインスタンスを作成するときに

class Derived(Base): 
    def __init__(self, name, email): 
     super().__init__(email) 
     self._name = name 

    @add_content("key_number_one") 
    def something(self): 
     return { 
      "email": self._email 
     } 

    @add_content("key_two") 
    def something_else(self): 
     return { 
      "email": "DAEMAIL" + self._email 
     } 

    @add_content("key_for_static") 
    @staticmethod 
    def _do_private_things(): 
     return { 
      "oh lord": "hoho" 
     } 

は今、魔法が起こる:

derived = Derived("John", "[email protected]") 
content = derived.parse() 
print(content) 

結果:

{key_for_static ':{' oh lord ':' hoho '}、' key_number_one ':{' email ':' [email protected] '}、' key_two ':{' email ':' DAEMAILsome @emai l.com '}}

外部からすべてのメンバーに電話をかけても、すべてが期待通りに機能します。

1

なぜ単に外の関数としてデコレータを定義していない(それは内部に行うことができ

これが生じるはずですクラスが、面倒に感じる):

def register(key): 
    def register_decorator(function): 
     def fn(self, *args, **kw): 
      out = function(self, *args, **kw) 
      self.register(key, out) 
     return fn 
    return register_decorator 

そして、このような使用のこと:、

+0

これはうまくいかず、出力が得られません。あなたはこれを自分で試しましたか? – Crippin

+0

私はdo_something()への明示的な呼び出しを避けたいと思います。これはdo_something()を呼び出す場合にのみ有効です。デコレータを登録するときや、run_them_all()を呼び出すときにdo_somethingへの暗黙の呼び出しが必要です。 – Crippin

関連する問題