C'è un modo per verificare se l'output della funzione è assegnato a una variabile in Python?

StackOverflow https://stackoverflow.com/questions/813882

Domanda

In Python, vorrei scrivere una funzione che stampasse piuttosto i suoi risultati sulla console se chiamata da sola (principalmente per l'uso interattivo o per il debug). Ai fini di questa domanda, diciamo che controlla lo stato di qualcosa. Se chiamo solo

check_status()

Vorrei vedere qualcosa del tipo:

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

Tuttavia, vorrei anche che passasse l'output come elenco se lo chiamo nel contesto di un'assegnazione variabile:

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'}

Quindi ... c'è un modo per sapere dinamicamente, all'interno di una funzione, se il suo output è assegnato? Mi piacerebbe essere in grado di farlo senza ricorrere al passaggio di parametri o scrivere un'altra funzione dedicata a questo. Ho cercato su Google per un po ', e da quel poco che posso dire sembra che dovrei ricorrere a giocare con il bytecode. È davvero necessario?

È stato utile?

Soluzione

Nuova soluzione

Questa è una novità che la soluzione rileva quando il risultato della funzione viene utilizzato per l'assegnazione esaminando il proprio bytecode. Non viene eseguita la scrittura di bytecode e dovrebbe anche essere compatibile con le versioni future di Python perché utilizza il modulo opcode per le definizioni.

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

Soluzione 1

Questa è la vecchia soluzione che rileva ogni volta che si chiama la funzione durante il debug in Python -i o 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()
>>>

Altri suggerimenti

  

Tuttavia, vorrei anche che passasse l'output come elenco

Intendi " restituisci l'output come dizionario " - fai attenzione ;-)

Una cosa che potresti fare è usare la capacità dell'interprete Python per convertire automaticamente in una stringa il risultato di qualsiasi espressione. Per fare ciò, crea una sottoclasse personalizzata di dict che, quando ti viene chiesto di convertirsi in una stringa, esegue la formattazione che desideri. Ad esempio,

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],
       ... )

Ovviamente potresti probabilmente trovare un modo più carino di scrivere il codice, ma l'idea di base è che il metodo __str__ crea la stringa piuttosto stampata che rappresenta lo stato dell'oggetto e restituisce esso. Quindi, se si restituisce un PrettyDict dalla funzione check_status () , quando si digita

>>> check_status()

vedresti

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

L'unica cattura è quella

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

ti darebbe la stessa cosa, perché la stessa conversione in stringa avviene come parte della funzione print . Ma per averlo usato in qualche vera applicazione, dubito che ciò avrebbe importanza. Se davvero volessi vedere il valore restituito come un motto puro, potresti farlo

>>> print repr(not_robot_stat)

invece, e dovrebbe mostrarti

{'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'}

Il punto è che, come hanno detto altri poster, non c'è modo per una funzione in Python di sapere cosa si farà con il suo valore di ritorno ( EDIT : okay, forse non ci sarebbe un modo strano per bytecode, ma non farlo) - ma puoi aggirarlo nei casi che contano.

Non c'è nessun caso d'uso per questo. Python assegna tutti i risultati interattivi a una variabile speciale denominata _ .

Puoi fare quanto segue in modo interattivo. Funziona alla grande. Nessun affare divertente.

>>> 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'}

Non c'è modo per una funzione di sapere come viene usato il suo valore di ritorno.

Bene, come dici tu, gli hack di egregio bytecode potrebbero essere in grado di farlo, ma sarebbe davvero complicato e probabilmente fragile. Segui il percorso esplicito più semplice.

Anche se c'è un modo per farlo, è una cattiva idea. Immagina di provare a eseguire il debug di qualcosa che si comporta in modo diverso a seconda del contesto da cui viene chiamato. Ora prova a immaginare che saranno tra sei mesi e questo è sepolto in una parte di un sistema che è troppo grande per tenerti in mente tutto in una volta.

Semplificalo. Esplicito è meglio che implicito. Basta creare una funzione di stampa carina (o utilizzare il modulo pprint) e chiamarla sul risultato. In una sessione interattiva Pythn puoi usare _ per ottenere il valore dell'ultima espressione.

Non c'è modo di farlo, almeno non con le normali procedure di sintassi, perché la chiamata di funzione e l'assegnazione sono operazioni completamente indipendenti, che non sono consapevoli l'una dell'altra.

La soluzione più semplice che posso vedere per questo è passare una bandiera come arg alla tua funzione check_status e gestirla di conseguenza.

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

E poi ...

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

EDIT: ho pensato che avresti stampato abbastanza spesso più di quanto avresti assegnato. In caso contrario, scambia il valore predefinito e il valore che passi.

L'unico modo per fare ciò che vuoi è davvero "giocare con il bytecode" - non esiste altro modo per recuperare tali informazioni. Un modo molto migliore, ovviamente, è quello di fornire due funzioni separate: una per ottenere lo stato come dict, l'altra per chiamare la prima e formattarla.

In alternativa (ma non un'architettura eccellente) potresti avere una singola funzione che accetta un parametro opzionale per comportarsi in questi due modi completamente diversi: questo non è eccellente perché una funzione dovrebbe avere UNA funzione, cioè fondamentalmente fare UNA cosa, non due diversi come questi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top