2

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

那么......有没有办法在一个函数中动态地知道它的输出是否被分配?我希望能够在不使用参数传递或为此编写另一个专用函数的情况下做到这一点。我已经用谷歌搜索了一段时间,从我所能知道的一点点看来,我不得不求助于使用字节码。这真的有必要吗?

4

7 回答 7

5

新解决方案

这是一个新的解决方案,通过检查自己的字节码来检测函数的结果何时用于分配。没有编写字节码,它甚至应该与 Python 的未来版本兼容,因为它使用 opcode 模块进行定义。

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()
>>>
于 2009-05-02T01:35:18.753 回答
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()

你会看到

漂亮的打印机状态检查 0.02v
注意:这完全不是为巨型机器人编写的
==================================
系统运行:...好的
离子炮充电时间为 9m 21s
助推器火箭处于 AFTERBURNER 状态
范围检查是最佳的
火箭燃料消耗 10h 19m 40s
供应的啤酒是 WICKSE LAGER 类型,最适合冷藏
建议的标语是 01_FIGHTING_SPIRIT_GOGOGO
虚拟...开启

唯一的问题是

>>> 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': '该死的'}

关键是,正如其他海报所说,Python 中的函数无法知道如何处理它的返回值(编辑:好吧,也许会有一些奇怪的字节码黑客方式,但不要'不要那样做) - 但你可以在重要的情况下解决它。

于 2009-05-02T01:32:18.353 回答
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'}
于 2009-05-02T01:42:49.217 回答
2

函数无法知道它的返回值是如何被使用的。

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

于 2009-05-02T01:15:11.857 回答
2

即使有办法做到这一点,这也是一个坏主意。想象一下,尝试根据调用它的上下文来调试行为不同的东西。现在试着想象从现在开始还有六个月的时间,它被埋在某个系统的一部分中,这个系统太大而无法一次全部留在你的脑海中。

把事情简单化。显式优于隐式。只需制作一个漂亮的打印函数(或使用 pprint 模块)并在结果上调用它。在交互式 Pythn 会话中,您可以使用 _ 来获取最后一个表达式的值。

于 2009-05-02T05:01:11.957 回答
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.

编辑:我认为你会比你分配的更频繁地打印。如果不是这种情况,请交换默认值和您传入的值。

于 2009-05-02T01:16:21.480 回答
0

做你想做的事情的唯一方法确实是“使用字节码”——没有其他方法可以恢复该信息。当然,更好的方法是提供两个单独的函数:一个获取状态为 dict,另一个调用第一个函数并对其进行格式化。

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

于 2009-05-02T01:18:41.367 回答