57 votes

Méthode d'extension pour les types intégrés python

est-il possible d'ajouter une méthode d'extension aux types intégrés de python ? Je sais que je peux ajouter une méthode d'extension au type défini en ajoutant simplement une nouvelle méthode par . comme suit:

 class myClass:
    pass

myClass.myExtensionMethod = lambda self,x:x * 2
z = myClass()
print z.myExtensionMethod(10)

Mais est-il possible d'ajouter une méthode d'extension aux types intégrés de python tels que list, dict, ...

 list.myExtension = lambda self,x:x * 2
list.myExtension(10)

81voto

toothygoose Points 536

Cela peut être fait en pur Python avec ce module incroyablement intelligent :

https://pypi.python.org/pypi/forbiddenfruit

Par exemple:

 import functools
import ctypes
import __builtin__
import operator

class PyObject(ctypes.Structure):
    pass

Py_ssize_t = hasattr(ctypes.pythonapi, 'Py_InitModule4_64') and ctypes.c_int64 or ctypes.c_int

PyObject._fields_ = [
    ('ob_refcnt', Py_ssize_t),
    ('ob_type', ctypes.POINTER(PyObject)),
]

class SlotsPointer(PyObject):
    _fields_ = [('dict', ctypes.POINTER(PyObject))]

def proxy_builtin(klass):
    name = klass.__name__
    slots = getattr(klass, '__dict__', name)

    pointer = SlotsPointer.from_address(id(slots))
    namespace = {}

    ctypes.pythonapi.PyDict_SetItem(
        ctypes.py_object(namespace),
        ctypes.py_object(name),
        pointer.dict,
    )

    return namespace[name]

def die(message, cls=Exception):
    """
        Raise an exception, allows you to use logical shortcut operators to test for object existence succinctly.

        User.by_name('username') or die('Failed to find user')
    """
    raise cls(message)

def unguido(self, key):
    """
        Attempt to find methods which should really exist on the object instance.
    """
    return functools.partial((getattr(__builtin__, key, None) if hasattr(__builtin__, key) else getattr(operator, key, None)) or die(key, KeyError), self)

class mapper(object):
    def __init__(self, iterator, key):
        self.iterator = iterator
        self.key = key
        self.fn = lambda o: getattr(o, key)

    def __getattribute__(self, key):
        if key in ('iterator', 'fn', 'key'): return object.__getattribute__(self, key)
        return mapper(self, key)

    def __call__(self, *args, **kwargs):
        self.fn = lambda o: (getattr(o, self.key, None) or unguido(o, self.key))(*args, **kwargs)
        return self

    def __iter__(self):
        for value in self.iterator:
            yield self.fn(value)

class foreach(object):
    """
        Creates an output iterator which will apply any functions called on it to every element
        in the input iterator. A kind of chainable version of filter().

        E.g:

        foreach([1, 2, 3]).__add__(2).__str__().replace('3', 'a').upper()

        is equivalent to:

        (str(o + 2).replace('3', 'a').upper() for o in iterator)

        Obviously this is not 'Pythonic'.
    """
    def __init__(self, iterator):
        self.iterator = iterator

    def __getattribute__(self, key):
        if key in ('iterator',): return object.__getattribute__(self, key)
        return mapper(self.iterator, key)

    def __iter__(self):
        for value in self.iterator:
            yield value

proxy_builtin(list)['foreach'] = property(foreach)

import string

print string.join([1, 2, 3].foreach.add(2).str().add(' cookies').upper(), ', ')

>>> 3 COOKIES, 4 COOKIES, 5 COOKIES

Là, ça fait du bien, non ?

20voto

Non. Les types définis en C ne peuvent pas être patchés par singe.

12voto

ironchefpython Points 2209

Non, tu dois sous-classer !

 >>> import string
>>> class MyString(str):
...     def disemvowel(self):
...         return MyString(string.translate(self, None, "aeiou"))
... 
>>> s = MyString("this is only a test")
>>> s.disemvowel()
'ths s nly  tst'

Ou plus spécifique à votre exemple

 >>> class MyList(list):
...     pass
... 
>>> MyList.myExtension = lambda self,x:x * 2
>>> l = MyList()
>>> l.myExtension(10)
20

3voto

fletom Points 899

Non, car je suis presque sûr que tous les types intégrés sont écrits en C optimisé et ne peuvent donc pas être modifiés avec Python. Quand je l'essaye, j'obtiens juste :

 TypeError: can't set attributes of built-in/extension type 'list'

2voto

whrrgarbl Points 1105

Le mieux que vous puissiez faire semble être de dériver une classe du type intégré. Par exemple:

 class mylist(list):
    def myfunc(self, x):
        self.append(x)

test = mylist([1,2,3,4])
test.myfunc(99)

(Vous pouvez même le nommer "liste" afin d'obtenir le même constructeur, si vous le souhaitez.) Cependant, vous ne pouvez pas modifier directement un type intégré comme l'exemple de votre question.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X