从类方法“返回自我”的目的?


46

我在一个开源项目中遇到了这样的事情。修改实例属性的方法返回对该实例的引用。这种构造的目的是什么?

class Foo(object):

  def __init__(self):
    self.myattr = 0

  def bar(self):
    self.myattr += 1
    return self

2
这就是所有jQuery的编写量。几乎每个函数都返回一个jQuery对象
CaffGeek 2010年

11
您为什么不相信这样编写的代码?
sepp2k 2010年

Answers:


65

这是允许链接。

例如:

var Car = function () {
    return {
        gas : 0,
        miles : 0,
        drive : function (d) {
            this.miles += d;
            this.gas -= d;
            return this;
        },
        fill : function (g) {
            this.gas += g;
            return this;
        },
    };
}

现在您可以说:

var c = Car();
c.fill(100).drive(50);
c.miles => 50;
c.gas => 50;

20
作为记录,这种链接通常在具有流畅接口的代码中看到。
Lie Ryan

10

正如@Lie Ryan和@Frank Shearar提到的那样,这被称为“流畅的界面”,但是这种模式已经存在了很长时间了。

该模式的有争议的部分是,在OO中,您具有可变状态,因此void方法具有一种隐含的返回值this-即,具有更新状态的对象就是该返回值的一种。

因此,在具有可变状态的OO语言中,这两个或多或少是等效的:

a.doA()
a.doB()
a.doC()

...相对于

a.doA().doB().doC()

因此,我听说过去的人们会拒绝流畅的界面,因为他们喜欢第一种形式。我听说过的“流畅接口”的另一个名字是“火车残骸”;)

我之所以说“差不多”,是因为流畅的界面会增加皱纹。他们不必“退货”。他们可以“重获新生”。这是在OO中获得不可变对象的一种方式。

所以你可以有一个A类做(伪代码)

function doA():
    return new A(value + 1)

function doB():
    return new A(value * 2)

function doC():
    return new A(sqrt(value))

现在,每个方法都返回一个全新的对象,而初始对象保持不变。那是一种无需更改代码即可进入不可变对象的方法。


好的,但是如果您的方法返回new(...),那将会泄漏内存。
smci

@smci这将在很大程度上取决于语言,实现和用法。当然,如果是C ++,则很可能在各处泄漏内存。但这一切都可以用带有垃圾收集器的语言轻松清理。一些实现(带有或不带有GC)甚至可以检测到何时不使用这些新对象之一,而实际上没有分配任何东西。
8bittree '18

@ 8bittree:我们在谈论Python,这个问题在8年前被标记为Python。Python GC并不是很好,所以最好不要首先泄漏内存。__init__反复调用也会带来开销。
smci

8

大多数语言都知道“返回自我”的习惯用法,如果不在一行中使用它,则忽略它。但是值得注意的是,在Python中,函数None默认返回。

当我在CS学校我的教练做了一个巨大的大约函数,过程,程序和方法之间的差别交易; 机械铅笔在我手中变得很热,从而解决了许多有关抽筋的论文问题。

可以这么说,返回self是设计类方法的确定的OO方法,但是Python允许多个返回值,元组,列表,对象,基元或None。

正如他们所说,链接只是将最后一个操作的答案放到下一个操作中,Python的运行时可以优化这种操作。列表推导是其内置形式。(很强大!)

因此,在Python中,每个方法或函数都返回值并不重要,这就是为什么默认值为None的原因。

有一种流派认为,程序中的每个动作都应将其成功,失败或结果报告给调用上下文或对象,但是在这里没有谈论DOD ADA要求。如果您需要从方法中获取反馈,请继续执行或不执行,但请尝试保持一致。

如果某个方法可能失败,则应返回成功或失败或引发要处理的异常。

一个警告是,如果您使用return self惯用语,Python将允许您将所有方法分配给变量,并且您可能会认为实际上是在获取对象时正在获取数据结果或列表。

当您尝试执行此操作时,类型限制语言会尖叫,大喊和打断,但解释型语言(Python,Lua,Lisp)更具动态性。


4

在Smalltalk中,每个未显式返回内容的方法都具有隐式的“返回自身”。

这是因为(a)使每个方法都返回某些内容会使计算模型更加统一,并且(b)使方法返回self很有用。Josh K举了一个很好的例子。


有趣的是...中的Python每个未显式返回内容的方法都有一个隐式的“返回无”。
工作

2

返回对象的优点(return self

  • 例:

    print(foo.modify1().modify2())
    
    # instaed of
    foo.modify1()
    foo.modify2()
    print(foo)
    
  • 我认为编程社区中更流行的风格。

变异物件的优点(no return self

  • 能够return用于其他目的。
  • Guido van Rossum 赞成这种风格(我不同意他的说法)。
  • 我认为Python社区中更流行的风格。
  • 默认(较少的源代码)。

圭多的争论使我信服。特别是他谈到的“读者必须熟悉每种方法”这一部分,即它迫使您需要先确定这是一个自身链,输出链还是一个组合,然后才能理解结尾处的内容。连锁店
Nath,2013年

@Nat字符串处理操作与其余操作有何根本区别?
xged '16

不同之处在于,字符串大小写每次都会生成一个新的完全不同的对象。即未就地修改项目。很明显,现有对象没有被修改。在隐含返回自我的语言中,相反的说法是正确的,但混合环境似乎有问题
Nath

@Matt我只记得Python字符串是不可变的。这完全回答了我的问题。
xged '16

1
@Matt“很明显,现有对象没有被修改”-从外观上还不清楚。
xged '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.