Есть ли способ проверить, назначен ли вывод функции для переменной в Python? - PullRequest
1 голос
/ 02 мая 2009

В Python я хотел бы написать функцию, которая бы красиво выводила результаты на консоль, если вызывалась сама (в основном для интерактивного использования или для отладки). Для целей этого вопроса, скажем, он проверяет состояние чего-либо. Если я позвоню просто

check_status()

Я бы хотел увидеть что-то вроде:

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

Однако я также хотел бы, чтобы он передавал выходные данные в виде списка, если я вызываю его в контексте присвоения переменной:

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

Итак ... есть ли способ динамически узнать, внутри функции, назначен ли ее вывод? Я хотел бы иметь возможность сделать это, не прибегая к передаче параметров или написанию другой функции, предназначенной для этого. Я немного погуглил, и из того, что я могу сказать, похоже, мне придется прибегнуть к игре с байт-кодом. Это действительно необходимо?

Ответы [ 7 ]

5 голосов
/ 02 мая 2009

Новое решение

Это новое решение, которое решение обнаруживает, когда результат функции используется для назначения, путем изучения своего собственного байт-кода. Запись байт-кода не производится, и он должен быть даже совместим с будущими версиями Python, поскольку для определений используется модуль кода операции.

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

Раствор 1

Это старое решение, которое обнаруживает всякий раз, когда вы вызываете функцию во время отладки в python -i или 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()
>>>
4 голосов
/ 02 мая 2009

Тем не менее, я также хотел бы передать вывод в виде списка

Вы имеете в виду «вернуть вывод в виде словаря» - будьте осторожны; -)

Одна вещь, которую вы могли бы сделать, это использовать способность интерпретатора Python автоматически преобразовывать в строку результат любого выражения. Для этого создайте пользовательский подкласс dict, который, когда его попросят преобразовать себя в строку, выполнит любое удобное форматирование, какое вы захотите. Например,

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

Конечно, вы, вероятно, могли бы придумать более красивый способ написания кода, но основная идея заключается в том, что метод __str__ создает симпатично напечатанную строку, которая представляет состояние объекта и возвращает его. Затем, если вы вернете PrettyDict из функции check_status(), когда вы наберете

>>> check_status()

вы бы увидели

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

Единственный улов в том, что

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

даст вам то же самое, потому что такое же преобразование в строку происходит как часть функции print. Но для использования этого в некотором реальном приложении я сомневаюсь, что это имело бы значение. Если вы действительно хотите, чтобы возвращаемое значение было чистым, вы могли бы сделать

>>> print repr(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'}

Дело в том, что, как говорили другие авторы, у функции в Python нет способа узнать, что будет сделано с ее возвращаемым значением ( EDIT : ладно, может быть странный способ взлома байт-кода, но не делайте этого) - но вы можете обойти это в тех случаях, когда это имеет значение.

3 голосов
/ 02 мая 2009

Там нет варианта использования для этого. Python назначает все интерактивные результаты специальной переменной с именем _.

Вы можете сделать следующее в интерактивном режиме. Работает отлично. Не смешное дело.

>>> 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'}
2 голосов
/ 02 мая 2009

Даже если есть способ сделать это, это плохая идея. Представьте, что вы пытаетесь отладить что-то, что ведет себя по-разному, в зависимости от контекста, из которого оно вызывается. Теперь попытайтесь представить, что прошло шесть месяцев, и это скрыто в какой-то системе, которая слишком велика, чтобы держать ее в голове сразу.

Будьте проще. Явное лучше, чем неявное. Просто создайте функцию pretty-print (или используйте модуль pprint) и вызовите ее для результата. В интерактивном сеансе Pythn вы можете использовать _, чтобы получить значение последнего выражения.

2 голосов
/ 02 мая 2009

Функция не может узнать, как используется ее возвращаемое значение.

Ну, как вы упомянули, вопиющие взломы байт-кода могли бы сделать это, но это было бы действительно сложно и, вероятно, хрупко. Идите по более простому явному маршруту.

0 голосов
/ 02 мая 2009

Единственный способ сделать то, что вы хотите, это действительно "играть с байт-кодом" - другого способа восстановить эту информацию не существует. Конечно, гораздо лучший способ - предоставить две отдельные функции: одну для получения статуса в качестве диктанта, другую для вызова первой и ее форматирования.

В качестве альтернативы (но не отличной архитектуры) у вас может быть одна функция, которая принимает необязательный параметр для поведения этих двух совершенно разных способов - это не очень хорошо, потому что функция должна иметь ОДНУ функцию, то есть в основном делать ОДНУ вещь, не два разных, таких как эти.

0 голосов
/ 02 мая 2009

Нет способа сделать это, по крайней мере, с помощью нормальных синтаксических процедур, потому что вызов функции и назначение являются полностью независимыми операциями, которые не знают друг друга.

Самый простой обходной путь, который я вижу для этого, состоит в том, чтобы передать флаг в качестве аргумента вашей функции check_status и обработать его соответствующим образом.

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

А потом ...

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

РЕДАКТИРОВАТЬ: Я предполагал, что вы будете печатать довольно часто, чем вы назначаете. Если это не так, поменяйте местами значение по умолчанию и значение, которое вы передаете.

...