2016-04-11 11 views
0

クラスがロードされた後にマネージャにクラスを登録する必要があります。たとえば、httpハンドラがハンドラマネージャに登録されるようにします。Pythonでクラスがロードされた後に関数を呼び出す方法はありますか?

マップ内の関係を定義する、クラス定義の後にレジスタ関数を呼び出すなど、他の方法で行うこともできます。

しかし、自動的にこれを行うより良い方法がありますか?


更新:

砂丘の答えは私のニーズを満たしながら。私は質問を改善し、同じ問題を抱えている人たちにもっと役立つようにしています。

ここに例があります。

ハンドラ/ __ init__.py

ハンドラ/ basehandler.py - classBase、HandlerManager

ハンドラ/ handlerA.py - クラスA(classBase)

ハンドラ/ hanlderB.py - ClassBの(classBase)

handlerAおよびhandlerBには、classBおよびClassBが含まれます。これらはclassBaseのサブクラスです。

クラスA//、ClassBのハンドラ/ B/

からハンドラ要求私は、ハンドラモジュールがインポートされた最初の時間で自動的にHandlerManagerにそれらを登録する必要があります。

+0

「読み込み」クラスは正確に何を意味していますか?小さな、自立した例を教えてください。 –

答えて

0

からコピーされたメタクラスのための可能な用途のように思えます。何にでもメタクラスを使用する必要はほとんどありません。ほとんどの場合、メタクラスは過度のものです。メタクラスを使用して達成できるほとんどのことは、デコレータを使用してより簡単に達成できます。ただし、このようにして、ベースハンドラのサブクラスが自動的に登録されるようにすることもできます(登録しないことを要求しない限り)。

class HandlerManager: 
    handlers = [] 
    @classmethod 
    def register(cls, handler): 
     print("registering", handler) 
     cls.handlers.append(handler) 

class HandlerRegisterer(type): 
    def __init__(self, name, bases, attrs, register=True): 
     super().__init__(name, bases, attrs) 
     if register: 
      HandlerManager.register(self) 

    def __new__(metaclass, name, bases, attrs, register=True): 
     return super().__new__(metaclass, name, bases, attrs) 

class BaseHandler(metaclass=HandlerRegisterer, register=False): 
    # not actually a real handler, so don't register this class 
    pass 

class MyHandler(BaseHandler): 
    # only have to inherit from another handler to make sure this class 
    # gets registered. 
    pass 

print(HandlerManager.handlers) 
assert BaseHandler not in HandlerManager.handlers 
assert MyHandler in HandlerManager.handlers 

あなたは抽象クラスを使用する必要がある場合、あなたはあなたのメタクラスのサブクラスABCMetaを行う必要があります。これは、抽象クラスはメタクラスを使用することで実現され、Pythonではクラスに1つのメタクラスしか持たないためです。 ABCMetaをサブクラス化することによって、2つのサブクラスを互換性のあるものにできます。

from abc import ABC, ABCMeta, abstractmethod 

class HandlerRegisterer(ABCMeta): 
    # subclass ABCMeta rather than type 
    def __init__(self, name, bases, attrs, register=True): 
     super().__init__(name, bases, attrs) 
     if register: 
      HandlerManager.register(self) 

    def __new__(metaclass, name, bases, attrs, register=True): 
     return super().__new__(metaclass, name, bases, attrs) 

class AbstractSubHandler(MyHandler, ABC, register=False): 
    # not strictly necessary to subclass ABC, but nice to know it won't 
    # screw things up 
    @abstractmethod 
    def some_method(self): 
     pass 

try: 
    AbstractSubHandler() 
except TypeError: 
    print("Can not instantiate abstract class") 

print(HandlerManager.handlers) 
assert AbstractSubHandler not in HandlerManager.handlers 
+0

この素晴らしい実装について教えていただきありがとうございます。 – Cai

1

「ロードされた」クラスが意味することは、通常は初期化されているか、呼び出されているかわかりません。

この場合、__init__メソッドまたは__call__メソッドのいずれかが使用されます。

両方を定義でき、マネージャに登録するための呼び出しを含めることができます。

クラス、具体的には__init__メソッドは、より良く記述されています。here


小さな例:

class test: 
    def __init__(self): 
     print 'I have started!' 

>>> x = test() 
I have started! 

(。ちょうどあなたの登録コードとprintを置き換え)

+0

お返事ありがとうございます。インスタンスになる前に登録されたクラスが必要です。つまり、初期化されていないことを意味します。 '' '__init__''関数は呼び出されません。 – Cai

+0

意味が分からないので[mcve]を入力する必要があります。 –

1

はい、これを自動的に行う方法があります。

インバーが示すように、__init__メソッドは、オブジェクト作成を登録する場所です。

__init__を上書きするのではなく、の既存のクラスを効果的にラップするための例です。この場合、リストクラスのラッパーを作成しました。 superを呼び出すと、元のクラスの初期化コードを使用できます。

class nlist(list): 
    """ """ 
    def __init__(self, *args, **kwargs): 
     print('making a new list!') # overwrite with call to a manager 
     super().__init__(*args) 

これがどのように見えるか:ここで(最初の時点で)「インポートされる」手段「にロードされている」

>>> list('hello') 
['h', 'e', 'l', 'l', 'o'] 
>>> nlist('hello') 
making a new list! 
['h', 'e', 'l', 'l', 'o'] 
3

場合は、そのクラスのデコレータがソリューションです。サンプルコードの下this page

registry = {} 

def register(cls): 
    registry[cls.__clsid__] = cls 
    return cls 

@register 
class Foo(object): 
    __clsid__ = "123-456" 

def bar(self): 
    pass 
関連する問題