简体   繁体   English

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

[英]Is there a way to check whether function output is assigned to a variable in Python?

In Python, I'd like to write a function that would pretty-print its results to the console if called by itself (mostly for use interactively or for debugging). 在Python中,我想编写一个将其结果漂亮地打印到控制台的函数,如果它本身被调用(主要用于交互使用或调试)。 For the purpose of this question, let's say it checks the status of something. 出于这个问题的目的,我们假设它检查事物的状态。 If I call just 如果我打电话给

check_status()

I would like to see something like: 我希望看到类似的东西:

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

However, I would also like it to pass the output as a list if I call it in the context of a variable assignment: 但是,如果我在变量赋值的上下文中调用输出,我也希望它将输出作为列表传递:

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... is there a way to dynamically know, within a function, whether its output is being assigned? 那么...有没有一种方法可以动态地知道一个函数中的输出是否已分配? I'd like to be able to do this without resorting param passing, or writing another function dedicated for this. 我希望能够在不诉诸参数传递或编写另一个专用于此的函数的情况下做到这一点。 I've Googled for a bit, and from what little I can tell it looks like I'd have to resort to playing wth the bytecode. 我已经用Google搜索了一下,从一点也看不出来,看来我不得不诉诸字节码了。 Is that really necessary? 那真的有必要吗?

New Solution 新解决方案

This is a new that solution detects when the result of the function is used for assignment by examining its own bytecode. 这是一个新的解决方案,它通过检查其自身的字节码来检测何时将函数的结果用于赋值。 There is no bytecode writing done, and it should even be compatible with future versions of Python because it uses the opcode module for definitions. 没有字节码的编写,它甚至应该与将来的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

Solution 1 解决方案1

This is the old solution that detects whenever you are calling the function while debugging under python -i or PDB. 这是旧的解决方案,该解决方案在使用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()
>>>

However, I would also like it to pass the output as a list 但是,我也希望它将输出作为列表传递

You mean "return the output as a dictionary" - be careful ;-) 您的意思是“将输出作为字典返回”-注意;-)

One thing you could do is use the ability of the Python interpreter to automatically convert to a string the result of any expression. 您可以做的一件事是使用Python解释器的功能,将任何表达式的结果自动转换为字符串。 To do this, create a custom subclass of dict that, when asked to convert itself to a string, performs whatever pretty formatting you want. 为此,创建一个dict的自定义子类,当要求将其自身转换为字符串时,该子类将执行您想要的任何漂亮格式。 For instance, 例如,

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

Of course you could probably come up with a prettier way to write the code, but the basic idea is that the __str__ method creates the pretty-printed string that represents the state of the object and returns it. 当然,您可能会想出一种更漂亮的方式来编写代码,但是基本思想是__str__方法创建表示对象状态的漂亮打印字符串并返回它。 Then, if you return a PrettyDict from your check_status() function, when you type 然后,如果您在PrettyDictcheck_status()函数返回了PrettyDict

>>> check_status()

you would see 你会看到

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

The only catch is that 唯一的收获是

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

would give you the same thing, because the same conversion to string takes place as part of the print function. 会给你同样的事情,因为作为print功能的一部分,对字符串的相同转换也发生了。 But for using this in some real application, I doubt that that would matter. 但是对于在某些实际应用中使用它,我怀疑这是否重要。 If you really wanted to see the return value as a pure dict, you could do 如果您真的想将返回值视为纯字典,则可以执行

>>> print repr(not_robot_stat)

instead, and it should show you 相反,它应该告诉你

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

The point is that, as other posters have said, there is no way for a function in Python to know what's going to be done with it's return value ( EDIT : okay, maybe there would be some weird bytecode-hacking way, but don't do that) - but you can work around it in the cases that matter. 关键是,正如其他发布者所说的那样,Python中的函数无法知道返回值将要做什么( 编辑 :好的,也许会有一些奇怪的字节码破解方法,但是不要这样做)-但在重要的情况下,您可以解决它。

There's no use case for this. 没有用例。 Python assigns all interactive results to a special variable named _ . Python将所有交互结果分配给名为_的特殊变量。

You can do the following interactively. 您可以交互地执行以下操作。 Works great. 效果很好。 No funny business. 没有可笑的事。

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

There is no way for a function to know how its return value is being used. 函数无法知道如何使用其返回值。

Well, as you mention, egregious bytecode hacks might be able to do it, but it would be really complicated and probably fragile. 就像您提到的那样,出色的字节码黑客也许可以做到这一点,但这确实很复杂,而且很脆弱。 Go the simpler explicit route. 走更简单的显式路线。

Even if there is a way to do this, it's a bad idea. 即使有办法做到这一点,这也是一个坏主意。 Imagine trying to debug something that behaves differently depending on the context it's called from. 想象一下尝试调试某些行为,具体取决于调用它的上下文。 Now try to imagine that it's six months from now and this is buried in part of some system that's too big to keep in your head all at once. 现在,尝试想象距离现在已经六个月了,并且它被埋在某个系统的一部分中,该系统太大了,无法一下子让您全神贯注。

Keep it simple. 把事情简单化。 Explicit is better than implicit. 显式胜于隐式。 Just make a pretty-print function (or use the pprint module) and call that on the result. 只需创建一个漂亮的打印函数(或使用pprint模块)并在结果上调用它即可。 In an interactive Pythn session you can use _ to get the value of the last expression. 在交互式Pythn会话中,您可以使用_来获取最后一个表达式的值。

There is no way to do this, at least not with the normal syntax procedures, because the function call and the assignment are completely independent operations, which have no awareness of each other. 这是没有办法的,至少对于普通的语法过程而言,这是没有办法的,因为函数调用和赋值是完全独立的操作,彼此之间不了解。

The simplest workaround I can see for this is to pass a flag as an arg to your check_status function, and deal with it accordingly. 我可以看到的最简单的解决方法是将一个标志作为arg传递给您的check_status函数,并进行相应的处理。

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

And then... 接着...

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

EDIT: I assumed you'd be pretty printing more often than you'd assign. 编辑:我想你会比分配更多的时间来进行漂亮的打印。 If that's not the case, interchange the default value and the value you pass in. 如果不是这种情况,请交换默认值和您传入的值。

The only way to do what you want is indeed "to play with the bytecode" -- there is no other way to recover that info. 做您想要的事情的唯一方法确实是“使用字节码” —没有其他方法可以恢复该信息。 A much better way, of course, is to provide two separate functions: one to get the status as a dict, the other to call the first one and format it. 当然,更好的方法是提供两个单独的函数:一个将状态作为dict,另一个调用第一个并将其格式化。

Alternatively (but not an excellent architecture) you could have a single function that takes an optional parameter to behave in these two utterly different ways -- this is not excellent because a function should have ONE function, ie basically do ONE thing, not two different ones such as these. 另一种选择(但不是优秀的体系结构),您可以有一个函数,该函数需要一个可选参数以这两种完全不同的方式运行-这不是很好,因为一个函数应该具有一个函数,即基本上可以做一件事情,而不是两个不同这样的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM