如何在python中链接多个参数? add(1)(2)(3)= 6

时间:2016-08-13 17:31:22

标签: python

我试图创建一个链接多个参数的函数。

def hi(string):
   print(string)<p>
   return hi

调用hi("Hello")("World")并按预期成为Hello \ n World。

问题是当我想将结果附加为单个字符串时,但是 return string + hi产生错误,因为hi是一个函数。

我尝试使用__str__ and __repr__来更改hi输入时的行为方式。但这只会在其他地方造成不同的问题。

hi("Hello")("World") = "Hello"("World") - &gt;自然会产生错误。

我理解为什么程序无法解决它,但我无法找到解决方案。 :/

2 个答案:

答案 0 :(得分:7)

你在这里遇到了困难,因为每次调用函数的结果本身都必须是可调用的(所以你可以链接另一个函数调用),同时是一个合法的字符串(如果你链接另一个函数调用,只是按原样使用返回值)。

幸运的是,Python已经涵盖了:通过在其上定义__call__方法,可以使任何类型像函数一样可调用。像str这样的内置类型没有这样的方法,但您可以定义str的子类。

class hi(str):
    def __call__(self, string):
        return hi(self + '\n' + string)

这不是很漂亮而且非常脆弱(即当您使用特殊字符串执行几乎任何操作时,您将最终得到常规str对象,除非您覆盖str的所有方法返回hi实例,因此不被认为是非常Pythonic。

在这种特殊情况下,如果你在开始使用结果时最终得到常规的str实例,那就不重要了,因为那时你已经完成了链接函数调用,或者应该处于任何理智状态世界。但是,这通常是一个问题,在这种情况下,您通过子类化向内置类型添加功能。

首先,您的标题中的问题可以类似地回答:

class add(int):    # could also subclass float
    def __call__(self, value):
        return add(self + value)

要真正做到add(),你希望能够返回结果类型的可调用子类,无论它是什么类型;它可能是除intfloat之外的其他内容。我们可以根据结果类型动态创建它们,而不是尝试编目这些类型并手动编写必要的子类。这是一个快速而肮脏的版本:

class AddMixIn(object):
     def __call__(self, value):
         return add(self + value)

def add(value, _classes={}):
    t = type(value)
    if t not in _classes:
        _classes[t] = type("add_" + t.__name__, (t, AddMixIn), {})
    return _classes[t](value)

令人高兴的是,这个实现适用于字符串,因为它们可以使用+连接。

一旦你开始沿着这条路走下去,你可能也希望为其他操作做这件事。这是一个拖拽复制和粘贴每个操作基本相同的代码,所以让我们编写一个为您编写函数的函数!只需指定一个实际完成工作的函数,即获取两个值并对它们执行某些操作,它会为您提供一个函数,可以为您完成所有类的操作。您可以使用lambda(匿名函数)或预定义函数(例如operator模块中的函数)指定操作。因为它是一个函数,它接受一个函数并返回一个函数(好吧,一个可调用的对象),它也可以用作装饰器!

 def chainable(operation):

     class CallMixIn(object):
         def __call__(self, value):
             return do(operation(self, value))

     def do(value, _classes={}):
         t = type(value)
         if t not in _classes:
             _classes[t] = type(t.__name__, (t, CallMixIn), {})
         return _classes[t](value)

     return do

 add = chainable(lambda a, b: a + b)

 # or...
 import operator
 add = chainable(operator.add)

 # or as a decorator...
 @chainable
 def add(a, b): return a + b

最后它的仍然不是很漂亮,仍然 sorta脆弱,仍然不会被认为是非常Pythonic。

如果你愿意使用额外的(空)调用来表示链的末尾,那么事情变得更加简单,因为你需要返回函数,直到你被调用而没有参数:

def add(x):
    return lambda y=None: x if y is None else add(x+y)

你这样称呼它:

add(3)(4)(5)()    # 12

答案 1 :(得分:2)

通过让hi返回对自身的引用,您正在深入研究Haskell风格的类型理论问题。相反,只需接受多个参数并在函数中连接它们。

def hi(*args):
    return "\n".join(args)

一些示例用法:

print(hi("Hello", "World"))
print("Hello\n" + hi("World"))