22

以这种方式从函数返回多个值是pythonic吗?

def f():
    f.x = 1
    f.y = 2
    return f

r = f()
print r.x,r.y
1 2
4

8 回答 8

39

您不是“返回链式值”,而是在对其自身设置变量之后创建一个返回自身的函数。

这样做的问题是,如果您重新调用该函数(假设它不仅仅是示例中所示的常量函数),那么该函数的每一次外观(并理解这与您的代码r中的相同f)都会有那些值变化。无论您的程序是否使用多个线程,您都会遇到这个问题。

返回多个值的正常方法是简单地返回一个元组,它可以是解构(序列)赋值的来源。或者,如果你想一起管理一堆变量,你会使用一个对象。这就是他们的目的。

于 2013-07-10T14:12:12.653 回答
33

不,您正在更改一个非线程安全的全局对象。

比较常见的是

return 1, 2

或者,如果你想有名字,

return {'x': 1, 'y': 2}
于 2013-07-10T14:11:26.850 回答
20

它不是 Pythonic,对于任何可能支持这种结构的语言来说甚至都不合理。

您所做的是使用函数的全局状态作为输出值的载体。要从函数返回值,您应该使用返回值,而不是被调用的函数。在您的示例中,您无法确定您的返回值是多少:

>> def f(x):
...   f.x=x
...   return f
...
>>> z=f(1)
>>> z.x
1
>>> f(2)    # <--- alters global state of the function
<function f at 0x10045c5f0>
>>> z.x
2           # <--- errr, five lines ago z.x was 1, no?

您可以使用namedtuple或自定义类型(尽管使用type()可能会被认为太低级):

>>> def dict_as_tuple(**kwargs):
...     return type('CustomType', (object,), kwargs)()
...
>>> z = dict_as_tuple(x=1, y=2)
>>> z.x
1
>>> z.y
2

关于方法链,一种常见的方式是返回self(如果你想改变对象的状态)或相同类型的新对象(对象是不可变的,这很好

>>> class C(object):
...   def __init__(self, x):
...      self.x = x
...   def add(self, y):
...     return C(self.x + y)
...   def mul(self, y):
...     return C(self.x * y)
...
>>> C(0).add(1).mul(10).x
10
于 2013-07-10T14:26:42.427 回答
18

到目前为止给出的所有建议都很好,但没有显示太多代码,这里一一列出。

最常见的答案是“返回一个元组”,看起来像这样

def f():
    return 1, 2

x, y = f()

一个相关的答案是'return a namedtuple':

from collections import namedtuple

Point = namedtuple('Point', 'x y')

def f():
    return Point(1, 2)

r = f()
print r.x, r.y

另一个想法是使用“对象”。

class Foo(object):
    def __init__(self, x, y)
        self.x, self.y = x, y

r = Foo(1, 2)
print r.x, r.y

您在问题中提到了“链接”;好像您希望重复调用f()以给出不同的结果。这对于生成器也是可行的。

def f_gen():
    for x in range(1, 10)
        yield x, x + 1

f = f_gen()
print f()
print f()
于 2013-07-10T14:39:44.730 回答
7

这是错的:

>>> def f(a):
...   f.x = a
...   return f
...
>>> r = f(1)
>>> print r.x
1
>>> p = f(2)
>>> print p.x
2
>>> print r.x
2

没想到r也变了!所以这样做不是很好的做法。

于 2013-07-10T14:39:57.250 回答
4

如果它是pythonic,请查看您的问题的其他评论......
作为一个简单的解决方案,您可以使用一个类来代替......

class cls:
    def __init__(self):
        self.x, self.y = 1, 2
r = cls()
print(r.x, r.y) # 1 2
于 2013-07-10T14:29:30.310 回答
4

您可以返回一个元组:

def f():
    return 1,2

x,y = f()
print x,y
...
(1,2)
于 2013-07-10T14:11:48.700 回答
1

如果需要保留一些状态信息,另一种选择是使用一个类并重载__call __方法

class f:
    def __call__(self):
        self.x = 1
        self.y = 2
        return self.x, self.y

r = f()
print(r())

这将打印:

(1, 2)
于 2013-07-10T14:35:31.780 回答