Saya menggunakan dekorator di atas kelas untuk mendaftarkan komponen. Ini kode saya

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)

Sejauh ini bagus. registry dan register berfungsi seperti yang diharapkan

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

Tapi, memanggil konstruktor pada kelas yang didekorasi menimbulkan kesalahan.

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

Saya berasumsi functools.wraps berusaha menyembunyikan dekorator, tapi ini bukan. Jika saya mengubah super(Module1, self).__init__(arg=arg) menjadi super().__init__(arg=arg), itu berhasil! Apakah ini perilaku yang diharapkan dengan dekorator atau ada yang salah dengan definisi saya tentang fungsi registry()?


EDIT: Saya baru-baru ini menemukan bahwa warisan rusak

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

Mengarah ke

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

Saya mengerti bahwa kelas yang didekorasi menjadi fungsi, tetapi bagaimana cara memperbaikinya? Hanya saja tidak begitu jelas dari dokumentasi.

  1. Saya telah merujuk ke PEP-3129 dan mereka berkata Untuk pemeriksaan mendetail tentang dekorator, lihat PEP 318< /em> Bukan satu contoh untuk dekorator kelas dengan argumen!

  2. Dokumentasi paling berguna yang saya temukan adalah Primer pada dekorator Mereka mengabaikan dekorator kelas dengan argumen.

1
Thamme Gowda 16 Mei 2021, 10:38

2 jawaban

Jawaban Terbaik

Saya mempelajari implementasi @dataclass dan menemukan cara yang benar. Tidak seperti yang disebutkan dalam dokumen dan panduan di tempat lain, implementasi dekorator kelas adalah sedikit berbeda dari fungsi dekorator -- kita tidak perlu menerima argumen dan menyebutnya.

Inilah yang berfungsi:

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)
-1
Thamme Gowda 17 Mei 2021, 22:43

Masalah yang Anda hadapi adalah, Anda sebenarnya mengembalikan fungsi dari dekorator Anda bukan kelas lagi. Mengapa Anda tidak mendaftarkan kelasnya saja lalu mengembalikan kelas itu sendiri?

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))

Semuanya berfungsi dengan baik, keluaran:

{'file1': <class '__main__.Module1'>, 'Module2': <class '__main__.Module2'>}
init called from Module2
<__main__.Module2 object at 0x000001B83B0B7FD0>
1
SorousH Bakhtiary 17 Mei 2021, 23:11