132 votes

`staticmethod` et `abc.abstractmethod` : Est-ce que cela va se mélanger ?

Dans mon application Python, je veux créer une méthode qui est à la fois un staticmethod et une abc.abstractmethod. Comment puis-je faire cela?

J'ai essayé d'appliquer les deux décorateurs, mais cela ne fonctionne pas. Si je fais ceci :

import abc

class C(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    @staticmethod    
    def my_function(): pass

Je reçois une exception*, et si je fais cela :

class C(object):
    __metaclass__ = abc.ABCMeta

    @staticmethod    
    @abc.abstractmethod
    def my_function(): pass

La méthode abstraite n'est pas appliquée.

Comment puis-je créer une méthode statique abstraite?

*L'exception :

Fichier "c:\Python26\Lib\abc.py", ligne 29, dans abstractmethod
 funcobj.__isabstractmethod__ = True
AttributeError: l'objet 'staticmethod' n'a pas d'attribut '__isabstractmethod__'

287voto

gerrit Points 1588

À partir de Python 3.3, il est possible de combiner @staticmethod et @abstractmethod, donc aucune des autres suggestions ne sont plus nécessaires :

@staticmethod
@abstractmethod
def my_abstract_staticmethod(...):

De plus, @abstractstatic est déprécié depuis la version 3.3.

45 votes

Notez que vous devez mettre @staticmethod en premier, sinon vous obtiendrez une AttributeError: l'attribut '__isabstractmethod__' des objets 'staticmethod' n'est pas accessible en écriture

4 votes

De même pour @property

14 votes

Ce serait vraiment utile si c'était la réponse acceptée!

39voto

Rosh Oxymoron Points 6965
classe abstractstatic(staticmethod):
    __slots__ = ()
    def __init__(self, fonction):
        super(abstractstatic, self).__init__(fonction)
        fonction.__isabstractmethod__ = True
    __isabstractmethod__ = True

classe A(objet):
    __metaclass__ = abc.ABCMeta
    @abstractstatic
    def test():
        print 5

9 votes

Bien joué, monsieur ou madame :-) Vous avez omis import abc en haut; ainsi qu'une sous-classe qui montre l'instanciation de A. (par exemple, class B(A):\n @staticmethod\n def test():\n print 10\n)

1 votes

J'ai mis à jour le code pour fonctionner correctement avec la sous-classe. A.test devrait également avoir l'attribut __isabstractmethod__.

3 votes

Faut-il ajouter abstractstatic en amont de abc.py ?

14voto

Lennart Regebro Points 52510

Cela fera l'affaire :

  >>> import abc
  >>> abstractstaticmethod = abc.abstractmethod
  >>>
  >>> class A(object):
  ...     __metaclass__ = abc.ABCMeta
  ...     @abstractstaticmethod
  ...     def themethod():
  ...          pass
  ... 
  >>> a = A()
  >>> Traceback (most recent call last):
  File "asm.py", line 16, in 
    a = A()
  TypeError: Impossible d'instancier la classe abstraite A avec des méthodes abstraites test

Vous vous dites "Hein? Cela renomme simplement @abstractmethod", et c'est tout à fait correct. Parce que toute sous-classe de ce qui précède devra inclure de toute façon le décorateur @staticmethod. Vous n'en avez pas besoin ici, sauf comme documentation lors de la lecture du code. Une sous-classe devrait ressembler à ceci :

  >>> class B(A):
  ...     @staticmethod
  ...     def themethod():
  ...         print "Faites ce que vous voulez"

Pour avoir une fonction qui vous obligerait à faire de cette méthode une méthode statique, vous devriez sous-classer ABCmeta pour vérifier cela et l'imposer. Cela représente beaucoup de travail pour peu de retour. (Si quelqu'un oublie le décorateur @staticmethod, il obtiendra de toute façon une erreur claire, cela n'évoquera simplement pas les méthodes statiques.

En fait, cela fonctionne aussi bien :

  >>> import abc
  >>>
  >>> class A(object):
  ...     __metaclass__ = abc.ABCMeta
  ...     @abc.abstractmethod
  ...     def themethod():
  ...         """Les sous-classes doivent implémenter ceci en tant que @staticmethod"""
  ...          pass

Mise à jour - Une autre façon de l'expliquer :

Le fait qu'une méthode soit statique contrôle la façon dont elle est appelée. Une méthode abstraite n'est jamais appelée. Et une méthode abstraite statique est donc un concept assez inutile, sauf à des fins de documentation.

4voto

Nick Points 148

Cela n'est actuellement pas possible en Python 2.X, qui se contentera d'imposer la méthode comme étant abstraite ou statique, mais pas les deux.

En Python 3.2+, les nouveaux décorateurs abc.abstractclassmethod et abc.abstractstaticmethod ont été ajoutés pour combiner leur imposition d'être abstraite et statique ou abstraite et une méthode de classe.

Voir Problème Python 5867

13 votes

Bien que nouveaux en Python 3.2, abstractclassmethod et abstractstaticmethod ont rapidement été obsolètes en Python 3.3, ainsi que abstractproperty. docs.python.org/3/library/abc.html

4 votes

FYI: bugs.python.org/issue11610 décrit l'obsolescence et la nouvelle façon de le faire...

1voto

La documentation dit ci-dessous :

Quand abstractmethod() est appliqué en combinaison avec d'autres descripteurs de méthode, il doit être appliqué comme le décorateur le plus interne, ...

Ainsi, @abstractmethod doit être le décorateur le plus interne comme indiqué ci-dessous :

from abc import ABC, abstractmethod

class Personne(ABC):

    @classmethod
    @abstractmethod # The innermost decorator
    def test1(cls):
        pass

    @staticmethod
    @abstractmethod # The innermost decorator
    def test2():
        pass

    @property
    @abstractmethod # The innermost decorator
    def name(self):
        pass

    @name.setter
    @abstractmethod # The innermost decorator
    def name(self, name):
        pass

    @name.deleter
    @abstractmethod # The innermost decorator
    def name(self):
        pass

Ensuite, vous devez les remplacer dans la classe enfant comme indiqué ci-dessous :

class Étudiant(Personne):

    def __init__(self, name):
        self._name = name

    @classmethod
    def test1(cls): # Overrides abstract class method
        print("Test1")

    @staticmethod
    def test2(): # Overrides abstract static method
        print("Test2")

    @property
    def name(self): # Overrides abstract getter
        return self._name

    @name.setter
    def name(self, name): # Overrides abstract setter
        self._name = name

    @name.deleter
    def name(self): # Overrides abstract deleter
        del self._name

Ensuite, vous pouvez instancier la classe enfant et les appeler comme indiqué ci-dessous :

obj = Étudiant("John") # Instantiates "Student" class
obj.test1() # Class method
obj.test2() # Static method
print(obj.name) # Getter
obj.name = "Tom" # Setter
print(obj.name) # Getter
del obj.name # Deleter
print(hasattr(obj, "name"))

Resultat :

Test1
Test2
John 
Tom  
False

Vous pouvez voir ma réponse qui explique sur propriété abstraite.

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