【问题标题】:Is there anyway to get the names of passed arguments to a function in python?无论如何要获取传递给python函数的参数的名称吗?
【发布时间】:2021-02-20 11:21:08
【问题描述】:

我想知道传递给函数时的局部变量名称是什么。我不确定这是否可能。让我们考虑这个例子:

函数定义:

def show(x):
  print(x)

用法:

a = 10
show(a)

这会打印 10。但我喜欢打印“a = 10”。这在python中可能吗?

【问题讨论】:

  • 你有什么特别想用这个的吗?
  • 我喜欢它输出“23.4567 = 23.4567”或一些随机或固定的名称。
  • 原因是我有两种作业要提交给集群。 Matlab 作业 (echo "matlab -r a=1,b=2 function" | qsub) 或 shell 作业 (echo "program 1 2" | qsub)。在 matlab 案例中,我需要知道变量名。当然,我可以添加另一个参数,但只是想确定是否有更简洁的方法来实现它。
  • @MohammadMoghimi 明确表达并没有什么“不干净” :)
  • @JonClements 这就是我最后所做的! :)

标签: python


【解决方案1】:

不完全是这样。但是,您可以实现类似的效果:

def show(**kwargs):
  print(', '.join('%s=%s' % kv for kv in kwargs.items()))

show(a=20)

【讨论】:

  • 要专门接听 OP,电话是show(a=a, otherarg=otherarg, etc=etc),必要时可以延长。
【解决方案2】:

不,您无法知道用于将值传递给您的函数的局部变量的名称。

无论如何,这都是一项不可能完成的任务。以下示例中的变量名称是什么?

arguments = ('a', 1, 10)
somefunction(*(arguments[:2] + [10]))

这里我们传入 3 个参数,其中两个取自我们之前定义的元组,一个字面量值,这三个参数都使用可变参数列表语法传入。

【讨论】:

  • 我的回答是“不”——有一些警告——我有一种非常可怕的感觉,你可以用 syshooks 做一些事情——但我坚持“不”——“为什么这很重要你愿意吗?”
  • @JonClements:您也可以尝试使用sys._getframe(1) 并反编译代码框架.. 颤抖不是因为胆小的人,除非 OP 有一个 该死的 充分的理由这样做,否则我不会对此进行探索。
  • 我相信缺乏分析/其他深奥的目的 - 没有:)
  • @dash-tom-bang ummm,应该是profiling/other/esoteric(或者只是没有other
  • @JonClements 明白了,抱歉!当我怀疑有人建议分析(或单元测试)不是每个人都应该做的事情时,我的怒气让我大发雷霆!
【解决方案3】:

我喜欢 Python 编程常见问题解答中的 answer to this question,引用 Fredrik Lundh 的话:

与您在门廊上找到的那只猫的名字相同: 猫(物体)本身不能告诉你它的名字,它 并不在乎——所以找出它叫什么的唯一方法是 询问所有邻居(命名空间)是否是他们的猫(对象)...

....如果您发现它有很多名字,或者根本没有名字,请不要感到惊讶!

【讨论】:

  • 是的 - 一定会喜欢 effbot 的表达方式 :) [话虽如此,但我相信 Dirk Gently 的其中一本书,Dirk 的任务之一是尝试找出一个cat 并且确实做到了:)!]
  • 我从阅读 effbot 的代码中学到了更多关于 Python 的知识,而不是从我所做的任何其他事情中学到的。他打败了我的 C++。
【解决方案4】:

我预感下面的解决方案会受到一些批评

def show(*x):
    for el in x:
        fl = None
        for gname,gobj in globals().iteritems():
            if el==gobj:
                print '%s == %r' % (gname,el)
                fl = True
        if not fl:
            print 'There is no identifier assigned to %r in the global namespace' % el

un = 1
y = 'a'
a = 12
b = c = 45
arguments = ('a', 1, 10)
lolo = [45,'a',a,'heat']

print '============================================'
show(12)
show(a)
print '============================================'
show(45)
print
show(b)
print '============================================'
show(arguments)
print
show(('a', 1, 10))
print '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
show(*arguments)
print '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
show(*(arguments[1:3] + (b,)))

结果

============================================
a == 12
a == 12
============================================
c == 45
b == 45

c == 45
b == 45
============================================
arguments == ('a', 1, 10)

arguments == ('a', 1, 10)
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
y == 'a'
un == 1
There is no identifier assigned to 10 in the global namespace
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
un == 1
There is no identifier assigned to 10 in the global namespace
c == 45
b == 45

【讨论】:

  • 创造力+1。但考虑到它必须在全局命名空间内,它似乎有点有限,而且答案本身似乎有点冗长。我希望有人会通过查找帧堆栈来做某事。也许我会自己做。或者也许我会认为这不值得。
  • 我想我有一个更好的答案,它搜索调用者的本地命名空间,加上全局命名空间,加上内置,正确处理阴影,以及处理多个标识符的情况分配给相同的值:stackoverflow.com/a/28634996/901641
【解决方案5】:

这似乎在 Python 中是不可能的,但在 C++ 中实际上是可能的。

#define show(x)   std::cout << #x << " = " << x << std::endl

【讨论】:

    【解决方案6】:

    使用readline的新解决方案

    如果您处于交互式会话中,这里有一个非常简单的解决方案,通常会起作用:

    def show(x):
        from readline import get_current_history_length, get_history_item
        print(get_history_item(get_current_history_length()).strip()[5:-1] + ' = ' + str(x))
    

    它所做的只是读取交互式会话缓冲区中的最后一行输入,删除任何前导或尾随空格,然后为您提供除前五个字符(希望是 show()和最后一个字符(希望是 ))之外的所有内容,从而使您得到传入的任何内容。

    例子:

    >>> a = 10
    >>> show(a)
    a = 10
    >>> b = 10
    >>> show(b)
    b = 10
    >>> show(10)
    10 = 10
    >>> show([10]*10)
    [10]*10 = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
    >>> show('Hello' + 'World'.rjust(10))
    'Hello' + 'World'.rjust(10) = Hello     World
    

    如果您在 OS X 上使用随附的 Python 版本,则默认情况下您没有安装 readline,但您可以通过 pip 安装它。如果您在 Windows 上,readline 不适合您...您可能可以使用来自pippyreadline 但我从未尝试过,所以我不能说它是否是可接受的替代品与否。

    我把让上面的代码更防弹的问题留给读者作为练习。要考虑的事情是如何让它处理这样的事情:

    show(show(show(10)))
    show(
    10
    )
    

    如果您希望这种东西显示脚本中的变量名称,您可以查看使用检查并获取调用框架的源代码。但是考虑到我想不出你为什么会想在脚本中使用show(),或者为什么你会像我上面所做的那样让函数复杂化只是为了处理故意搞砸的人,我不会浪费我的时间现在想起来了。

    使用inspect的原始解决方案

    这是我最初的解决方案,它更复杂,有更明显的警告,但更便携,因为它只使用inspect,而不是readline,因此可以在所有平台上运行,无论您是否在交互式会话或脚本:

    def show(x):
    
        from inspect import currentframe
    
        # Using inspect, figure out what the calling environment looked like by merging
        #   what was available from builtin, globals, and locals.
        # Do it in this order to emulate shadowing variables
        #   (locals shadow globals shadow builtins).
    
        callingFrame = currentframe().f_back
        callingEnv = callingFrame.f_builtins.copy()
        callingEnv.update(callingFrame.f_globals)
        callingEnv.update(callingFrame.f_locals)
    
        # Get the variables in the calling environment equal to what was passed in.
        possibleRoots = [item[0] for item in callingEnv.items() if item[1] == x]
    
        # If there are none, whatever you were given was more than just an identifier.
        if not possibleRoots:
            root = '<unnamed>'
        else:
            # If there is exactly one identifier equal to it,
            #   that's probably the one you want.
            # This assumption could be wrong - you may have been given
            #   something more than just an identifier.
            if len(possibleRoots) == 1:
                root = str(possibleRoots[0])
            else:
                # More than one possibility? List them all.
                # Again, though, it could actually be unnamed.
                root = '<'
                for possibleRoot in possibleRoots[:-1]:
                    root += str(possibleRoot) + ', '
                root += 'or ' + str(possibleRoots[-1]) + '>'
    
        print(root + ' = ' + str(x))
    

    这是一个完美运行的案例(问题中的那个):

    >>> a = 10
    >>> show(a)
    a = 10
    

    这是另一个有趣的案例:

    >>> show(quit)
    quit = Use quit() or Ctrl-Z plus Return to exit
    

    现在您知道该功能是如何在 Python 解释器中实现的 - quitstr 的内置标识符,表示如何正确退出。

    在某些情况下,它可能比您想要的要少,但是...可以接受吗?

    >>> b = 10
    >>> show(b)
    <a, or b> = 10
    >>> show(11)
    <unnamed> = 11
    >>> show([a])
    <unnamed> = [10]
    

    这是一个例子,它打印出一个真实的陈述,但绝对不是你要找的:

    >>> show(10)
    <a, or b> = 10
    

    【讨论】:

    • 与其将['a','b'] 列表转换为&lt;a, or b&gt;,不如将​​对象作为x 传递给show(x)...只需将id(x) 与相应的匹配globals() 中的对象?你甚至有一个简短的键列表来搜索。这个问题真正困难的部分是当您正在查找在本地范围内定义的变量的名称时 - 比如说 a = 10 在函数内部定义,并且您想从 locals() 中提取名称引用和值在函数内部。
    • @MikeMcKerns:你自己试过吗?当我尝试比较 id(x) 的结果而不是值时,我得到了完全相同的结果。我相信ids 与对象/值相关联,而不是标识符。作为一种快速简便的方法,您可以尝试:a = 10; b = 10; id(a); id(b); id(10) - 您会看到所有三个都具有相同的id
    • @MikeMcKerns:我已经对此进行了更新,现在有两种可能的解决方案。我的新版本非常短且幼稚,只使用readline。与我原来的解决方案相比,它有一个不同的、(对我来说)更容易接受的警告列表。
    • 原因a = 10; b = 10; id(10) is id(a) and id(10) is id(b)ab 只是对IntType 10 实例的名称引用——这是在python 启动时预先构建的。大约 1M ints 是预先构建的。无论如何,a 只是一个指向 10 的“指针”(名称引用),所以它们当然具有相同的id——它们是同一个对象。这就是为什么没有保证可以从值中回溯名称的原因......这就像试图获取与 dict -- not guaranteed to be unique. 中的某个值相对应的键一样。这是我的观点。
    • 由于您的代码(两种变体)现在开始看起来像dill.source,您可能还想查看dill.pointers。有一种方法可以在每种情况下跟踪哪个对象指向哪个对象,除非多个名称引用指向同一个对象 - 基本上是通过gc 模块,它允许您跟踪子父指针关系是。
    【解决方案7】:

    这是一个只有在使用 f-strings 的 Python 3.6 才成为可能的答案:

    x = 10
    print(f'{x=}')  # Outputs x=10
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-02
      • 1970-01-01
      • 2019-11-20
      • 1970-01-01
      • 2020-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多