Given a method, how do I return the class it belongs to in Python 3.3 onward?

前端 未结 1 1132
孤街浪徒
孤街浪徒 2020-12-18 00:34

Given x = C.f after:

class C:
    def f(self):
        pass

What do I call on x that will return C?<

1条回答
  •  隐瞒了意图╮
    2020-12-18 00:56

    If your aim is to get rid of the exec statement, but are willing to use the __qualname__ attribute, even though you are still required to manually parse it, then at least for simple cases the following seems to work:

    x.__globals__[x.__qualname__.rsplit('.', 1)[0]]
    

    or:

    getattr(inspect.getmodule(x), x.__qualname__.rsplit('.', 1)[0])
    

    I'm not a Python expert, but I think the second solution is better, considering the following documentation excerpts:

    • from What's new in Python 3.3:

      Functions and class objects have a new __qualname__ attribute representing the “path” from the module top-level to their definition. For global functions and classes, this is the same as __name__. For other functions and classes, it provides better information about where they were actually defined, and how they might be accessible from the global scope.

    • from __qualname__'s description in PEP 3155:

      For nested classed, methods, and nested functions, the __qualname__ attribute contains a dotted path leading to the object from the module top-level.

    EDIT:

    1. As noted in the comments by @eryksun, parsing __qualname__ like this goes beyond its intended usage and is extremely fragile considering how __qualname__ reflects closures. A more robust approach needs to exclude closure namespaces of the form name.. For example:

      >>> class C:
      ...     f = (lambda x: lambda s: x)(1)
      ... 
      >>> x = C.f
      >>> x
      .. at 0x7f13b58df730>
      >>> x.__qualname__
      'C...'
      >>> getattr(inspect.getmodule(x), x.__qualname__.rsplit('.', 1)[0])
      Traceback (most recent call last):
        File "", line 1, in 
      AttributeError: 'module' object has no attribute 'C..'
      

      This specific case can be handled in the following manner:

      >>> getattr(inspect.getmodule(x),
      ...         x.__qualname__.split('.', 1)[0].rsplit('.', 1)[0])
      
      

      Nonetheless, it's unclear what other corner cases exist now or may come up in future releases.

    2. As noted in the comment by @MichaelPetch, this answer is relevant only for Python 3.3 onward, as only then the __qualname__ attribute was introduced into the language.

      • However, according to @WouterBolsterlee, github.com/wbolster/qualname provides an equivalent for older Python versions.
    3. For a complete solution that handles bound methods as well, please refer to this answer.

    0 讨论(0)
提交回复
热议问题