装饰一个构造函数有参数的类

时间:2016-11-02 12:56:12

标签: python python-decorators

如何修饰构造函数具有参数的类?这是我的代码;

angular.module('app', ['ngMessages']);

这个想法是在基类中的dict中注册子类的实例。

当我运行代码时,我收到以下错误

# Base Class
class Model(object):
    models = {}
    def __init__(self):
        pass

# decorator
def register(cls):
    Model.models[cls.__name__] = cls()

#Subclasses

@register
class PaperModel(Model):
    def __init__(self, paper):
        self.paper = paper

@register
class WoodenModel(Model):
    def __init__(self, wood):
        self.wood = wood

但是,如果我删除子类(PaperModel& WoodenModel)构造函数中的参数,代码就可以工作。

4 个答案:

答案 0 :(得分:4)

如果要注册带注释的子类的实例,注释必须返回一些将创建实例并注册它们的内容。

顺便说一下,如果你使用一个简单的字典,你只会注册每个子类的最后一个创建的实例。如果要注册所有实例,最好使用defaultdict(list)

以下是代码示例:

class Model(object):
    models = collections.defaultdict(list)


def register(cls):
    def registrar(*args, **kwargs):
        # first create the instance with the passed parameters
        instance = cls(*args, **kwargs) 
        # then register the instance and return it
        Model.models[cls.__name__].append(instance)
        return instance
    return registrar

@register
class PaperModel(Model):
    def __init__(self, paper):
        self.paper = paper

此代码可以注册不同的实例:

>>> p1 = PaperModel('A4')
>>> p1
<__main__.PaperModel object at 0x02AA0FB0>
>>> p2 = PaperModel('A3')
>>> p2
<__main__.PaperModel object at 0x02AA0F10>
>>> Model.models
defaultdict(<type 'list'>, {'PaperModel': [<__main__.PaperModel object at 0x02AA0FB0>, <__main__.PaperModel object at 0x02AA0F10>]})

或者,您根本无法使用注释,并使用基类的特殊__new__方法来获得相同的结果:

class Model(object):
    models = collections.defaultdict(list)
    def __new__(cls, *args, **kwargs):
        inst = super(Model, cls).__new__(cls)
        inst.__init__(*args, **kwargs)
        Model.models[cls.__name__].append(inst)
        return inst


class PaperModel(Model):
    def __init__(self, paper):
        self.paper = paper

此代码也可以注册实例:

>>> p1 = PaperModel('A4')
>>> p2 = PaperModel('A3')
>>> Model.models
defaultdict(<type 'list'>, {'PaperModel': [<__main__.PaperModel object at 0x02AA02D0>, <__main__.PaperModel object at 0x02AA03F0>]})
>>> Model.models['PaperModel'][0] is p1
True
>>> Model.models['PaperModel'][1] is p2
True

答案 1 :(得分:3)

只有在实例未实例化时才应注册实例:

def register(cls):
    def wrapper(*args, **kwargs):
        inst = cls(*args, **kwargs)
        Model.models[cls.__name__] = inst
        return inst
    return wrapper

请注意,您还需要从装饰器返回该类,否则您的装饰者将返回None,当您尝试在课程中使用装饰器时,您将获得TypeError

wm = WoodenModel('log')
print(wm)
# <__main__.WoodenModel object at 0x033A6730>
print(Model.models)
# {'WoodenModel': <__main__.WoodenModel object at 0x033A6730>}

答案 2 :(得分:2)

您需要instance,即类的对象(不是类本身)要在基类中注册。更清洁的方法是在父类的__init__中注册实例,因为子类是从它派生的。为此创建特定的装饰器是没有意义的。装饰器用于通用,例如,如果Parent类也是动态的。您的装饰者应该注册以下内容:<SomeClass>.models[<some_sub_class>]

以下是在父__init__中注册的示例代码:

# update the entry the __init__() of parent class
class Model(object):
    models = {}
    def __init__(self):
        Model.models[self.__class__.__name__] = self # register instance

class WoodenModel(Model):
    def __init__(self, wood):
        self.wood = wood
        super(self.__class__, self).__init__()  # Make a call to parent's init()

# Create a object
wooden_model_obj = WoodenModel(123)

print wooden_model_obj
# prints: <__main__.WoodenModel object at 0x104b3e990>
#                                            ^

print Model.models
# prints: {'WoodenModel': <__main__.WoodenModel object at 0x104b3e990>}
#                                                            ^

# Both referencing same object

如果你想要一个通用装饰器来实现这一点,假设:

  • 子类只有一个父
  • 存储该值的属性在perent类
  • 中为models

样本装饰器将如下:

def register(cls):
    def register_wrapper(*args, **kwargs):
        obj = cls(*args, **kwargs)
        obj.__class__.__bases__[0].models[cls.__name__] = obj
        # "obj.__class__.__bases__[0]" will return first inherited parent class
        return obj
    return register_wrapper

答案 3 :(得分:0)

您可以注册类型而不是实例(您不知道如何构造,您没有正确的参数可用):

Model.models[cls.__name__] = cls # note no parens