Return 自己在 python

Return self in python

我有一个 class 表示对象。我有一堆方法可以修改这个对象状态,没有明显的 return 或明显没有任何 return。在 C# 中,我会将所有这些方法声明为 void 并且看不到其他方法。但是在 Python 中,我将使用所有方法 return self 来让自己能够像这样写出很棒的单行代码:

classname().method1().method2().method3()

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

这是一个示例,它演示了一个可能是好的技术的场景

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()

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

对于通过方法构建状态的 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)

请注意,在许多情况下它不会 return self;它将return 克隆 当前对象的某个方面进行了更改。这样你就可以创建基于共享基础的 divergent 链; base = instance.method1().method2(),然后 foo = base.method3()bar = base.method4()

在上面的示例中,Query 对象 return 由 Query.join()Query.filter() 调用编辑的不是同一实例,而是具有过滤器的新实例或加入申请。

它使用 Generative base class 作为基础;所以使用的模式不是 return self

def method(self):
    clone = self._generate()
    clone.foo = 'bar'
    return clone

通过使用 a decorator:

进一步简化了 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'

所有 @_generative-decorated 方法所要做的就是在副本上进行更改,装饰器负责生成副本,将方法绑定到副本而不是原始方法,并且 return代您转给来电者。

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

I'd like to explain once more why I'm so adamant that sort() shouldn't return 'self'.

This comes from a coding style (popular in various other languages, I believe especially Lisp revels in it) where a series of side effects on a single object can be chained like this:

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

which would be the same as

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

I find the chaining form a threat to readability; it requires that the reader must be intimately familiar with each of the methods. The second form makes it clear that each of these calls acts on the same object, and so even if you don't know the class and its methods very well, you can understand that the second and third call are applied to x (and that all calls are made for their side-effects), and not to something else.

I'd like to reserve chaining for operations that return new values, like string processing operations:

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

There are a few standard library modules that encourage chaining of side-effect calls (pstat comes to mind). There shouldn't be any new ones; pstat slipped through my filter when it was weak.

如果您愿意,可以在此处使用装饰器。对于查看您的代码以查看界面的人来说,它会脱颖而出,并且您不必从每个函数中显式 return self(如果您有多个退出点,这可能会很烦人)。

import functools


def fluent(func):
    @functools.wraps(func)
    def wrapped(*args, **kwargs):
        # Assume it's a method.
        self = args[0]
        func(*args, **kwargs)
        return self
    return wrapped


class Foo(object):
    @fluent
    def bar(self):
        print("bar")

    @fluent
    def baz(self, value):
        print("baz: {}".format(value))

foo = Foo()
foo.bar().baz(10)

打印:

bar
baz: 10