Klassenmethode kann nicht gleichzeiting Membername sein?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
BlackJack

@mutetella: Nein das ist nicht möglich, das hätte aber aus den bisherigen Beiträgen schon ersichtlich sein müssen, denn ob und wann das abgefragte Objekt aufgerufen wird kann der Interpeter beim Abfragen ja nicht ermitteln. Das kann ja irgendwann an einer ganz anderen Stelle im Code passieren und muss nicht direkt nach dem Abfragen des Attributs passieren. Warum Du im Falle von `request_has_brackets` das zweite Objekt in dem Tupel *aufrufst* ist mir auch nicht so ganz klar, denn dann muss das selber ja wieder etwas aufrufbares zurückgeben weil der Code der diesen Rückgabewert bekommt, ihn ja offensichtlich aufrufen wird, sonst wäre `request_has_brackets` nicht wahr.

Was Du im Grunde fragst ist ob eine Funktion/Methode herausfinden kann was der Aufrufer mit ihrem Rückgabewert machen wird. Nein, das geht nicht, weil das nicht vorgesehen ist. Das wäre auch ziemlich schräg.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Vielleicht ist es doch möglich, wenn man im aufrufenden Stackframe schaut was das noch für Bytecodes hat und ob danach ein Funktionsaufruf kommt. So ähnlich wie birkenfeld ``runkit_return_value_used`` implementiert hat ;-)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@Leonidas: Ich ging davon aus das wir von Python reden und nicht von Interna einer bestimmten Implementierung wo man schmutzige Hacks anwenden kann. :-)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Also so schmutzig ist dass gar nicht, nachdem man die Magie hinter einer Metaklasse versteckt hat :)

Code: Alles auswählen

import sys
import dis
import inspect


NO_ATTRIBUTE = object()


class SeparateNamedspacedMeta(type):
    def __new__(cls, name, bases, attributes):
        _methods = {}
        for name, attribute in attributes.iteritems():
            if name.startswith('__') and name.endswith('__'):
                continue
            if inspect.isfunction(attribute):
                _methods[name] = attribute
                attributes[name] = AttributeOrMethodDescriptor(name)
        attributes['_methods'] = _methods
        return type.__new__(cls, name, bases, attributes)


class AttributeOrMethodDescriptor(object):
    def __init__(self, name):
        self.name = name
        self.attribute_value = NO_ATTRIBUTE

    def __get__(self, instance, owner):
        if instance is None or self.attribute_value is NO_ATTRIBUTE:
            return owner._methods[self.name].__get__(instance, owner)
        calling_frame = sys._getframe(1)
        next_opcode = ord(calling_frame.f_code.co_code[calling_frame.f_lasti + 3])
        if dis.opname[next_opcode] == 'CALL_FUNCTION':
            return owner._methods[self.name].__get__(instance, owner)
        return self.attribute_value

    def __set__(self, instance, value):
        self.attribute_value = value

    def __delete__(self, instance):
        self.attribute_value = NO_ATTRIBUTE


class SeparateNamespacedClass(object):
    __metaclass__ = SeparateNamedspacedMeta

    def __init__(self):
        self.spam = 'attribute'

    def spam(self):
        return 'method'


foo = SeparateNamespacedClass()
print SeparateNamespacedClass.spam # <unbound method spam.spam>
print foo.spam # attribute
print foo.spam() # method
BlackJack

Egal was man da drumherum an Magie veranstaltet: ``next_opcode = ord(calling_frame.f_code.co_code[calling_frame.f_lasti + 3])`` wird unter Jython, IronPython, und PyPy wohl nicht funktionieren. Das ist halt ein Implementierungsdetail von CPython. `sys._getframe()` kann man ja im Grunde schon nicht als gegeben ansehen. Und es löst nicht das Problem das man manchmal auch die Methode haben möchte ohne sie direkt aufzurufen.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Das war auch nicht ganz ernst gemeint ;)

Tatsächlich funktioniert der Code unter PyPy nicht weil PyPy eine Optimierung für Methodenaufrufe hat. PyPys Compiler produziert statt CALL_FUNCTION an dieser Stelle CALL_METHOD und damit funktioniert es nicht mehr.
Antworten