提问人:Darren J. Fitzpatrick 提问时间:6/16/2010 最后编辑:Karl KnechtelDarren J. Fitzpatrick 更新时间:7/13/2023 访问量:153709
如何从函数中获取结果(输出)?以后如何使用结果?
How do I get a result (output) from a function? How can I use the result later?
问:
假设我有一个这样的函数:
def foo():
x = 'hello world'
如何让函数返回,以便我可以将其用作另一个函数的输入或在程序主体中使用变量?我尝试在另一个函数中使用然后使用变量,但我得到了这种方式。x
return
x
NameError
对于同一类中方法之间通信信息的特定情况,通常最好将信息存储在 self
中。有关详细信息,请参阅在 Python 中的方法之间传递变量?
答:
>>> def foo():
return 'hello world'
>>> x = foo()
>>> x
'hello world'
您可以使用 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
您可以使用语句,然后实现您想要的,而无需返回
函数。例如,您可以执行如下操作:global
def foo():
global x
x = "hello world"
foo()
print x
上面的代码将打印“hello world”。
但请注意,使用“全局”根本不是一个好主意,最好避免使用我的示例中显示的用法。
另请查看有关在 Python 中使用全局语句的相关讨论。
评论
实际上,有两种方式:直接和间接。
直接的方法是从函数中获取一个值,就像你尝试的那样,让调用代码使用该值。这通常是你想要的。从函数中获取信息的自然、简单、直接、显式的方法是向它返回。从广义上讲,函数的目的是计算一个值,并表示“这是我们计算的值;我们到此为止”。return
return
return
直接使用return
这里的主要技巧是返回一个值,而不是一个变量。因此,在调用函数后,不会启用调用代码,也不会修改调用上下文中的任何现有值。(这大概就是你得到 .return
return x
x
x
NameError
我们在函数中使用后: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”实际上是同义词;这对其他一些语言来说并不那么有效。return
return
我们可以在返回线上创建一个元组;或者我们可以使用字典、namedtuple
(Python 2.6+)、types.simpleNamespace
(Python 3.3+)、dataclass
(Python 3.7+) 或其他类(甚至可能是我们自己编写的类)将名称与返回的值相关联;或者我们可以从列表中的循环中累积值;等等等等。 可能性是无穷无尽的。
另一方面,无论您喜欢与否,该函数都会被调用(除非引发异常)。如果到达末尾,则隐含特殊值。您可能希望也可能不想明确地这样做。return
return
None
间接方法
除了将结果直接反馈给调用者之外,我们还可以通过修改调用者知道的一些现有对象来传达它。有很多方法可以做到这一点,但它们都是同一主题的变体。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.value
Test.__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_word
display
Смотритетакже: 在 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)
我们真的不应该使用它来向调用者发送信息。它有全局变量的所有常见问题,而且更难理解。但是,从函数内部设置函数的属性可能很有用,以便函数在调用之间记住某些内容。(这类似于方法通过修改 .) 来记住调用之间的内容。标准库就是这样做的,例如在实现中。self
functools
cache
内置示波器
这是行不通的。内置命名空间不包含任何可变对象,并且无法分配新的内置名称(它们将改为进入全局命名空间)。
一些在 Python 中不起作用的方法
只是在函数结束之前计算一些东西
在其他一些编程语言中,有某种隐藏变量,每次计算时都会自动获取上次计算的结果;如果你到达一个函数的末尾而没有任何东西,它就会被返回。这在 Python 中不起作用。如果到达末尾时没有任何内容,则函数将返回 。return
return
None
分配给函数的名称
在其他一些编程语言中,允许(或期望)将变量分配给与函数同名的变量;在函数结束时,返回该值。这在 Python 中仍然行不通。如果你到达终点时没有收到任何内容,你的函数仍然会返回 。return
None
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
我们看到的工作方法之一是修改传入列表。这是有效的,因为如果列表本身发生了变化,那么它就会改变 - 使用它的名称或代码的哪个部分使用该名称并不重要。但是,向其分配新列表不会导致现有列表发生更改。它只是开始成为不同列表的名称。words
words
有关详细信息,请参阅如何通过引用传递变量?。
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 的一些优化依赖于提前知道函数的局部变量。
评论