我是python编程的新手, 我想知道如何增强内置函数的功能(Monkeypatch)
例如
我知道sum()内置函数只允许使用数字项
>>> sum([4,5,6,7]) #22
我想将sum函数设为允许项目列表为字符串,如下所示
例如
>>> sum(['s','t','a','c','k']) # 'stack'
提前致谢
答案 0 :(得分:12)
你不能像对象,对象,模块等那样“monkeypatch”一个函数。
其他所有东西最终都归结为属性集合,因此用不同的属性替换一个属性或添加新属性既简单又有用。另一方面,函数基本上是原子的东西。*
当然,您可以通过替换sum
函数来monkeypatch builtins模块。但我不认为这就是你所要求的。 (如果你是,见下文。)
无论如何,你不能修补sum
,但你可以编写一个新的函数,如果你想要的话,可以使用相同的名称,(可能包含原始函数的包装器 - 你会注意到,究竟装饰者做了什么。)
但实际上没有办法使用sum(['s','t','a','c','k'])
来做你想做的事情,因为默认情况下sum
从0开始并添加内容。并且您不能将字符串添加到0。**
当然,您始终可以传递明确的start
而不是默认值,但您必须更改调用代码才能发送相应的start
。在某些情况下(例如,您发送文字列表显示的地方),这很明显;在其他情况下(例如,在通用功能中)它可能不是。这仍然不适用于此,因为sum(['s','t','a','c','k'], '')
只会引发TypeError
(尝试并阅读错误以了解原因),但在其他情况下也可以。
但是没有办法避免必须使用sum
知道合适的起始值,因为这就是sum
的工作方式。
如果您考虑一下,sum
在概念上等同于:
def sum(iterable, start=0):
reduce(operator.add, iterable, start)
这里唯一真正的问题是start
,对吧? reduce
允许您保留起始值,它将以iterable中的第一个值开头:
>>> reduce(operator.add, ['s', 't', 'a', 'c', 'k'])
'stack'
这是sum
无法做到的事情。但是,如果您真的想要,可以重新定义sum
,以便可以:
>>> def sum(iterable):
... return reduce(operator.add, iterable)
......或:
>>> sentinel = object()
>>> def sum(iterable, start=sentinel):
... if start is sentinel:
... return reduce(operator.add, iterable)
... else:
... return reduce(operator.add, iterable, start)
但请注意,这个sum
在整数上要比原始值慢得多,它会在空序列上引发TypeError
而不是返回0
,依此类推。
如果你确实想要monkeypatch内置函数(而不是仅仅使用新名称定义一个新函数,或者在模块的globals()
中使用同名的新函数来隐藏内置函数),这里是一个适用于Python 3.1+的示例,只要您的模块使用普通的全局字典(除非您在嵌入式解释器或exec
调用或类似操作中运行,否则它们将一直存在):
import builtins
builtins.sum = _new_sum
换句话说,就像monkeypatching任何其他模块一样。
在2.x中,模块称为__builtin__
。关于它如何通过全局变量访问的规则在2.3左右变化,在3.0中再变化。有关详细信息,请参阅builtins
/ __builtin__
。
*当然不是相当是真的。函数在其代码对象之上具有名称,闭包单元列表,文档字符串等。甚至代码对象也是一系列字节码,您可以使用bytecodehacks
或硬编码的hackery。除了sum
实际上是一个内置函数,而不是一个函数,因此它甚至没有可以从Python访问的代码......无论如何,它足够接近大多数目的来说函数是原子的东西。
**当然,你可以将字符串转换为知道如何将自身添加到整数的子类(通过忽略它们),但实际上,你不想这样做。
答案 1 :(得分:5)
不是完全修补猴子,只是重新定义sum
以使其适用于字符串。
>>> import __builtin__
def sum(seq, start = 0):
if all(isinstance(x,str) for x in seq):
return "".join(seq)
else:
return __builtin__.sum(seq, start)
...
>>> sum([4,5,6,7])
22
>>> sum(['s','t','a','c','k'])
'stack'
答案 2 :(得分:3)
要做你想做的事,你应该使用str.join
:
"".join(['s','t','a','c','k'])
猴子修补是可能的,但在Python中不赞成,特别是对于像这样的琐碎事情。它会让你的代码更难阅读,因为标准的库函数会做出意想不到的事情。
但是,如果你真的想,你可以重新定义这个功能。 Python不会阻止你:
def sum(l):
return "".join(l)
Python将允许您对现有模块执行任何操作:
import sys
sys.stdout = open("somefile", "w")
但是,你不应该。
答案 3 :(得分:3)
sum
已经适用于定义__add__
函数的任何内容。第二个参数是起始点,默认为0,但您可以将其替换为任何正在求和的“无”版本。例如,从空列表开始添加列表列表:
sum([[1, 2, 3], [4, 5, 6]], [])
返回:
[1, 2, 3, 4, 5, 6]
通常,这实际上会起作用:
sum(['s','t','a','c','k'], '')
但是这引发了一个异常,它特别告诉你对字符串使用join
。可能是因为它表现得更好。
答案 4 :(得分:1)
比请求更容易请求宽恕:
import __builtin__
def sum(seq, start = 0):
try:
return "".join(seq)
except TypeError:
return __builtin__.sum(seq, start)
...
>>> sum([4,5,6,7])
22
>>> sum(['s','t','a','c','k'])
'stack'
请原谅我,如果这看起来像我刚刚复制了别人的大部分回答。 :)
但严重的是,你应该使用''.join()
而不是@nmclean在一个不太理解的答案中解释过。