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.
Krischu
User
Beiträge: 97
Registriert: Dienstag 14. Januar 2014, 09:07

Mir fällt gerade auf, als ich eine membervariable self.header eingeführt hatte und eine
methode header() definiert hatte. Das scheint nicht erlaubt zu sein.
--
Grüße
Christoph
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Und du suchst jetzt eine Bestaetigung? Ja, ein Name/Attribut kann immer nur auf ein Objekt verweisen. Aber verboten ist es deshalb nicht. Man darf sich damit gerne selbst ins Knie schiessen.

So, warum macht es keinen Sinn dass der Name auf 2 Objekte verweisen koennte? Weil der Zugriffsweg derselbe ist. Du kannst an ein Attribut beliebige Werte zuweisen, u.a. auch Funktionen. Wie soll man dann zwischen dem Aufruf der "richtigen" Methode `Klass.header` und dem Aufruf einer Funktion, die an `klass.header` gebunden ist, unterscheiden?
BlackJack

@Krischu:Das ist doch irgendwie klar, denn wie sollte ein Objekt denn auch mehrere Attribute mit dem gleichen Namen haben? Methoden benennt man üblicherweise auch nach Tätigkeiten, denn die tun ja etwas. Für `header()` gibt es also vielleicht einen besseren Namen der dem Leser verrät was die Methode tut.
Krischu
User
Beiträge: 97
Registriert: Dienstag 14. Januar 2014, 09:07

cofi hat geschrieben:Und du suchst jetzt eine Bestaetigung? Ja, ein Name/Attribut kann immer nur auf ein Objekt verweisen. Aber verboten ist es deshalb nicht. Man darf sich damit gerne selbst ins Knie schiessen.

So, warum macht es keinen Sinn dass der Name auf 2 Objekte verweisen koennte? Weil der Zugriffsweg derselbe ist. Du kannst an ein Attribut beliebige Werte zuweisen, u.a. auch Funktionen. Wie soll man dann zwischen dem Aufruf der "richtigen" Methode `Klass.header` und dem Aufruf einer Funktion, die an `klass.header` gebunden ist, unterscheiden?
indem ich sage

Code: Alles auswählen

klass.header   - damit greife ich direkt auf self.header zu (auch komisch, daß ich von draußen drauf zugreifen kann, aber gut
                      Python hat keine private members.

klass.header() - ist der Methodenaufruf. Was wäre daran verboten?
--
Grüße
Christoph
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Krischu hat geschrieben:code=python file=Untitled.py]klass.header - damit greife ich direkt auf self.header zu (auch komisch, daß ich von draußen drauf zugreifen kann, aber gut Python hat keine private members.
Python benötigt keine privaten Attribute, dass wird über Konvention geregelt. Wenn du etwas als Implementierungsdetail kennzeichen willst, dann benutze Namen mit einem führenden Unterstrich.
Krischu hat geschrieben:klass.header() - ist der Methodenaufruf. Was wäre daran verboten?
Weil die Namensauflösung in Python anders funktioniert. Mit ``klass.header`` holst du dir das header-Attribut von "klass". Dabei kann "header" alles mögliche sein: ein Integer, ein Float oder auch etwas Aufrufbares wie eine Methode (Funktionen sind in Python First-Class-Objekte). Wenn du also ``klass.header()`` schreibst, dann holst du dir das header-Objekt und rufst dieses auf. Was konkret bedeutet, dass du die __call__-Methode von "header" aufrufst.

Code: Alles auswählen

>>> class Spam(object):
...     def ham(self, x):
...         print x
... 
>>> s = Spam()
>>> h = s.ham
>>> h(42)
42
>>> h.__call__(42)
42
Das Leben ist wie ein Tennisball.
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

oder der gegenteilige Fall:

Code: Alles auswählen

>>> class Spam(object):
...     def __init__(self):
...         self.ham = None
... 
>>> def egg(x):
...     print x
... 
>>> spam = Spam()
>>> spam.ham = egg
>>> spam.ham(42)
42
>>> 
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Du kannst natürlich auch ``self.header = 42`` setzen und dann versuchen ``self.header()`` aufzurufen und gucken was passiert. Tipp: 42 ist nicht "callable".
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Wenn man mal nicht über Sinn oder Unsinn einer solchen Aktion nachdenkt: Wo genau ist die Stelle, an der man sich einklinken müsste, um zu ermitteln, ob ein Name abgefragt oder aufgerufen wird, sprich ob Klammern gesetzt sind oder nicht?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
kbr
User
Beiträge: 1506
Registriert: Mittwoch 15. Oktober 2008, 09:27

mutetella hat geschrieben:Wo genau ist die Stelle, an der man sich einklinken müsste, um zu ermitteln, ob ein Name abgefragt oder aufgerufen wird, sprich ob Klammern gesetzt sind oder nicht?
Meinst Du so was?

Code: Alles auswählen

>>> class A(object):
...     b = 42
...     def c(self):
...             pass
... 
>>> a = A()
>>> type(a.b)
<type 'int'>
>>> type(a.c)
<type 'instancemethod'>
BlackJack

@mutetella: Da gibt es keine Stelle denn ein Attribut wird immer erst mit dem Punktoperator abgefragt, egal ob es ein ”Datenattribut” oder eine gebundene Methode ist. Und ob und wann das Objekt dann aufgerufen wird, sagt einem immer noch nicht ob es sich um eine Methode dieses Objekts oder ein ”Datenattribut” handelt. Und da der Punktoperator und das Aufrufen komplett voneinander unabhängig sind, kann man auch nicht Unterscheiden ob der Abrufer des Attributs ein ”Datenattribut” oder eine Methode haben wollte, wenn man denn diese zwei ”verschiedenen” Sachen unter dem selben Attributnamen anbieten möchte.
Benutzeravatar
diesch
User
Beiträge: 80
Registriert: Dienstag 14. April 2009, 13:36
Wohnort: Brandenburg a.d. Havel
Kontaktdaten:

Methoden sind in Python normale Objekte wie Strings, Zahlen usw. auch. Ich kann daher so was schreiben:

Code: Alles auswählen

class Foo:

    def __init__(self):
        self.a = 42

    def b(self):
        return 17


foo=Foo()

for name in 'a', 'b':
    attr = getattr(foo, name)
    
    if callable(attr):
        value = attr()
    else:
        value = attr
        
    print('%s: %s' % (name, value))
        
http://www.florian-diesch.de
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack:
Keine Stelle, die ich mit Pythonmitteln erreiche oder generell keine, bzw. nur, wenn ich den Interpreter verändere?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Vielleicht solltest Du noch mal genau beschreiben was Du erreichen willst. Ob ein Name abgefragt oder (das Objekt hinter einem Namen) aufgerufen wird ist jedenfalls keine sauber getrennte Unterscheidung, weil aufrufen in diesem Fall immer ein abfragen vorausgehen muss. ``obj.spam()`` ist nicht eine Operation sondern zwei, das kann man auch entsprechend Klammern um es zu verdeutlichen: ``(obj . spam)()``. Das ist legale Python-Syntax.

Und ob ein abgefragtes Attribut aufgerufen wird: Wie willst Du das testen. Das muss ja auch nicht zwingend direkt nach dem Abfragen passieren wie von anderen ja hier schon an Beispielen gezeigt wurde.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack
Ich meine, ob sowas in dieser Art machbar wäre:

Code: Alles auswählen

class Nonsense(object):
    def __init__(self):
        self._multiple = ('any_value', self.any_callable)

    def any_callable(self, value):
        pass

    @property
    def multiple(self):
        request_has_brackets, value = interpreter.please_give_it_to_me()
        if request_has_brackets:
            return self._multiple[1](value)
        return self._multiple[0]
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

@mutetella: Nein, das laesst die Semantik nicht zu. Wie BlackJack schon geschrieben hat, sind es zwei Operationen, die man u.a. auch trennen kann.
Selbst wenn man den Zugriff auf Methoden und Datenattributen noch durch einen moeglichen Aufruf unterscheiden koennte: Das ist zu kurz gegriffen, denn man verbietet damit zum einen callables als Datenattribute und zum anderen den blossen Zugriff auf die Methoden-Objekte.

Was man machen koennte ist, manuell 2 Namensraeume fuer Daten und Methoden einzufuehren ... nur braeuchte man dann zumindest fuer eines einen eigenen Zugriffweg, sonst steht man wieder vor dem Problem von oben.
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.
Antworten