有没有办法检查函数输出是否分配给Python中的变量?

时间:2009-05-02 01:08:19

标签: python functional-programming bytecode

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

那么......有没有办法在函数中动态地知道它的输出是否被分配?我希望能够在不借助param传递或者编写专门用于此的其他功能的情况下完成此操作。我用谷歌搜索了一下,从我可以说的一点点看起来我不得不求助于使用字节码。这真的有必要吗?

7 个答案:

答案 0 :(得分:5)

新解决方案

这是一个新的解决方案,通过检查自己的字节码来检测何时将函数的结果用于赋值。没有完成字节码编写,它甚至应该与未来版本的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()
>>>

答案 1 :(得分:4)

  

但是,我还希望它将输出作为列表传递

你的意思是“将输出作为字典返回” - 小心; - )

您可以做的一件事是使用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函数的一部分。但是为了在一些实际应用中使用它,我怀疑那很重要。如果你真的想把返回值看作纯粹的dict,你可以做到

>>> 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 :好吧,也许会有一些奇怪的字节码黑客方式,但不要这样做 - 但你可以在重要的情况下解决它。

答案 2 :(得分:3)

这没有用例。 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'}

答案 3 :(得分:2)

函数无法知道其返回值的使用方式。

好吧,正如你所提到的,令人震惊的字节码黑客可能会做到这一点,但它会非常复杂并且可能很脆弱。走更简单的显式路线。

答案 4 :(得分:2)

即使有办法做到这一点,这也是一个坏主意。想象一下,尝试调试一些行为不同的东西,具体取决于它所调用的上下文。现在试着想象它是从现在开始的六个月之后,它被埋没在某个系统的一部分中,这个系统太大而无法立刻保留在你的头脑中。

保持简单。显式优于隐式。只需创建一个漂亮的打印功能(或使用pprint模块)并在结果上调用它。在交互式Pythn会话中,您可以使用_来获取最后一个表达式的值。

答案 5 :(得分:0)

没有办法做到这一点,至少不能用正常的语法程序,因为函数调用和赋值是完全独立的操作,它们彼此没有意识。

我能看到的最简单的解决方法是将一个标志作为一个arg传递给你的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.

编辑:我认为你的印刷时间比你指定的要多。如果不是这种情况,请交换默认值和传入的值。

答案 6 :(得分:0)

实现你想要的唯一方法就是“使用字节码” - 没有其他方法可以恢复该信息。当然,更好的方法是提供两个独立的功能:一个用于获取状态为dict,另一个用于调用第一个并对其进行格式化。

或者(但不是一个优秀的架构)你可以有一个单独的函数,它使一个可选参数以这两种完全不同的方式运行 - 这不是很好,因为一个函数应该有一个函数,即基本上做一件事,不是两个不同的,比如这些。

相关问题