简体   繁体   中英

how to enhance the features of builtin functions in python?

I am new to python programming, i like to know how can i enhance the features of builtin functions( Monkeypatch )

for example

i know sum() built in function allowed only on number items

>>> sum([4,5,6,7]) #22

i would like to make sum function to be allow list of items as strings as shown below

for example

>>> sum(['s','t','a','c','k']) # 'stack'

Thanks in advance

You can't really "monkeypatch" a function the way you can a class, object, module, etc.

Those other things all ultimately come down to a collection of attributes, so replacing one attribute with a different one, or adding a new one, is both easy and useful. Functions, on the other hand, are basically atomic things.*

You can, of course, monkeypatch the builtins module by replacing the sum function. But I don't think that's what you were asking. (If you were, see below.)

Anyway, you can't patch sum , but you can write a new function, with the same name if you want, (possibly with a wrapper around the original function—which, you'll notice, is exactly what decorators do).


But there is really no way to use sum(['s','t','a','c','k']) to do what you want, because sum by default starts off with 0 and adds things to it. And you can't add a string to 0.**

Of course you can always pass an explicit start instead of using the default, but you'd have to change your calling code to send the appropriate start . In some cases (eg, where you're sending a literal list display) it's pretty obvious; in other cases (eg, in a generic function) it may not be. That still won't work here, because sum(['s','t','a','c','k'], '') will just raise a TypeError (try it and read the error to see why), but it will work in other cases.

But there is no way to avoid having to know an appropriate starting value with sum , because that's how sum works.

If you think about it, sum is conceptually equivalent to:

def sum(iterable, start=0):
    reduce(operator.add, iterable, start)

The only real problem here is that start , right? reduce allows you to leave off the start value, and it will start with the first value in the iterable:

>>> reduce(operator.add, ['s', 't', 'a', 'c', 'k'])
'stack'

That's something sum can't do. But, if you really want to, you can redefine sum so it can :

>>> def sum(iterable):
...     return reduce(operator.add, iterable)

… or:

>>> sentinel = object()
>>> def sum(iterable, start=sentinel):
...     if start is sentinel:
...         return reduce(operator.add, iterable)
...     else:
...         return reduce(operator.add, iterable, start)

But note that this sum will be much slower on integers than the original one, and it will raise a TypeError instead of returning 0 on an empty sequence, and so on.


If you really do want to monkeypatch the builtins (as opposed to just defining a new function with a new name, or a new function with the same name in your module's globals() that shadows the builtin), here's an example that works for Python 3.1+, as long as your modules are using normal globals dictionaries (which they will be unless you're running in an embedded interpreter or an exec call or similar):

import builtins
builtins.sum = _new_sum

In other words, same as monkeypatching any other module.

In 2.x, the module is called __builtin__ . And the rules for how it's accessed through globals changed somewhere around 2.3 and again in 3.0. See builtins / __builtin__ for details.


* Of course that isn't quite true. A function has a name, a list of closure cells, a doc string, etc. on top of its code object. And even the code object is a sequence of bytecodes, and you can use bytecodehacks or hard-coded hackery on that. Except that sum is actually a builtin-function, not a function, so it doesn't even have code accessible from Python… Anyway, it's close enough for most purposes to say that functions are atomic things.

** Sure, you could convert the string to some subclass that knows how to add itself to integers (by ignoring them), but really, you don't want to.

Not monkey patching exactly, just re-defined sum to make it work for strings as well.

>>> 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'

To do what you want, you should use str.join :

"".join(['s','t','a','c','k'])

Monkey patching is possible, but frowned upon, in Python, especially for trivial things like this. It will make you code harder to read, because standard library functions will do unexpected things.

But, if you really want to, you can just redefine the function. Python won't stop you:

def sum(l):
    return "".join(l)

Python will let you do whatever you want to existing modules:

import sys
sys.stdout = open("somefile", "w")

But again, you shouldn't.

sum already works with anything that defines an __add__ function. The second parameter is the starting point, which defaults to 0, but you can replace it with the "nothing" version of whatever you're summing. For example, adding together a list of lists, starting with an empty list:

sum([[1, 2, 3], [4, 5, 6]], [])

returns:

[1, 2, 3, 4, 5, 6]

So normally, this would actually work:

sum(['s','t','a','c','k'], '')

but this raises an exception which specifically tells you to use join for strings. Probably because it performs better.

Easier to ask forgiveness than permission:

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'

Please forgive me if this looks like I just copied most of someone else's answer. :)

But seriously, you should just use ''.join() instead as @nmclean has explained in an under appreciated answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM