web-dev-qa-db-ja.com

pythonでselfを返す

オブジェクトを表すクラスがあります。そして、このオブジェクトの状態を、明らかな戻り値なしで、または明らかに戻り値なしで変更する多数のメソッドがあります。 C#では、これらのすべてのメソッドをvoidとして宣言し、代替手段がないことを確認します。しかし、Pythonでは、すべてのメソッドを作成しようとしていますreturn selfこのようなすばらしいワンライナーを書く能力を自分に与えるために:

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

これはPythonicですか、それともPythonで受け入れ可能ですか?

26
Philip B

このトピックに関するGuido van Rossum(Pythonプログラミング言語)の著者)からのメールは次のとおりです。 https://mail.python.org/pipermail/python-dev/ 2003-October/038855.html

Sort()が 'self'を返さないように頑張っている理由をもう一度説明したいと思います。

これは、単一のオブジェクトに対する一連の副作用を次のように連鎖させることができるコーディングスタイル(他のさまざまな言語で人気があり、特にLISPがおもしろいと思います)に由来します。

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

これは同じだろう

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

連鎖は読みやすさに対する脅威だと思います。読者が各方法に精通している必要があります。 2番目の形式は、これらの呼び出しのそれぞれが同じオブジェクトに作用することを明確にしているので、クラスとそのメソッドをよく知らなくても、2番目と3番目の呼び出しがxに適用されることを理解できます(そしてすべての呼び出しは副作用のために行われ、他の呼び出しではありません。

文字列処理操作など、新しい値を返す操作のチェーンを予約します。

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

副作用呼び出しの連鎖を促進する標準ライブラリモジュールがいくつかあります(pstatが思い浮かびます)。新しいものはありません。フィルターが弱いときにpstatがフィルターをすり抜けました。

26
Querenker

メソッドを通じて状態を構築している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=='[email protected]').\
...     filter(adalias2.email_address=='[email protected]'):
...     print(username, email1, email2)
_

多くの場合、selfを返さないことに注意してください。特定のアスペクトが変更された現在のオブジェクトのcloneを返します。このようにして、共有ベースに基づいてdivergentチェーンを作成できます。 base = instance.method1().method2()、次にfoo = base.method3()およびbar = base.method4()

上記の例では、Query.join()またはQuery.filter()呼び出しによって返されるQueryオブジェクトは同じインスタンスではなく、フィルターまたは結合が適用された新しいインスタンスです。

Generative基本クラス を使用して構築します。したがって、_return self_ではなく、使用されるパターンは次のとおりです。

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

デコレータ を使用して、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メソッドがしなければならないことは、コピーに変更を加えることだけです。デコレータはコピーの生成を処理し、メソッドを元ではなくコピーにバインドし、呼び出し元に返します。

14
Martijn Pieters

これは、良いテクニックであるシナリオを示す(愚かな)例です。

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

この場合、操作を実行する順序が、コードを読みやすくする(ただし長くする)関数呼び出しの順序によって厳密に決定されるオブジェクトを作成できます。

12
Banach Tarski

必要に応じて、ここでデコレータを使用できます。インターフェースを見るためにコードを調べている人には際立っており、明示的に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
4
Waleed Khan