2009-05-02 11 views
2

In Python möchte ich eine Funktion schreiben, die ihre Ergebnisse auf die Konsole drucken würde, wenn sie selbst aufgerufen würde (hauptsächlich zur interaktiven Verwendung oder zum Debuggen). Für den Zweck dieser Frage, lassen Sie uns sagen, dass es den Status von etwas überprüft. Wenn ich nurGibt es eine Möglichkeit zu überprüfen, ob die Funktionsausgabe einer Variablen in Python zugewiesen ist?

check_status() 

nennen würde Ich mag so etwas wie sehen:

Pretty printer status check 0.02v 
NOTE: This is so totally not written for giant robots 
================================= 
System operational: ... ok 
Time to ion canon charge is 9m 21s 
Booster rocket in AFTERBURNER state 
Range check is optimal 
Rocket fuel is 10h 19m 40s to depletion 
Beer served is type WICKSE LAGER, chill optimal 
Suggested catchphrase is 01_FIGHTING_SPIRIT_GOGOGO 
Virtual ... on 

Aber ich möchte auch, dass er die Ausgabe als Liste übergeben, wenn ich es im Rahmen einer Variablen aufrufen Zuordnung:

not_robot_stat = check_status() 
print not_robot_stat 
>>> {'cond_op': 1, 't_canoncharge': 1342, 'stage_booster': 5, 'range_est_sigma': 0.023, 'fuel_est': 32557154, 'beer_type': 31007, 'beer_temp': 2, 'catchphrase_suggestion': 1023, 'virtual_on': 'hell yes'} 

So ... ist es eine Möglichkeit, um dynamisch zu wissen, innerhalb einer Funktion, ob sein Ausgang zugewiesen wird? Ich würde gerne in der Lage sein, dies zu tun, ohne Param-Passing zu verwenden oder eine andere dafür vorgesehene Funktion zu schreiben. Ich habe ein wenig gegoogelt, und von dem, was ich sagen kann, sieht es so aus, als müsste ich mit dem Bytecode spielen. Ist das wirklich notwendig?

+0

Tim, hast du meinen Code probiert?Es erkennt immer, wenn Sie die Funktion vom Debuggen aus aufrufen und die Diagnose wie gewünscht ausgeben können. – Unknown

+0

Nun, ich gehe noch einmal auf die Feature-Anforderungen, um zu sehen, ob dies erforderlich ist ... aber es sieht so aus, als ob ich es in ALLEN Fällen brauche, nicht nur im interaktiven Modus. Die Sotry ist ein wenig beteiligt, also werde ich versuchen, es kohärenter zu machen und meine Frage in ein paar Tagen zu aktualisieren. –

+0

@ Tim, in diesem Fall habe ich eine neue Lösung mit Bytecode lesen. – Unknown

Antwort

6

Neue Lösung

Dies ist eine neue Lösung, dass erkennt, wenn das Ergebnis der Funktion für die Zuordnung verwendet wird von einem eigenen Bytecode zu untersuchen. Es gibt kein Bytecode-Schreiben, und es sollte sogar mit zukünftigen Versionen von Python kompatibel sein, da es das Opcode-Modul für Definitionen verwendet.

import inspect, dis, opcode 

def check_status(): 

    try: 
     frame = inspect.currentframe().f_back 
     next_opcode = opcode.opname[ord(frame.f_code.co_code[frame.f_lasti+3])] 
     if next_opcode == "POP_TOP": 
      # or next_opcode == "RETURN_VALUE": 
      # include the above line in the if statement if you consider "return check_status()" to be assignment 
      print "I was not assigned" 
      print "Pretty printer status check 0.02v" 
      print "NOTE: This is so totally not written for giant robots" 
      return 
    finally: 
     del frame  

    # do normal routine 

    info = {'cond_op': 1, 't_canoncharge': 1342, 'stage_booster': 5} 

    return info 

# no assignment  
def test1(): 
    check_status() 

# assignment 
def test2(): 
    a = check_status() 

# could be assignment (check above for options) 
def test3(): 
    return check_status() 

# assignment 
def test4(): 
    a = [] 
    a.append(check_status()) 
    return a 

Lösung 1

Dies ist die alte Lösung, wenn Sie die Funktion aufrufen erkennt während des Debuggens unter Python -i oder PDB.

import inspect 

def check_status(): 
    frame = inspect.currentframe() 
    try: 
     if frame.f_back.f_code.co_name == "<module>" and frame.f_back.f_code.co_filename == "<stdin>": 
      print "Pretty printer status check 0.02v" 
      print "NOTE: This is so totally not written for giant robots" 
    finally: 
     del frame 

    # do regular stuff 
    return {'cond_op': 1, 't_canoncharge': 1342, 'stage_booster': 5} 

def test(): 
    check_status() 


>>> check_status() 
Pretty printer status check 0.02v 
NOTE: This is so totally not written for giant robots 
{'cond_op': 1, 't_canoncharge': 1342, 'stage_booster': 5} 

>>> a=check_status() 
Pretty printer status check 0.02v 
NOTE: This is so totally not written for giant robots 

>>> a 
{'cond_op': 1, 't_canoncharge': 1342, 'stage_booster': 5} 

test() 
>>> 
+0

Danke! Ich habe auf eine gute Bytecode-Mangling-Implementierung gehofft, wenn es keine native Syntax gibt, und das nagelt es! –

+0

Nun, jetzt, wo ich Zeit hatte, es genau zu betrachten, scheint es, dass Sie immer noch nicht wirklich unterscheiden, ob die Funktion von selbst aufgerufen wurde oder zur Zuweisung aufgerufen wurde. –

+0

Gegenbeispiel: den Code in eine Datei "blahblah.txt" schreiben und "cat blahblah.txt | python" ausführen ;-) aber das ist nur pedantisch. Der Punkt ist, dass diese Lösung nicht die gewünschten Ziele erreicht, das Wörterbuch hübsch auszudrucken oder zu erkennen, was mit dem Rückgabewert der Funktion gemacht wird. (Für das, was es wert ist, scheinst du ein bisschen verzweifelt zu sein - du bekommst keine Upvotes, indem du um sie bettelst) –

1

Es gibt keine Möglichkeit für eine Funktion zu wissen, wie ihr Rückgabewert verwendet wird.

Nun, wie Sie erwähnen, können ungeheuer Bytecode-Hacks es tun, aber es wäre wirklich kompliziert und wahrscheinlich zerbrechlich. Gehen Sie die einfachere explizite Route.

0

Es gibt keine Möglichkeit dies zu tun, zumindest nicht mit den normalen Syntaxprozeduren, weil der Funktionsaufruf und die Zuweisung völlig unabhängige Operationen sind, die sich gegenseitig nicht kennen.

Die einfachste Abhilfe, die ich für diese sehen kann, ist ein Flag als arg auf Ihre check_status Funktion zu übergeben und damit entsprechend umgehen.

def check_status(return_dict=False) : 
    if return_dict : 
     # Return stuff here. 
    # Pretty Print stuff here. 

Und dann ...

check_status() # Pretty print 
not_robot_stat = check_status(True) # Get the dict. 

EDIT: Ich nahm Sie mehr ziemlich Druck würde oft als Sie zuweisen würde. Wenn das nicht der Fall ist, vertauschen Sie den Standardwert und den Wert, den Sie übergeben.

0

Die einzige Möglichkeit zu tun, was Sie wollen, ist in der Tat "mit dem Bytecode zu spielen" - es gibt keine andere Möglichkeit, diese Informationen wiederherzustellen. Ein viel besserer Weg besteht natürlich darin, zwei separate Funktionen bereitzustellen: eine, um den Status als ein Diktat zu erhalten, die andere, um die erste zu benennen und sie zu formatieren.

Alternativ (aber nicht eine ausgezeichnete Architektur) könnte man eine einzelne Funktion haben, die einen optionalen Parameter verwendet, um sich auf diese beiden völlig unterschiedlichen Arten zu verhalten - das ist nicht exzellent, weil eine Funktion EINE Funktion haben sollte, dh grundsätzlich EINE Sache nicht zwei verschiedene wie diese.

+0

Ich bin nicht unbedingt damit einverstanden, dass eine Funktion, die zurückgibt/druckt abhängig von einem Wert ist schlechte Architektur, aber ich denke, wir haben alle unsere Meinungen. –

+0

Technik und Architektur geht es nicht um "Meinungen". – Hejazzman

+0

Ich stimme nicht zu. Hole es? –

4

Allerdings würde Ich mag es auch die Ausgabe als Liste übergeben

Sie meinen, „die Ausgabe als Wörterbuch return“ - vorsichtig sein ;-)

Eines könnte man Verwenden Sie die Fähigkeit des Python-Interpreters, das Ergebnis eines Ausdrucks automatisch in eine Zeichenfolge zu konvertieren. Erstellen Sie dazu eine benutzerdefinierte Unterklasse von dict, die, wenn Sie aufgefordert werden, sich in eine Zeichenfolge zu konvertieren, die gewünschte Formatierung durchführt.Zum Beispiel

class PrettyDict(dict): 
    def __str__(self): 
     return '''Pretty printer status check 0.02v 
NOTE: This is so totally not written for giant robots 
================================= 
System operational: ... %s 
Time to ion canon charge is %dm %ds 
Booster rocket in %s state 
(other stuff) 
''' % (self.conf_op and 'ok' or 'OMGPANIC!!!', 
     self.t_canoncharge/60, self.t_canoncharge % 60, 
     BOOSTER_STATES[self.booster_charge], 
     ...) 

Natürlich könnte man wahrscheinlich mit einem schöneren Weg, um den Code zu schreiben, aber die Grundidee ist, dass die __str__ Methode Pretty-Print-String erzeugt, der den Zustand des Objekts und kehrt darstellt es. Dann, wenn Sie ein PrettyDict aus Ihrer check_status() Funktion zurückgeben, wenn Sie

>>> check_status() 

geben würden Sie

Pretty printer status check 0.02v 
NOTE: This is so totally not written for giant robots 
================================= 
System operational: ... ok 
Time to ion canon charge is 9m 21s 
Booster rocket in AFTERBURNER state 
Range check is optimal 
Rocket fuel is 10h 19m 40s to depletion 
Beer served is type WICKSE LAGER, chill optimal 
Suggested catchphrase is 01_FIGHTING_SPIRIT_GOGOGO 
Virtual ... on

Der einzige Haken sehen ist, dass

>>> not_robot_stat = check_status() 
>>> print not_robot_stat 

würden Sie die gleiche Sache , weil die gleiche Umwandlung in eine Zeichenfolge als Teil der print-Funktion stattfindet. Aber um das in einer realen Anwendung zu verwenden, bezweifle ich, dass das wichtig wäre. Wenn Sie wirklich den Rückgabewert als reine dict sehen wollte, könnte man

>>> print repr(not_robot_stat) 

stattdessen tun, und es sollten Sie

{'cond_op': 1, 't_canoncharge': 1342, 'stage_booster': 5, 'range_est_sigma': 0.023, 'fuel_est': 32557154, 'beer_type': 31007, 'beer_temp': 2, 'catchphrase_suggestion': 1023, 'virtual_on': 'hell yes'}

Der Punkt zeigen, dass, wie andere Plakate gesagt haben, es ist keine Möglichkeit für eine Funktion in Python zu wissen, was mit dem Rückgabewert (EDIT: okay, vielleicht wäre es einige seltsame Bytecode-Hacking-Weg, aber tun Sie das nicht tun) - aber Sie können umgehen es in den Fällen, die wichtig sind.

+0

Das ist auch ein guter Vorschlag ... Ich würde darüber nachdenken. Vielen Dank! –

3

Es gibt keinen Anwendungsfall dafür. Python weist alle interaktiven Ergebnisse einer speziellen Variablen mit dem Namen _ zu.

Sie können interaktiv Folgendes tun. Funktioniert super. Kein witziges Geschäft.

>>> check_status() 
{'cond_op': 1, 't_canoncharge': 1342, 'stage_booster': 5, 'range_est_sigma': 0.023, 'fuel_est': 32557154, 'beer_type': 31007, 'beer_temp': 2, 'catchphrase_suggestion': 1023, 'virtual_on': 'hell yes'} 

>>> pprint.pprint(_) 
{'beer_temp': 2, 
'beer_type': 31007, 
'catchphrase_suggestion': 1023, 
'cond_op': 1, 
'fuel_est': 32557154, 
'range_est_sigma': 0.023, 
'stage_booster': 5, 
't_canoncharge': 1342, 
'virtual_on': 'hell yes'} 
1

Auch wenn es einen Weg gibt, dies zu tun, ist es eine schlechte Idee. Stellen Sie sich vor, Sie versuchen, etwas zu debuggen, das sich je nach dem Kontext, aus dem es aufgerufen wird, anders verhält. Jetzt versuchen Sie sich vorzustellen, dass es in sechs Monaten sein wird, und das ist teilweise in einem System vergraben, das zu groß ist, um es in Ihrem Kopf auf einmal zu behalten.

Halten Sie es einfach. Explizit ist besser als implizit. Mach einfach eine Pretty-Print-Funktion (oder nutze das pprint-Modul) und rufe das Ergebnis auf. In einer interaktiven Python-Sitzung können Sie _ verwenden, um den Wert des letzten Ausdrucks zu erhalten.

Verwandte Themen