web-dev-qa-db-fra.com

functools.partial sur la méthode de classe

J'essaie de définir certaines méthodes de classe en utilisant une autre méthode de classe plus générique comme suit:

class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    red = functools.partial(_color, type='_red')
    blue = functools.partial(_color, type='_blue')
    green = functools.partial(_color, type='_green')

Mais lorsque j'essaie d'invoquer l'une de ces méthodes, j'obtiens:

rgb = RGB(100, 192, 240)
print rgb.red()
TypeError: _color() takes exactly 2 arguments (1 given)

Je suppose que self n'est pas passé à _color Puisque rgb.red(rgb) fonctionne.

40
Arjor

Vous créez des partiels sur la fonction , pas sur la méthode. Les objets functools.partial() ne sont pas des descripteurs, ils n'ajouteront pas eux-mêmes l'argument self et ne pourront pas faire office de méthodes eux-mêmes. Vous pouvez uniquement encapsuler les méthodes ou fonctions liées, elles ne fonctionnent pas du tout avec les méthodes non liées. C'est documenté :

Les objets partial sont similaires aux objets function dans la mesure où ils peuvent être appelés, référencés faiblement et peuvent avoir des attributs. Il existe des différences importantes. Par exemple, les attributs __name__ Et __doc__ Ne sont pas créés automatiquement. De plus, les objets partial définis dans les classes se comportent comme des méthodes statiques et ne se transforment pas en méthodes liées lors de la recherche d'attributs d'instance.

Utilisez plutôt propertys; ces sont des descripteurs :

class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    @property
    def red(self): return self._color('_red')
    @property
    def blue(self): return self._color('_blue')
    @property
    def green(self): return self._color('_green')

À partir de Python 3.4, vous pouvez utiliser le nouvel objet functools.partialmethod() ici; il fera la bonne chose lorsqu'il sera lié à une instance:

class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    red = functools.partialmethod(_color, type='_red')
    blue = functools.partialmethod(_color, type='_blue')
    green = functools.partialmethod(_color, type='_green')

mais il faudrait les appeler, tandis que les objets property peuvent être utilisés comme de simples attributs.

47
Martijn Pieters

Le problème avec partialmethod est qu'il n'est pas compatible avec inspect.signature, functools.wraps, ...

Curieusement, si vous réimplémentez functools.partial Vous-même en utilisant exemple d'implémentation de documentation partielle , cela fonctionnera:

# Implementation from:
# https://docs.python.org/3/library/functools.html#functools.partial
def partial(func, /, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = {**keywords, **fkeywords}
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc
class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    red = partial(_color, type='_red')
    blue = partial(_color, type='_blue')
    green = partial(_color, type='_green')

rgb = RGB(100, 192, 240)
print(rgb.red())  # Print red

La raison en est que newfunc est une vraie fonction qui implémente le protocole descripteur avec newfunc.__get__. Alors que type(functools.partial) est une classe personnalisée avec __call__ Écrasé. La classe n'ajoutera pas automatiquement le paramètre self.

0
Conchylicultor