簡體   English   中英

class 上的 python 裝飾器:TypeError:super() 參數 1 必須是類型,而不是 function

[英]python decorator on class: TypeError: super() argument 1 must be type, not function

我在類之上使用裝飾器來注冊組件。 這是我的代碼

import functools
registry = {}

def register(name=None):
    """A decorator for registering modules
    :param name: (optional) name for component
    """
    def _wrap_func(func):
        registry[name or func.__name__] = func

        @functools.wraps(func)
        def _wrap_args(*args, **kwargs):
            return func(*args, **kwargs)

        return _wrap_args
    return _wrap_func

class Base:

    def __init__(self, arg):
        self.arg = arg

@register(name="module1")
class Module1(Base):

    def __init__(self, arg):
        super(Module1, self).__init__(arg=arg)
        # super().__init__(arg=arg)

@register(name="module2")
class Module2(Base):

    def __init__(self, arg):
        super(Module2, self).__init__(arg=arg)

到目前為止很好。 registryregister按預期工作

print(registry)
# {'module1': <class '__main__.Module1'>, 'module2': <class '__main__.Module2'>}

但是,在裝飾類上調用構造函數會引發錯誤。

module1 = Module1(arg='some1')
print(module1)

Traceback (most recent call last):
  File "/tmp.py", line xx, in <module>
    module1 = Module1(arg='some1')
  File "/tmp.py", line xx, in _wrap_args
    return func(*args, **kwargs)
  File "/tmp.py", line xx, in __init__
    super(Module1, self).__init__(arg=arg)
TypeError: super() argument 1 must be type, not function

我假設functools.wraps負責隱藏裝飾器,但這里不是。 如果我將super(Module1, self).__init__(arg=arg)更改為super().__init__(arg=arg) ,它可以工作! 這是裝飾器的預期行為,還是我對registry() function 的定義有問題?


編輯:我最近發現 inheritance 壞了

@register(name="module3")
class Module3(Module1):
    pass

導致

Traceback (most recent call last):
  File "tmp.py", line xx, in <module>
    class Module3(Module1):
TypeError: function() argument 'code' must be code, not str

我知道裝飾的 class 變成了 function,但是如何解決這個問題? 它只是從文檔中不那么明顯。

  1. 我提到了PEP-3129 ,他們說有關裝飾器的詳細檢查,請參閱PEP 318 Not a single example for class decorators with arguments!

  2. 我發現的最有用的文檔是Primer on decorators他們遺漏了 class 裝飾器和 arguments

您面臨的問題是,您實際上從裝飾器返回 function 而不是 class 。 為什么不直接注冊 class 然后返回 class 本身?

registry = {}

def register(name=None):
    def inner(cls):
        registry[name or cls.__name__] = cls
        return cls

    return inner


class Base:
    def __init__(self, arg):
        self.arg = arg


@register(name="file1")
class Module1(Base):

    def __init__(self, arg):
        super(Module1, self).__init__(arg=arg)


@register()
class Module2(Base):

    def __init__(self, arg):
        print('init called from Module2')
        super(Module2, self).__init__(arg=arg)


print(registry)
print(registry['Module2'](10))

一切正常,output:

{'file1': <class '__main__.Module1'>, 'Module2': <class '__main__.Module2'>}
init called from Module2
<__main__.Module2 object at 0x000001B83B0B7FD0>

我研究@dataclass實現並找到了正確的方法。 與其他地方的文檔和指南中所說的不同,Class 裝飾器實現與 function 裝飾器略有不同——我們不需要接收參數並調用它。

這是一個有效的:

def register(cls=None, name=None):
    """
    A decorator for registering modules
    :param name: (optional) name for this component
    """
    def _wrap_cls(cls):
        registry[name or cls.__name__] = cls
        return cls
    if cls is None:
        return _wrap_cls
    else:
        return _wrap_cls(cls)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM