当前位置: 首页 > 面试题库 >

Python:名称解析;函数def的顺序

平和雅
2023-03-14
问题内容

我有一个非常简单的示例:

#!/usr/bin/env python

#a()  # 1: NameError: name 'a' is not defined
#b()  # 1: NameError: name 'b' is not defined
#c()  # 1: NameError: name 'c' is not defined

def a():
    c()   # note the forward use here...

#a()  #2: NameError: global name 'c' is not defined 
#b()  #2: NameError: name 'b' is not defined
#c()  #2: NameError: name 'c' is not defined

def b():
    a()

#a()   #3: NameError: global name 'c' is not defined    
#b()   #3: NameError: global name 'c' is not defined
#c()   #3: NameError: name 'c' is not defined

def c():
    pass

a()    # these all work OK...   
b()
c()

我有3个名为的函数a()b()并按c()字母顺序在Python源文件中定义。每个函数定义的主体都是对其他函数之一的调用。您可以通过我的评论看到,我必须在它们的定义下方(在文本文件中)对这些函数中的第一个进行初始调用,但是您不一定需要在另一个调用它的函数之上定义一个函数。

当然,在所有函数定义(在Python和许多其他语言中)下都具有第一个可执行代码似乎是一种常见的做法,现在我明白了为什么。在C和C
++中,头文件会处理此问题。在Pascal中,您必须先使用名称定义。

例如,假设您在Python中具有以下功能:

def a(a_arg):          c(a_arg)
def b(b_arg):          a()
def c(a_arg,b_arg):    b(b_arg)
a(1)

TypeError: c() takes exactly 2 arguments (1 given)在其他错误为编译时的运行时,它将正常失败。(在C语言中,它将编译然后神秘地失败…)

在Perl中,由于子例程名称通常在运行时解析,因此您可以按任何顺序使用Perl定义和代码:

#!/usr/bin/env perl

a();
b();
c();

sub a{ c(); }
sub b{ a(); }
sub c{ return; }

在C语言中,使用尚未原型化且不应忽略的函数将是错误或警告(取决于实现)。

您可以拥有:

void a(void) { c(); }   /* implicitly assumed to be int c(...) unless prototyped */
void b(void) { a(); }
void c(void) { return; }

int main(void) {
    a();
    return EXIT_SUCCESS;
}

我的假设和困惑是:如果Python在运行时才解析子例程名称,为什么源编译阶段会因尚未定义的子例程名称的前向声明而失败?是否在某个地方(除了观察其他代码之外)记录了您无法在子例程定义上方的源文件中包含代码?

似乎Python有元素动态名称解析(利用的c()a()下面的源文件在其定义中之前)和静态名称解析(Python中的失败运行呼叫的元素a(),如果放置在其定义的上方在源文件中)

是否有Python版本的THIS
DOCUMENT
涵盖了Perl可执行文件的生命周期以及如何在源文件解释和运行时之间解析名称?

在Python脚本的定义顺序上是否有明确的描述,指出状态函数可以具有其他子例程名称的正向定义,而主代码则不能?

编辑和结论

经过一些激烈的评论和我的一些研究,我得出的结论是,我的问题实际上更多地是关于如何解析名称以及如何在Python中定义名称空间,范围和模块。

从顶部:

“必须在当前名称空间中调用可调用对象之前对其进行定义。”
而这个环节上范围和名称

从S.Lott:

“在代码块中使用名称时,将使用最近的封闭范围来解析它。”
并链接到Python脚本的执行寿命。

从Python文档中:

“作用域定义了块中名称的可见性。”
从Python执行模型

“一个模块可以包含可执行语句以及函数定义。”
在有关模块的更多信息中

“实际上,函数定义也是被“执行”的“语句”;模块级函数的执行会在模块的全局符号表中输入函数名称。” 在其脚注中。

我自己的想法(Du!):

  1. Python将每个Python源文件都视为“模块”:“模块是包含Python定义和语句的文件。”

  2. 与Perl(我有更多经验)不同,Python在读取模块时执行模块。因此,引用同一模块中尚未定义的功能的立即可执行语句失败。


问题答案:

定义的顺序就是“在调用之前必须定义所有内容”。就是这样。

编辑 (将答案包括在注释中,阐明):

原因之类的

def call_a():
    a()

def a():
    pass

call_a()

工作原理,当你有a()call_a()之前a甚至定义为一个功能是因为Python实际上只查找值的上的符号 按需
的基础。当call_a被评估时,a()呼叫基本上存储字节码指令“查找什么a是并称之为” 在时机成熟时
,直到你坐下来的实际调用这是不是call_a()在底部。

这是call_a(via
dis.dis)的反汇编后的字节码:

Disassembly of call_a:
  2           0 LOAD_GLOBAL              0 (a)
              3 CALL_FUNCTION            0
              6 POP_TOP
              7 LOAD_CONST               0 (None)
             10 RETURN_VALUE

所以基本上,当你打call_a,它加载无论是存储a在堆栈中,调用它的函数,然后弹出返回值关闭返回前None,这是隐含发生的任何不明确地返回(call_a() is None返回True



 类似资料:
  • 在GCP nodejs云函数中,我使用dotenv在简单模式下重命名运行时触发器调用的函数:我通过dotenv-yaml从env_TEST. yml配置文件中读取部署函数的名称 PROD云函数触发器称为prod_fixNameFunction UAT云函数触发器称为uat_fixNameFunction 称为test_fixNameFunction的TEST云函数触发器 因为我有3.env_PRO

  • 我在nodejs中运行一个python脚本。但是我想保持开放,然后从nodejs调用def函数几次。我怎么能做到这一点。 ################################################################################### 测验派克

  • 本文向大家介绍Python如何获取函数名称?,包括了Python如何获取函数名称?的使用技巧和注意事项,需要的朋友参考一下 在本教程中,我们将学习如何在Python中获取函数名称。获得任何函数的名称都是一件简单的事情。对于Python2和Python3,我们有两种不同的方式。让我们看看他们两个。 Python2 Python2中的每个函数都有一个名为func_name的属性,该属性为您提供当前函数

  • 我正在尝试使用Spring Cloud的Open Faign调用另一个服务,但我一直得到以下回应: 这是我的密码: 我已将Faign配置为使用OkHttp客户端,但我不确定它是否对错误负责:

  • 本文向大家介绍python方法解析顺序?相关面试题,主要包含被问及python方法解析顺序?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: Python的方法解析顺序优先级从高到低为:实例本身类继承类(继承关系越近,越先定义,优先级越高)

  • 本文向大家介绍Python lambda和Python def区别分析,包括了Python lambda和Python def区别分析的使用技巧和注意事项,需要的朋友参考一下 Python支持一种有趣的语法,它允许你快速定义单行的最小函数。这些叫做lambda的函数,是从Lisp借用来的,可以用在任何需要函数的地方。 lambda的语法时常会使人感到困惑,lambda是什么,为什么要使用lambd