首页 > 解决方案 > 如何在方法链接时在 python 类方法中返回 self 和另一个变量?

问题描述

我知道我在这里问的可能不是最好的代码设计,但我问的原因是严格的学术性的。我试图了解如何使这个概念发挥作用。

通常,我将从self类方法返回,以便可以将以下方法链接在一起。我的理解是通过返回自我,我只是返回类的一个实例,以便以下方法起作用。

但在这种情况下,我试图弄清楚如何self从方法中返回两个值和另一个值。这个想法是,如果我不想链接,或者我不调用任何类属性,我想从被调用的方法中检索数据。

考虑这个例子:

class Test(object):
    def __init__(self):
        self.hold = None

    def methoda(self):
        self.hold = 'lol'
        return self, 'lol'

    def newmethod(self):
        self.hold = self.hold * 2
        return self, 2

t = Test()
t.methoda().newmethod()
print(t.hold)

在这种情况下,我会得到一个AttributeError: 'tuple' object has no attribute 'newmethod'预期的结果,因为该methoda方法返回的元组没有任何方法或属性称为newmethod.

我的问题不是关于解包多个返回,而是更多关于当前面的方法返回多个值时如何继续链接方法。我也明白我可以控制方法返回一个参数,但这不是我想要做的。

如前所述,我确实意识到这可能是一个糟糕的问题,如果这个问题没有任何意义,我很乐意删除该帖子。

标签: python

解决方案


按照@JohnColeman 的建议,如果它不是普通的元组属性,您可以返回一个特殊的元组,其属性查找委托给您的对象。这样它就像一个普通的元组,除非你链接方法。

您可以按如下方式实现:

class ChainResult(tuple):
    def __new__(cls, *args):
        return super(ChainResult, cls).__new__(cls, args)
    def __getattribute__(self, name):
        try:
            return getattr(super(), name)
        except AttributeError:
            return getattr(super().__getitem__(0), name)


class Test(object):
    def __init__(self):
        self.hold = None

    def methoda(self):
        self.hold = 'lol'
        return ChainResult(self, 'lol')

    def newmethod(self):
        self.hold = self.hold * 2
        return ChainResult(self, 2)

测试:

>>> t = Test()
>>> t.methoda().newmethod()
>>> print(t.hold)
lollol

返回的结果确实充当元组:

>>> t, res = t.methoda().newmethod()
>>> print(res)
2
>>> print(isinstance(t.methoda().newmethod(), tuple))
True

你可以想象各种语义,例如使用闭包将返回的值转发到链中的下一个方法:

class ChainResult(tuple):
    def __new__(cls, *args):
        return super(ChainResult, cls).__new__(cls, args)
    def __getattribute__(self, name):
        try:
            return getattr(super(), name)
        except AttributeError:
            attr = getattr(super().__getitem__(0), name)
            if callable(attr):
                chain_results = super().__getitem__(slice(1, None))
                return lambda *args, **kw: attr(*(chain_results+args), **kw)
            else:
                return attr

例如,

class Test:
    ...
    def methodb(self, *args):
        print(*args)

会产生

>>> t = Test()
>>> t.methoda().methodb('catz')
lol catz

如果您可以使 ChainResults 不可见,那就太好了。您几乎可以通过使用正常结果初始化元组基类并将对象保存在仅用于链接的单独属性中来做到这一点。然后使用一个类装饰器,用ChainResults(self, self.method(*args, **kw)). 对于返回元组的方法来说它可以正常工作,但单个值返回将像长度为 1 的元组一样,所以你需要类似obj.method()[0]or的东西result, = obj.method()来使用它。我在委托元组进行多次返回或对值本身进行单次返回方面进行了一些尝试;也许它可以工作,但它引入了太多的歧义,我怀疑它是否能很好地工作。


推荐阅读