如何从函数中获取结果(输出)?以后如何使用结果?

How do I get a result (output) from a function? How can I use the result later?

提问人:Darren J. Fitzpatrick 提问时间:6/16/2010 最后编辑:Karl KnechtelDarren J. Fitzpatrick 更新时间:7/13/2023 访问量:153709

问:

假设我有一个这样的函数:

def foo():
    x = 'hello world'

如何让函数返回,以便我可以将其用作另一个函数的输入或在程序主体中使用变量?我尝试在另一个函数中使用然后使用变量,但我得到了这种方式。xreturnxNameError


对于同一类中方法之间通信信息的特定情况,通常最好将信息存储在 self 中。有关详细信息,请参阅在 Python 中的方法之间传递变量?

Python 函数 返回

评论

7赞 Björn Pollex 6/16/2010
我真的建议你在这里阅读python教程:docs.python.org/tutorial/index.html

答:

4赞 SilentGhost 6/16/2010 #1
>>> def foo():
    return 'hello world'

>>> x = foo()
>>> x
'hello world'
37赞 Tim Pietzcker 6/16/2010 #2

您可以使用 return 语句从函数返回值,这不会使返回的变量(值)在调用方的作用域中可用。若要在调用方中使用该值,可以直接在语句中使用它,也可以在变量中捕获该值。

下面是一些演示此内容的代码:

def foo():
    x = 'hello world'
    return x  # return 'hello world' would do, too

foo()
print(x)   # NameError - x is not defined outside the function

y = foo()
print(y)   # this works

x = foo()
print(x)   # this also works, and it's a completely different x than that inside
           # foo()

z = bar(x) # of course, now you can use x as you want

z = bar(foo()) # but you don't have to
3赞 sateesh 6/16/2010 #3

您可以使用语句,然后实现您想要的,而无需返回 函数。例如,您可以执行如下操作:global

def foo():
    global x 
    x = "hello world"

foo()
print x

上面的代码将打印“hello world”。

但请注意,使用“全局”根本不是一个好主意,最好避免使用我的示例中显示的用法。

另请查看有关在 Python 中使用全局语句的相关讨论。

评论

2赞 Tim Pietzcker 6/16/2010
从技术上讲,这是 Seafoid 问题的解决方案,但从 python 上讲,这是最坏的情况(正如您已经指出的那样)。
8赞 Karl Knechtel 8/15/2022 #4

实际上,有两种方式:直接和间接。

直接的方法是从函数中获取一个值,就像你尝试的那样,让调用代码使用该值。这通常是你想要的。从函数中获取信息的自然、简单、直接、显式的方法是向它返回。从广义上讲,函数的目的是计算一个值,并表示“这是我们计算的值;我们到此为止”。returnreturnreturn

直接使用return

这里的主要技巧是返回一个值,而不是一个变量。因此,在调用函数后,不会启用调用代码,也不会修改调用上下文中的任何现有值。(这大概就是你得到 .returnreturn xxxNameError

我们在函数中使用后:return

def example():
    x = 'hello world'
    return x

我们需要编写调用代码来使用返回值:

result = example()
print(result)

这里的另一个关键点是,对函数的调用是一个表达式,因此我们可以像使用加法结果一样使用它。正如我们可以说的,我们可以说。之后,是该字符串的本地名称,我们可以用它做任何我们想做的事情。result = 'hello ' + 'world'result = foo()result

如果需要,我们可以使用相同的名称 。或者我们可以使用不同的名称。调用代码不必知道函数的编写方式,也不必知道它对事物使用什么名称。1x

我们可以直接使用该值来调用另一个函数:例如,.2 我们可以直接返回值:简单,无需赋值给 。(再说一遍:我们返回的是一个值,而不是一个变量print(foo())return 'hello world'x

该函数每次调用时只能调用一次。 终止函数 - 同样,我们只是确定了计算结果,因此没有理由进一步计算。因此,如果我们想返回多条信息,我们需要想出一个对象(在 Python 中,“value”和“object”实际上是同义词;这对其他一些语言来说并不那么有效。returnreturn

我们可以在返回线上创建一个元组;或者我们可以使用字典、namedtuple (Python 2.6+)、types.simpleNamespace (Python 3.3+)、dataclass (Python 3.7+) 或其他类(甚至可能是我们自己编写的类)将名称与返回的值相关联;或者我们可以从列表中的循环中累积值;等等等等。 可能性是无穷无尽的。

另一方面,无论您喜欢与否,该函数都会被调用(除非引发异常)。如果到达末尾,则隐含特殊值。您可能希望也可能不想明确地这样做returnreturnNone


间接方法

除了将结果直接反馈给调用者之外,我们还可以通过修改调用者知道的一些现有对象来传达它。有很多方法可以做到这一点,但它们都是同一主题的变体。return

如果您希望代码以这种方式传达信息,请让它返回 - 不要将返回值用于有意义的事情。这就是内置功能的工作原理None

当然,为了修改该对象,被调用的函数也必须知道它。这意味着,具有可在当前作用域查找的对象的名称。因此,让我们按顺序浏览这些内容:

本地范围:修改传入的参数

如果我们的一个参数是可变的,我们可以直接改变它,并依靠调用方来检查更改。这通常不是一个好主意,因为很难对代码进行推理。它看起来像:

def called(mutable):
    mutable.append('world')

def caller():
    my_value = ['hello'] # a list with just that string
    called(my_value)
    # now it contains both strings

如果该值是我们自己类的实例,我们还可以分配给一个属性:

class Test:
    def __init__(self, value):
        self.value = value

def called(mutable):
    mutable.value = 'world'

def caller():
    test = Test('hello')
    called(test)
    # now test.value has changed

分配给属性不适用于内置类型,包括 ;它可能不适用于某些明确阻止您这样做的类。object

局部作用域:在方法中修改self

我们在上面已经有一个这样的例子:在代码中设置。这是修改传入参数的特例;但这是 Python 中类工作方式的一部分,也是我们应该做的事情。通常,当我们这样做时,调用实际上不会检查对 - 它只会在逻辑的下一步中使用修改后的对象。这就是以这种方式编写代码的合适之处:我们仍然提供一个接口,因此调用者不必担心细节。self.valueTest.__init__self

class Example:
    def __init__(self):
        self._words = ['hello']
    def add_word(self):
        self._words.append('world')
    def display(self):
        print(*self.words)

x = Example()
x.add_word()
x.display()

在此示例中,调用将信息反馈给顶级代码 - 但我们不是寻找它,而是继续调用 。3add_worddisplay

Смотритетакже: 在 Python 中的方法之间传递变量?

封闭范围

在使用嵌套函数时,这是一种罕见的特殊情况。这里没什么好说的 - 它的工作方式与全局范围相同,只是使用非本地关键字而不是 .4global

全局作用域:修改全局

一般来说,在设置全局范围后更改任何内容都是一个坏主意。它使代码更难推理,因为任何使用该全局代码的东西(除了负责更改的内容)现在都有一个“隐藏”的输入源。

如果你还想这样做,语法很简单:

words = ['hello']
def add_global_word():
    words.append('world')

add_global_word() # `words` is changed

全局范围:分配给新的或现有的全局

这实际上是修改全局的特例。我并不是说赋值是一种修改(事实并非如此)。我的意思是,当你分配一个全局名称时,Python 会自动更新一个表示全局命名空间的字典。你可以用 来获取该字典,你可以修改该字典,它实际上会影响存在的全局变量。(即,返回的是字典本身,而不是副本。5globals()globals()

但请不要。这比前一个想法更糟糕。如果您确实需要通过分配给全局变量来从函数中获取结果,请使用 global 关键字告诉 Python 应该在全局范围内查找该名称:

words = ['hello']

def replace_global_words():
    global words
    words = ['hello', 'world']

replace_global_words() # `words` is a new list with both words

全局作用域:分配给或修改函数本身的属性

这是一个罕见的特例,但现在你已经看到了其他例子,理论应该很清楚了。在 Python 中,函数是可变的(即您可以设置它们的属性);如果我们在顶层定义一个函数,它就在全局命名空间中。所以这实际上只是修改一个全局:

def set_own_words():
    set_own_words.words = ['hello', 'world']

set_own_words()
print(*set_own_words.words)

我们真的不应该使用它来向调用者发送信息。它有全局变量的所有常见问题,而且更难理解。但是,从函数内部设置函数的属性可能很有用,以便函数在调用之间记住某些内容。(这类似于方法通过修改 .) 来记住调用之间的内容。标准库就是这样做的,例如在实现中。selffunctoolscache

内置示波器

这是行不通的。内置命名空间不包含任何可变对象,并且无法分配新的内置名称(它们将改为进入全局命名空间)。


一些在 Python 中不起作用的方法

只是在函数结束之前计算一些东西

在其他一些编程语言中,有某种隐藏变量,每次计算时都会自动获取上次计算的结果;如果你到达一个函数的末尾而没有任何东西,它就会被返回。这在 Python 中不起作用。如果到达末尾时没有任何内容,则函数将返回 。returnreturnNone

分配给函数的名称

在其他一些编程语言中,允许(或期望)将变量分配给与函数同名的变量;在函数结束时,返回该值。这在 Python 中仍然行不通。如果你到达终点时没有收到任何内容,你的函数仍然会返回 。returnNone

def broken():
    broken = 1

broken()
print(broken + 1) # causes a `TypeError`

如果您使用关键字,您似乎至少可以以这种方式使用该值:global

def subtly_broken():
    global subtly_broken
    subtly_broken = 1

subtly_broken()
print(subtly_broken + 1) # 2

但是,当然,这只是分配给全局的特例。而且它有一个很大的问题——同一个名字不能同时指代两件事。通过这样做,函数替换了自己的名称。所以下次会失败

def subtly_broken():
    global subtly_broken
    subtly_broken = 1

subtly_broken()
subtly_broken() # causes a `TypeError`

分配给参数

有时,人们希望能够赋值给函数的一个参数,并让它影响用于相应参数的变量。但是,这不起作用:

def broken(words):
    words = ['hello', 'world']

data = ['hello']
broken(data) # `data` does not change

就像 Python 返回值而不是变量一样,它也传递值而不是变量。 是本地名称;根据定义,调用代码对该命名空间一无所知。words

我们看到的工作方法之一是修改传入列表。这是有效的,因为如果列表本身发生了变化,那么它就会改变 - 使用它的名称或代码的哪个部分使用该名称并不重要。但是,向其分配新列表不会导致现有列表发生更改。它只是开始成为不同列表的名称。wordswords

有关详细信息,请参阅如何通过引用传递变量?


1 至少,不是为了取回价值。如果要使用关键字参数,则需要知道关键字名称是什么。但一般来说,函数的要点在于它们是一种抽象;你只需要知道他们的界面,你不需要考虑他们在内部做什么。

2 在 2.x 中,print 是一个语句而不是一个函数,因此这不会成为直接调用另一个函数的示例。但是,print foo() 仍然适用于 2.x 的 print 语句,print(foo()) 也是如此(在这种情况下,额外的括号只是普通的分组括号)。除此之外,自 2020 年初以来,2.7(最后一个 2.x 版本)一直不受支持——这比正常时间表延长了近 5 年。但是,这个问题最初是在 2010 年提出的。

3同样:如果方法的目的是更新对象,则不要同时返回值。有些人喜欢返回自我,这样你就可以“链接”方法调用;但在 Python 中,这被认为是糟糕的风格。如果你想要那种“流畅”的接口,那么不要编写更新自身的方法,而是编写创建新的、修改后的类实例的方法。

4 当然,如果我们修改一个值而不是赋值,我们不需要任何一个关键字。

5 还有一个 locals() 可以给你一个局部变量的字典。但是,这不能用于创建新的局部变量 - 行为在 2.x 中是未定义的,而在 3.x 中,字典是动态创建的,分配给它没有效果。Python 的一些优化依赖于提前知道函数的局部变量。