【问题标题】:How to get the class from a method in Python?如何从 Python 中的方法中获取类?
【发布时间】:2020-09-04 03:17:40
【问题描述】:

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

例如,如果我们有

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+ 中)?

谢谢

【问题讨论】:

  • 你的用例是什么?我想知道,因为期望的结果实际上是论点的一部分。您已经拥有HelloBonjour
  • 等等,等等——你想要一个解决方案来处理类方法和普通方法吗? 为什么? AFAICT,您会使用该技术实现单个方法,因此您已经知道您是在使用类方法还是在普通方法中工作。在classmethod内部,传入的cls已经是你需要的类对象了;普通方法内可以查看self.__class__
  • @KarlKnechtel 是的,我想要一个单一的解决方案(尽管我实际上只使用普通方法)。问题不是从方法本身获取类,而是将此方法传递给将获取类的函数。

标签: python python-3.x methods inspect


【解决方案1】:

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

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

【讨论】:

  • 谢谢...我喜欢你的isinstance(self, type)~! :) ...但这个论点self实际上是什么?
  • @Jean-FrancoisT。我简化了它。现在清楚了吗?或者我不明白你的问题。
  • 之前很清楚。只是一个错字if isinstance(self, type): 而不是if isinstance(self_or_cls, type): [缺少_or_cls]。这个问题很好理解并且正在解决问题(尽管我在发布问题时实际上有一个解决方案:))
【解决方案2】:

我提供了以下解决方案:

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[..., 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")
    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")

【讨论】:

    猜你喜欢
    • 2012-12-15
    • 1970-01-01
    • 2010-12-27
    • 2022-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多