Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

180
Visualizações
¿Cómo puedo documentar métodos heredados de una metaclase?

Considere las siguientes definiciones de metaclase/clase:

 class Meta(type): """A python metaclass.""" def greet_user(cls): """Print a friendly greeting identifying the class's name.""" print(f"Hello, I'm the class '{cls.__name__}'!") class UsesMeta(metaclass=Meta): """A class that uses `Meta` as its metaclass."""

Como sabemos , definir un método en una metaclase significa que es heredado por la clase y puede ser utilizado por la clase. Esto significa que el siguiente código en la consola interactiva funciona bien:

 >>> UsesMeta.greet_user() Hello, I'm the class 'UsesMeta'!

Sin embargo, una desventaja importante de este enfoque es que se pierde cualquier documentación que hayamos incluido en la definición del método. Si help(UsesMeta) en la consola interactiva, vemos que no hay ninguna referencia al método greet_user , y mucho menos a la cadena de documentación que ponemos en la definición del método:

 Help on class UsesMeta in module __main__: class UsesMeta(builtins.object) | A class that uses `Meta` as its metaclass. | | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined)

Ahora, por supuesto, el atributo __doc__ para una clase se puede escribir , por lo que una solución sería reescribir las definiciones de metaclase/clase de la siguiente manera:

 from pydoc import render_doc from functools import cache def get_documentation(func_or_cls): """Get the output printed by the `help` function as a string""" return '\n'.join(render_doc(func_or_cls).splitlines()[2:]) class Meta(type): """A python metaclass.""" @classmethod @cache def _docs(metacls) -> str: """Get the documentation for all public methods and properties defined in the metaclass.""" divider = '\n\n----------------------------------------------\n\n' metacls_name = metacls.__name__ metacls_dict = metacls.__dict__ methods_header = ( f'Classmethods inherited from metaclass `{metacls_name}`' f'\n\n' ) method_docstrings = '\n\n'.join( get_documentation(method) for method_name, method in metacls_dict.items() if not (method_name.startswith('_') or isinstance(method, property)) ) properties_header = ( f'Classmethod properties inherited from metaclass `{metacls_name}`' f'\n\n' ) properties_docstrings = '\n\n'.join( f'{property_name}\n{get_documentation(prop)}' for property_name, prop in metacls_dict.items() if isinstance(prop, property) and not property_name.startswith('_') ) return ''.join(( divider, methods_header, method_docstrings, divider, properties_header, properties_docstrings, divider )) def __new__(metacls, cls_name, cls_bases, cls_dict): """Make a new class, but tweak `.__doc__` so it includes information about the metaclass's methods.""" new = super().__new__(metacls, cls_name, cls_bases, cls_dict) metacls_docs = metacls._docs() if new.__doc__ is None: new.__doc__ = metacls_docs else: new.__doc__ += metacls_docs return new def greet_user(cls): """Print a friendly greeting identifying the class's name.""" print(f"Hello, I'm the class '{cls.__name__}'!") class UsesMeta(metaclass=Meta): """A class that uses `Meta` as its metaclass."""

Esto "resuelve" el problema; si ahora help(UsesMeta) en la consola interactiva, los métodos heredados de Meta ahora están completamente documentados:

 Help on class UsesMeta in module __main__: class UsesMeta(builtins.object) | A class that uses `Meta` as its metaclass. | | ---------------------------------------------- | | Classmethods inherited from metaclass `Meta` | | greet_user(cls) | Print a friendly greeting identifying the class's name. | | ---------------------------------------------- | | Classmethod properties inherited from metaclass `Meta` | | | | ---------------------------------------------- | | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined)

Sin embargo, eso es una gran cantidad de código para lograr este objetivo. ¿Hay una mejor manera?

¿Cómo lo hace la biblioteca estándar?

También tengo curiosidad acerca de la forma en que ciertas clases en la biblioteca estándar manejan esto. Si tenemos una definición de Enum así:

 from enum import Enum class FooEnum(Enum): BAR = 1

Luego, escribir help(FooEnum) en la consola interactiva incluye este fragmento:

 | ---------------------------------------------------------------------- | Readonly properties inherited from enum.EnumMeta: | | __members__ | Returns a mapping of member name->value. | | This mapping lists all enum members, including aliases. Note that this | is a read-only view of the internal mapping.

¿Cómo exactamente logra esto el módulo enum ?

La razón por la que estoy usando metaclases aquí, en lugar de simplemente definir classmethod de clase en el cuerpo de una definición de clase

Algunos métodos que puede escribir en una metaclase, como __iter__ , __getitem__ o __len__ , no se pueden escribir como classmethod s, pero pueden conducir a un código extremadamente expresivo si los define en una metaclase. El módulo enum es un excelente ejemplo de esto.

over 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

No he mirado el resto de stdlib, pero EnumMeta logra esto anulando el método __dir__ (es decir, especificándolo en la clase EnumMeta ):

 class EnumMeta(type): . . . def __dir__(self): return ( ['__class__', '__doc__', '__members__', '__module__'] + self._member_names_ )
over 4 years ago · Santiago Trujillo Relatório

0

La función help() se basa en dir() , que actualmente no siempre brinda resultados consistentes. Es por eso que su método se pierde en la documentación interactiva generada. Hay un problema de Python abierto sobre este tema que explica el problema con más detalle: vea los errores 40098 (especialmente el primer punto).

Mientras tanto, una solución alternativa es definir un __dir__ personalizado en la metaclase:

 class Meta(type): """A python metaclass.""" def greet_user(cls): """Print a friendly greeting identifying the class's name.""" print(f"Hello, I'm the class '{cls.__name__}'!") def __dir__(cls): return super().__dir__() + [k for k in type(cls).__dict__ if not k.startswith('_')] class UsesMeta(metaclass=Meta): """A class that uses `Meta` as its metaclass."""

que produce:

 Help on class UsesMeta in module __main__: class UsesMeta(builtins.object) | A class that uses `Meta` as its metaclass. | | Methods inherited from Meta: | | greet_user() from __main__.Meta | Print a friendly greeting identifying the class's name.

Esto es esencialmente lo que hace enum , ¡aunque su implementación es obviamente un poco más sofisticada que la mía! (El módulo está escrito en python, así que para obtener más detalles, simplemente busque "__dir__" en el código fuente ).

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda