函数和局部变量可以同名吗?

Can a function and local variable have the same name?

这是我的意思的一个例子:

def foo():
    foo = 5
    print(foo + 5)

foo()
# => 10

代码没有产生任何错误并且运行完美。这与变量和函数不应该具有相同名称除非您覆盖它们的想法相矛盾。 它为什么有效?当应用于实际代码时,我应该使用不同的 function/local 变量名,还是完全没问题?

foo = 5 在您的函数中创建一个局部变量。 def foo 创建一个全局变量。这就是为什么他们可以有相同的名字。

如果您在 foo() 函数中引用 foo,则您指的是局部变量。如果您在该函数之外引用 foo,则您指的是全局变量。

由于它显然会给试图遵循代码的人造成混淆,您可能不应该这样做。

答案是肯定的。 函数是 Python 中的第一个 class 个对象。 foo 函数和 foo 变量之间没有根本区别。这两个都是对内存的引用并且都有一个范围,所以它们都等同于一个变量。 如果你把 foo 定义为一个函数,然后不在本地覆盖它,它就像一个全局变量(取自上层作用域):

def foo():
  print(foo)

然后如果你用局部变量覆盖它,它只会在函数范围内定义一个局部变量:

def foo():
  foo = 3
  print(foo)

在Python中,每个引用(变量)都可以在特定作用域的生命周期内被覆盖。你可以试试:

def foo(): pass
foo = 3

这将覆盖 foo 的值,它现在将指向 3 而不是内存中的函数 foo。

嗯,这是因为每个 foo 的变量范围。函数foo()在全局范围内,因此可以在其他函数内和函数外调用。

然而,变量foo在本地范围内。这意味着它只“存在于”函数内部,不能在函数外部被调用或引用。

因此,每个不同的函数在调用时都会创建自己的局部作用域,而一旦作用域被销毁(函数结束),其中创建的变量就会被遗忘。

全局变量可以在局部范围内访问,局部变量不能在全局范围内访问。

如果要在全局范围内创建变量,可以这样调用:

global var

这是全局和局部范围的示例:

var=1
def foo1():
  var=3
foo1()
print(var) #prints 1 because the "var" in foo1() is locally assigned
def foo2():
  global var
  var=2
foo2()
print(var) #prints 2 because "var" is global

因此,您的函数有效,因为我只在本地而非全局分配名称 foo

但是,如果您稍后在该函数中调用foo(),它将引发错误,因为该局部作用域已将一个int值分配给foo , 不是函数,因此不可调用。

其他人尚未提及的内容:Python 是一种 动态 语言,只有很少的静态检查。所以当你写

def foo():
    foo = 5
    print(foo + 5)

您可能一直认为这是一个“矛盾”——foo 怎么能同时是一个函数和一个整型变量呢?一阶答案是“这是两个不同的 foo”,因为内部 foo 只是一个局部变量,与全局名称 foo 无关。但是使它成为全局的,并且代码仍然有效!

def foo():
    global foo
    foo = 5
    print(foo + 5)

foo()  # OK, prints 10

这个代码中只有一个foo!但是,它不是“既是变量又是函数”。首先,在第 1-4 行,我们将 foo 定义为具有特定主体的函数 — 但我们尚未执行该主体。 在第 4 行之后,全局 foo 持有一个函数。然后,在第 6 行,我们实际上调用了 foo 所引用的函数,它执行了它的主体。主体做的第一件事是将 5 分配给 foo... 并且 now foo 持有一个整数。通过将新值分配给 foo 的 运行 代码,我们更改了 foo 的值。它曾经是那个功能在那里;现在是 5。在像 Python 这样的动态类型语言中,这没什么问题。

def foo():
    global foo
    foo = 5
    print(foo + 5)

foo()  # OK, prints 10 (and incidentally assigns a new value to foo)
foo()  # Raises TypeError: 'int' object is not callable

使用相同名称的另一个问题显然是编写递归时的歧义:

def fibo(n: int) -> int:
    if n <= 1:
        fibo = 1
    else:
        fibo = fibo(n - 1) + fibo(n - 2)
    return fibo

结果:

UnboundLocalError: local variable 'fibo' referenced before assignment

它发生是因为在函数内部定义了 fibo。 当 Python 尝试 运行 fibo(n - 1) 时,它会搜索本地 fibo 变量,但找不到它的值。

简短的评论:pylint 用警告标记它。 pylint 是我们的朋友!

在python中,变量的作用域是从当前最内层作用域向全局作用域搜索确定的。

Python Scopes and Namespaces(Emphasis mine)

  • A namespace is a mapping from names to objects.
  • A scope is a textual region of a Python program where a namespace is directly accessible. “Directly accessible” here means that an unqualified reference to a name attempts to find the name in the namespace.

At any time during execution, there are 3 or 4 nested scopes whose namespaces are directly accessible:

  1. the innermost scope, which is searched first, contains the local names
  2. the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names
  3. the next-to-last scope contains the current module’s global names
  4. the outermost scope (searched last) is the namespace containing built-in names.

A special quirk of Python is that – if no global or nonlocal statement is in effect – assignments to names always go into the innermost scope.

示例中,

# Current module's scope starts here.

def foo():
    # Local Scope Starts here.
    foo = 5
    print(foo + 5)
    # Local scope Ends here.

foo()

# Current module's scope ends here.

应用上述规则解析范围

  • foo = 5 属于 本地范围 因为没有 globalnonlocal 语句。函数内部的foo属于函数的局部作用域。

  • print(foo + 5) 解析函数内部的 foo,因为 python 从最内层的范围开始搜索。范围解析规则 1 和 2 适用。这里的最内层作用域与封闭函数的作用域相同。

  • foo() 在模块级作用域被调用。 Python 开始搜索最内层的可用范围,这里是模块级。因此,它解析为在模块级别定义的函数。

另一个例子,

def foo():
    print(foo + 5)

foo()
# TypeError: unsupported operand type(s) for +: 'function' and 'int'
  • foo() 在模块级作用域被调用。 Python 开始搜索最内层的可用范围,这里是模块级。因此,它解析为在模块级别定义的函数。
  • print(foo + 5) 也将解析为模块级 foo 函数,因为 python 在封闭函数范围内开始搜索但未找到匹配项。移动到下一个范围,即模块级别并找到匹配项。匹配的是foo函数。因此,错误。

另一个例子,

def foo():
    foo = "str"
    def inner():
#(1)    foo = 1 # Commented this line
        print(foo)
    inner()
    print(foo)

foo()
# str
# str

# Uncommenting (1) gives output as

foo()
# 1
# str

阅读更多关于 Scoping rules