我试图创建一个链接多个参数的函数。
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;自然会产生错误。
我理解为什么程序无法解决它,但我无法找到解决方案。 :/
答案 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()
,你希望能够返回结果类型的可调用子类,无论它是什么类型;它可能是除int
或float
之外的其他内容。我们可以根据结果类型动态创建它们,而不是尝试编目这些类型并手动编写必要的子类。这是一个快速而肮脏的版本:
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"))