Frage Wie gebe ich bei einer Methode die Klasse zurück, zu der sie in Python 3.3 gehört?


Gegeben x = C.f nach:

class C:
    def f(self):
        pass

Worauf rufe ich an? x das wird zurückkehren C?

Das Beste, was ich tun könnte, ist execeinen geparsten Teil von x.__qualname__, das ist hässlich:

exec('d = ' + ".".join(x.__qualname__.split('.')[:-1]))

Stellen Sie sich für einen Anwendungsfall vor, dass ich einen Dekorateur haben möchte, der ein hinzufügt super Rufen Sie eine Methode auf, auf die sie angewendet wird. Wie kann der Dekorator, dem nur das Funktionsobjekt gegeben wird, die Klasse erhalten? super (das ??? unten)?

def ensure_finished(iterator):
    try:
        next(iterator)
    except StopIteration:
        return
    else:
        raise RuntimeError

def derived_generator(method):
    def new_method(self, *args, **kwargs):
        x = method(self, *args, **kwargs)
        y = getattr(super(???, self), method.__name__)\
            (*args, **kwargs)

        for a, b in zip(x, y):
            assert a is None and b is None
            yield

        ensure_finished(x)
        ensure_finished(y)

    return new_method

5
2017-09-18 20:06


Ursprung


Antworten:


Wenn es Ihr Ziel ist, die exec Aussage, aber sind bereit zu verwenden das __qualname__ AttributObwohl Sie es immer noch manuell parsen müssen, scheint zumindest in einfachen Fällen folgendes zu funktionieren:

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

oder:

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

Ich bin nicht ein Python Experte, aber ich denke, die zweite Lösung ist besser, wenn man die folgenden Dokumentationsauszüge berücksichtigt:

  • von Was gibt es Neues? Python 3.3:

    Funktionen und Klassenobjekte haben ein neues __qualname__ Attribut, das den "Pfad" von der obersten Ebene des Moduls zu ihrer Definition darstellt. Für globale Funktionen und Klassen ist dies das Gleiche wie __name__. Für andere Funktionen und Klassen bietet es bessere Informationen darüber, wo sie tatsächlich definiert wurden und wie sie möglicherweise vom globalen Gültigkeitsbereich aus zugänglich sind.

  • von __qualname__Beschreibung in PEP 3155:

    Für verschachtelte Klassen, Methoden und verschachtelte Funktionen __qualname__ Attribut enthält einen gepunkteten Pfad, der von der obersten Ebene des Moduls zum Objekt führt.

BEARBEITEN: 

  1. Wie in den Kommentaren von @eryksun beschrieben, Parsing __qualname__ so geht das über den vorgesehenen Verwendungszweck hinaus und ist äußerst fragil, wenn man bedenkt, wie __qualname__ spiegelt Verschlüsse. Ein robusterer Ansatz muss Schließnamespaces des Formulars ausschließen name.<locals>. Beispielsweise:

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

    Dieser spezielle Fall kann folgendermaßen gehandhabt werden:

    >>> getattr(inspect.getmodule(x),
    ...         x.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
    <class '__main__.C'>
    

    Nichtsdestoweniger ist es unklar, welche anderen Eckfälle jetzt existieren oder in zukünftigen Versionen auftauchen werden.

  2. Wie im Kommentar von @MichaelPetch angemerkt, ist diese Antwort nur relevant für Python 3.3 weiter, als nur dann das __qualname__ Attribut wurde eingeführt in die Sprache.

  3. Eine vollständige Lösung, die auch gebundene Methoden behandelt, finden Sie unter diese Antwort.


4
2017-09-18 20:51