如何从Python中的方法获取类?

问题描述

我正在尝试编写一个将给定方法作为参数传递时获取类的函数

例如,如果我们有

class Hello:
    NAME = "HELLO TOTO"

    def method(self) -> int:
        return 5

    @classmethod
    def cls_method(cls) -> str:
        return "Hi"


class Bonjour(Hello):
    NOM = "BONJOUR TOTO"

    def new_method(self) -> int:
        return 0

我会得到:

  • Hello来自方法Hello().methodHello().cls_method
  • Bonjour来自方法Bonjour().new_methodBonjour().cls_method

搜索了SO,但找不到任何直接答案。

如何实现这种功能(如果重要的话,在Python 3.6+中)?

谢谢

解决方法

我相信没有万无一失的方法,但这在大多数情况下都适用:

def get_class_of_bound_self(f):
    assert hasattr(f,'__self__')
    return f.__self__ if isinstance(f.__self__,type) else type(f.__self__)

请注意,如果f是元类M的方法,则此方法将失效;它将返回M而不是type

,

我提供了以下解决方案:

import inspect

def get_class(func: Callable[...,Any]) -> Any:
    """Return class of a method.

    Args:
        func: callable

    Returns:
        Class of the method,if the argument is a method

    Raises:
        AttributeError: if the argument is not callable or not a method
    """
    if not callable(func):
        raise AttributeError(f"{func} shall be callable")
    if not inspect.ismethod(func):
        raise AttributeError(f"Callable {func} shall be a method")

    first_arg = func.__self__  # type: ignore  # method have "self" attribute
    return first_arg if inspect.isclass(first_arg) else first_arg.__class__

最后一行return first_arg if inspect.isclass(first_arg) else first_arg.__class__用于处理类方法的情况(在这种情况下,func.__self__对应于cls,并且是类本身)。

没有inspect模块的另一种选择是捕获异常(非常感谢@Elazar使用isistance(...,type)的想法):

def get_class(func: Callable[...,if the argument is a method

    Raises:
        AttributeError: if the argument is not callable or not a method
    """
    if not callable(func):
        raise AttributeError(f"{func} shall be callable")
    try:
        first_arg = func.__self__  # type: ignore  # method have "self" attribute
    except AttributeError:
        raise AttributeError(f"Callable {func} shall be a method")

    cls_or_type = first_arg.__class__
    return first_arg if isinstance(cls_or_type,type) else cls_or_type

这是我用来检查您是否感兴趣的代码:

def my_func() -> int:
    """It feels like a zero"""
    return 0


for method in [
    Hello().method,Bonjour().method,Hello().cls_method,Bonjour().cls_method,Bonjour().new_method,]:
    # MyClass = get_class(func)
    MyClass = get_class_2(method)
    for attr in ["NAME","NOM"]:
        print(f"... {method} - {attr} ...")
        try:
            print(getattr(MyClass,attr))
        except AttributeError as exp:
            print(f"Error when getting attribute: {exp}")
# class_ = get_class(my_func)
for not_method in [my_func,int,Hello]:
    try:
        MyClass = get_class(not_method)
        print(f"{not_method} => NOK (no exception raised)")
    except AttributeError:
        print(f"{not_method} => OK")