【问题标题】:Conflicting variable and function names in pythonpython中的变量和函数名称冲突
【发布时间】:2013-04-14 23:50:36
【问题描述】:

假设我有以下功能:

def xplusy(x, y):
    return x+y

def xplus1(x):
    xplusy = xplusy(x, 1)
    return xplusy

现在如果我调用a = xplus1(4),它会抛出以下错误:

UnboundLocalError: local variable 'xplusy' referenced before assignment

报错是因为命名冲突,如果我重新定义xplus1如下:

def xplus1(x):
    s = xplusy(x, 1)
    return s

效果很好。

为什么会这样:编译器不能正确区分变量和函数调用?

有什么办法吗?

【问题讨论】:

  • 为什么要让你的生活复杂化?换个名字就行了。
  • 你可以这样做return xplusy(x,1),像这样:def xplus1(x): return xplusy(x,1)
  • 这通常是一件很自然的事情,并且在其他语言中广泛使用。更改它不是问题,但如果是在代码中添加几个字符的问题,我可以接受。为什么理论上会发生这种情况我也不清楚......
  • Python 中的“变量”和函数没有区别。函数和其他对象一样是对象。
  • @sashkello 您还应该阅读 python namespaces。命名空间本质上是从名称到对象的映射,因此可以覆盖任何名称,包括所有内置函数、您导入到本地命名空间的任何内容以及您自己定义的所有内容。

标签: python


【解决方案1】:

发生这种情况的原因是因为xplusy 作为全局变量存在于您的作用域中,除非您明确说xplusy 是global,否则无法更改它。

def xplusy(x,y):
    return x+y

def xplus1(x):
    global xplusy
    xplusy = xplusy(x,1)
    return xplusy

但是,这将使xplusy 引用int、float 或xplusy 第一次返回的任何内容,这意味着在第一次之后它将抛出TypeErrors。

更pythonic的方式很可能是

def xplus1(x):
    return xplusy(x,1)

或使用functools 模块:

from functools import partial
xplus1 = partial(xplusy,y=1) #since you wanted to override y with 1

如果你不关心哪个参数被覆盖,你可以简单地做

xplus1 = partial(xplusy,1)

【讨论】:

    【解决方案2】:

    在某些情况下,例如在回调中,您可以通过静态名称(funcName 而不是funcName())来引用函数。因此,在 Python 中,变量名是为函数保留的。很像您如何使用存储在变量中的 LAMBDA 函数。

    【讨论】:

      【解决方案3】:

      在 python 中,函数是第一类对象,这意味着它与任何其他对象一样。

      更多关于What are “first class” objects?的信息

      【讨论】:

      • 嗯,函数也是 Common Lisp 中的一等对象,但 Common Lisp 有一个单独的函数命名空间。
      【解决方案4】:

      在 Python 中,xplusy 可以引用任何内容。你可以这样做:

      def xplusy(x, y):
          return x+y
      
      def otherfunction(x, y):
          return x*y
      
      def xplus1(x):
          xplusy = otherfunction
          return  xplusy(x, 1)
      

      而xplusy 变量将在xplus1 范围内引用otherfunction。

      xplus1(2)
      >>> 2
      

      结果是 2 * 1 而不是 2 + 1。小心分配。使用尽可能多的变量。

      【讨论】:

      • 谢谢你,看起来很奇怪:)
      【解决方案5】:

      在 Python 中,函数是数据,输入是动态的。这意味着以下行是有效的 Python:

      def func(x):
          return x + 3
      
      func = 3
      

      func 现在是一个整数。不再引用原函数func。 func 最初是一个函数这一事实与将来可以分配给它的数据类型没有任何关系。 (这就是“动态类型”的意思。)

      因此,由于没有静态类型,并且“函数”是有效的数据类型,Python 解释器区分函数和同名引用的数据是没有意义的。因此,在给定的范围内,没有办法使用相同的非限定变量名来表示两种不同的东西。

      在您的特定情况下,如果您的 xplus1 函数中的代码有任何含义,则意味着“计算 xplusy(x,1) 的值并将 该值 分配给变量 xplusy --从而失去对 函数 xplusy 的引用。”但是,在函数的范围内,解释器不允许您对该范围之外的变量进行赋值,因此它假定通过编写赋值语句,您正在引入一个新的局部变量xplusy。但是,尚未定义局部变量,因此您尝试调用它xplusy(x,1) 失败。全局定义的函数不作为后备调用,因为同样,您不能让两个非限定名称相同并指向同一范围内的不同数据。


      另一个演示“在一个范围内不重复变量名”规则的示例(实际上,我只是在尝试构建此答案时使用提示符时才发现的):

      >>> def f1():
      ...     a = xplusy(3,4)
      ...     xplusy = 5
      ...     print xplusy
      ...
      >>> f1()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "<stdin>", line 2, in f1
      UnboundLocalError: local variable 'xplusy' referenced before assignment
      >>> def f1():
      ...     a = xplusy(3,4)
      ...     print a
      ...
      >>> f1()
      7
      

      这表明确实是范围,而不是语句需要唯一名称。


      编辑:这是一篇非常酷的帖子,解释了这个和其他与范围相关的行为:http://me.veekun.com/blog/2011/04/24/gotcha-python-scoping-closures/

      【讨论】:

      • 模块名称也是这样吗?我正在维护一个 python 脚本,它有一个与模块同名的变量。 (时间,如果你想知道的话。)
      • @Jack 我不确定你在问什么。确实,import 语句将模块名称(不是 变量名称)分配给变量,因此它们不受与模块同名的全局或局部变量的影响;也就是说,time = 3; from time import time as epoch_time; print time; print epoch_time() 将打印3,后跟自纪元以来的秒数。但是,time = 3; from time import time; print time 将打印 &lt;built-in function time&gt;,因为变量 time 由 import 语句重新分配。
      • 次要更正:import 语句当然也可以将模块的成员分配给变量,而不仅仅是模块本身。在模块本身的范围内,模块成员当然是变量。真正的重点是import 语句有两部分,一个变量和一个值。变量名在当前范围内(因为它是由import 语句执行的赋值的目标),但值(模块或模块成员)根本不在“范围”内,而是而是取决于模块在sys.path 中的存在及其内容。
      猜你喜欢
      • 2013-03-26
      • 2016-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-30
      • 1970-01-01
      • 2018-02-20
      • 2011-08-21
      相关资源
      最近更新 更多