gpt4 book ai didi

python - 如何记录从元类继承的方法?

转载 作者:行者123 更新时间:2023-12-04 11:47:03 25 4
gpt4 key购买 nike

考虑以下元类/类定义:

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."""
As we know ,在元类中定义一个方法意味着它被类继承,并且可以被类使用。这意味着交互式控制台中的以下代码可以正常工作:
>>> UsesMeta.greet_user()
Hello, I'm the class 'UsesMeta'!
然而,这种方法的一个主要缺点是我们可能包含在方法定义中的任何文档都丢失了。如果我们输入 help(UsesMeta)进入交互式控制台,我们看到没有引用方法 greet_user ,更不用说我们放在方法定义中的文档字符串了:
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)
现在当然是 __doc__类的属性 is writable ,所以一种解决方案是重写元类/类定义,如下所示:
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."""
这“解决”了问题;如果我们现在输入 help(UsesMeta)进入交互式控制台,继承自 Meta的方法现在已完全记录:
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)
然而,要实现这个目标需要大量的代码。有没有更好的办法?
标准库是如何做到的?
我也很好奇标准库中某些类的管理方式。如果我们有一个 Enum定义如下:
from enum import Enum

class FooEnum(Enum):
BAR = 1
然后,输入 help(FooEnum)进入交互式控制台包括这个片段:
 |  ----------------------------------------------------------------------
| 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.
enum究竟是怎么做的模块实现这个?
我在这里使用元类的原因,而不是仅仅定义 classmethod s 在类定义的主体中
您可能在元类中编写的一些方法,例如 __iter__ , __getitem____len__ , can't be writtenclassmethod s,但如果您在元类中定义它们,则可能会导致极具表现力的代码。 enum模块是 excellent example这个的。

最佳答案

help()函数依赖 dir() ,目前并不总是给出一致的结果。这就是您的方法在生成的交互式文档中丢失的原因。有一个关于这个主题的 open python issue 更详细地解释了这个问题:见 bugs 40098 (尤其是第一个要点)。
同时,解决方法是定义自定义 __dir__在元类中:

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."""
它产生:
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.
这基本上就是 enum确实 - 尽管它的实现显然比我的要复杂一些! (该模块是用 python 编写的,因此有关更多详细信息,只需在 source code 中搜索“__dir__”即可)。

关于python - 如何记录从元类继承的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68883042/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com