【问题标题】:Is there a way to call a function after a class was loaded in python?有没有办法在 python 中加载类后调用函数?
【发布时间】:2016-08-01 16:11:28
【问题描述】:

我想在加载类后将类注册到管理器,就像 http 处理程序注册到处理程序管理器一样。

也可以通过其他方式完成,比如在map中定义关系,或者在类定义后调用注册函数。

但是有没有更好的方法来自动执行此操作?


更新:

虽然沙丘的回答满足了我的需要。我正在努力改进这个问题,让它对遇到同样问题的其他人更有用。

这里是例子。

处理程序/__init__.py

handler/basehandler.py - classBase, HandlerManager

handler/handlerA.py - classA(classBase)

handler/hanlderB.py - classB(classBase)

handlerA 和 handlerB 包含 classA 和 classB,它们是 classBase 的子类。

classA 处理来自 /a/ 的请求,classB 处理 /b/

我需要在第一次导入处理程序模块时自动将它们注册到HandlerManager。

【问题讨论】:

  • 你所说的“加载”类是什么意思 - 你能举一个小而独立的例子吗?

标签: python


【解决方案1】:

如果这里的“正在加载”意味着“正在导入”(第一次),那么类装饰器是一种解决方案。下面的示例代码是从this page复制而来的

registry  =  {}

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

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

def bar(self):
    pass

【讨论】:

    【解决方案2】:

    似乎可以用于元类。很少需要对任何事情使用元类——它们对几乎所有事情都太过分了。使用元类可以实现的大多数事情都可以使用装饰器更容易地实现。但是,通过这种方式,您可以确保您的基本处理程序的任何子类也将自动注册(除非它要求不注册)。

    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只允许一个类有一个元类。通过将 ABCMeta 子类化,您可以使两个子类兼容(其中任何一个中都没有与另一个冲突的代码)。

    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
    

    【讨论】:

    • 非常感谢您让我知道这个出色的实现。
    【解决方案3】:

    我不确定您所说的“加载”类通常是初始化或调用的。

    在这种情况下,使用__init__ 方法或__call__ 方法。

    两者都可以定义,并且可以包括在管理器中注册的调用。

    类,特别是 __init__ 方法在 here 的描述更好。


    小例子:

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

    (只需将print 替换为您的注册码即可。)

    【讨论】:

    • 感谢您的回答。我需要在成为实例之前注册类,这意味着它没有被初始化,__init__ 函数没有被调用。
    • 您需要提供minimal reproducible example,因为您没有任何意义。
    【解决方案4】:

    是的,有一种方法可以自动执行此操作。

    正如 Inbar 所建议的,__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']
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-22
      • 1970-01-01
      • 1970-01-01
      • 2020-08-14
      • 2022-08-05
      • 2021-06-29
      • 2012-12-26
      • 2022-06-28
      相关资源
      最近更新 更多