在python中返回self

Phi*_*p B 26 python method-chaining

我有一个代表对象的类.我有一堆方法可以修改这个对象状态,没有明显的返回或显然没有任何返回.在C#中,我会将所有这些方法声明为void并且看不到其他选择.但是在Python中,我将要制作所有方法return self,让自己能够编写如下所示的真棒单行:

classname().method1().method2().method3()
Run Code Online (Sandbox Code Playgroud)

这是Pythonic,还是Python中可以接受的?

Que*_*ker 27

以下是来自Guido van Rossum(Python编程语言的作者)的关于此主题的邮件:https://mail.python.org/pipermail/python-dev/2003-October/038855.html

我想再次解释为什么我坚持认为sort()不应该返回'self'.

这来自于一种编码风格(在其他各种语言中很流行,我相信Lisp特别喜欢它),其中对单个对象的一系列副作用可以链接如下:

x.compress().斩(Y)的.sort(z)的

这将是相同的

x.compress()x.chop(y)x.sort(z)

我发现链接形式对可读性构成威胁; 它要求读者必须非常熟悉每种方法.第二种形式清楚地表明这些调用中的每一个都作用于同一个对象,因此即使您不熟悉该类及其方法,您也可以理解第二个和第三个调用应用于x(并且所有的电话都是为了他们的副作用而做的,而不是其他的.

我想为返回新值的操作保留链接,比如字符串处理操作:

y = x.rstrip("\n").split(":").lower()

有一些标准库模块鼓励链接副作用调用(pstat浮现在脑海中).不应该有任何新的; 当它很弱时,pstat穿过我的过滤器.

  • @TonySuffolk66:但是Django查询方法每次都会生成一个*new对象*,就像`str`方法一样.在ORM上下文中支持流畅的API非常有意义(SQLAlchemy是另一个例子,在我的回答中使用). (4认同)

Mar*_*ers 14

对于通过方法构建状态的API,这是一个很好的主意.SQLAlchemy使用它可以产生很好的效果,例如:

>>> from sqlalchemy.orm import aliased
>>> adalias1 = aliased(Address)
>>> adalias2 = aliased(Address)
>>> for username, email1, email2 in \
...     session.query(User.name, adalias1.email_address, adalias2.email_address).\
...     join(adalias1, User.addresses).\
...     join(adalias2, User.addresses).\
...     filter(adalias1.email_address=='jack@google.com').\
...     filter(adalias2.email_address=='j25@yahoo.com'):
...     print(username, email1, email2)
Run Code Online (Sandbox Code Playgroud)

请注意,self在许多情况下它不会返回; 它将返回当前对象的克隆,并更改某个方面.这样,您就可以基于共享库创建不同的链; base = instance.method1().method2(),然后foo = base.method3()bar = base.method4().

在上面的示例中,或者调用Query返回的对象不是同一个实例,而是应用了过滤器或连接的新实例.Query.join()Query.filter()

它使用Generative基类来构建; 所以return self使用的模式不是:

def method(self):
    clone = self._generate()
    clone.foo = 'bar'
    return clone
Run Code Online (Sandbox Code Playgroud)

哪个SQLAlchemy使用装饰器进一步简化:

def _generative(func):
    @wraps(func)
    def decorator(self, *args, **kw):
        new_self = self._generate()
        func(new_self, *args, **kw)
        return new_self
    return decorator

class FooBar(GenerativeBase):
    @_generative
    def method(self):
        self.foo = 'bar'
Run Code Online (Sandbox Code Playgroud)

所有 - @_generative解决方法所要做的就是对副本进行更改,装饰器负责生成副本,将方法绑定到副本而不是原始副本,并将其返回给调用者.


Ban*_*ski 12

这是一个(愚蠢的)示例,演示了一个好的技术

class A:
    def __init__(self, x):
        self.x = x
    def add(self, y):
        self.x += y
        return self
    def multiply(self, y)
        self.x *= y
        return self
    def get(self):
        return self.x
a = A(0)
print a.add(5).mulitply(2).get()
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您可以创建一个对象,其中执行操作的顺序严格按函数调用的顺序确定,这可能使代码更具可读性(但也更长)

  • @Floam答案清楚地表明他正在给出一个_When_的例子,它可以是一个很好的技巧. (5认同)
  • @Liongold,Banach在发表评论后更改了问题的答案...... (2认同)