`staticmethod` and `abc.abstractmethod`: Will it blend?
Asked Answered
J

5

190

In my Python app I want to make a method that is both a staticmethod and an abc.abstractmethod. How do I do this?

I tried applying both decorators, but it doesn't work. If I do this:

import abc

class C(object):
    __metaclass__ = abc.ABCMeta

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

I get an exception*, and if I do this:

class C(object):
    __metaclass__ = abc.ABCMeta

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

The abstract method is not enforced.

How can I make an abstract static method?

*The exception:

File "c:\Python26\Lib\abc.py", line 29, in abstractmethod
 funcobj.__isabstractmethod__ = True
AttributeError: 'staticmethod' object has no attribute '__isabstractmethod__'
Josselyn answered 17/12, 2010 at 20:11 Comment(0)
U
387

Starting with Python 3.3, it is possible to combine @staticmethod and @abstractmethod, so none of the other suggestions are necessary anymore:

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

Further @abstractstatic is deprecated since version 3.3.

Unschooled answered 23/7, 2015 at 14:36 Comment(3)
Note that you have to put @staticmethod as first, or you'll get AttributeError: attribute '__isabstractmethod__' of 'staticmethod' objects is not writableIntimist
Same for @propertyEhman
Deprecated in 3.3, but still there in 3.10 :-)Glimpse
V
41
class abstractstatic(staticmethod):
    __slots__ = ()
    def __init__(self, function):
        super(abstractstatic, self).__init__(function)
        function.__isabstractmethod__ = True
    __isabstractmethod__ = True

class A(object):
    __metaclass__ = abc.ABCMeta
    @abstractstatic
    def test():
        print 5
Vanhoose answered 17/12, 2010 at 20:26 Comment(7)
Well played, sir or madam :-) You left out import abc at the top; as well as a subclass that shows instantiation of A. (e.g., class B(A):\n @staticmethod\n def test():\n print 10\n)Rocky
I updated the code to work properly with subclassing. A.test should also have the __isabstractmethod__ attribute.Vanhoose
Should abstractstatic be added upstream to abc.py?Genevieve
abstractstaticmethod Deprecated since version 3.3: It is now possible to use staticmethod with abstractmethod(), making this decorator redundant. linkCoom
@iraklikhitarishvili Still, that doesn't enforce the subclass's method to be static! You have to duplicate the staticmethod decorator... Is there no way around that?Regardant
@DiegoAlonsoCortez Sorry for late answer. I prefer writing @staticmethod to each implementation. but if you want you can create subclass of ABCMeta and override __new__ method to check if the base class's method is static and if is then make child's method static too.example. but i don't like this approach. –Coom
Sorry had to -1 for not adding any explanationKoger
N
16

This will do it:

  >>> 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 <module>
    a = A()
  TypeError: Can't instantiate abstract class A with abstract methods test

You go "Eh? It just renames @abstractmethod", and this is completely correct. Because any subclass of the above will have to include the @staticmethod decorator anyway. You have no need of it here, except as documentation when reading the code. A subclass would have to look like this:

  >>> class B(A):
  ...     @staticmethod
  ...     def themethod():
  ...         print "Do whatevs"

To have a function that would enforce you to make this method a static method you would have to subclass ABCmeta to check for that and enforce it. That's a lot of work for no real return. (If somebody forgets the @staticmethod decorator they will get a clear error anyway, it just won't mention static methods.

So in fact this works just as well:

  >>> import abc
  >>>
  >>> class A(object):
  ...     __metaclass__ = abc.ABCMeta
  ...     @abc.abstractmethod
  ...     def themethod():
  ...         """Subclasses must implement this as a @staticmethod"""
  ...          pass

Update - Another way to explain it:

That a method is static controls how it is called. An abstract method is never called. And abstract static method is therefore a pretty pointless concept, except for documentation purposes.

Newsstand answered 18/12, 2010 at 8:5 Comment(0)
B
6

This is currently not possible in Python 2.X, which will only enforce the method to be abstract or static, but not both.

In Python 3.2+, the new decoratorsabc.abstractclassmethod and abc.abstractstaticmethod were added to combine their enforcement of being abstract and static or abstract and a class method.

See Python Issue 5867

Bryce answered 18/9, 2012 at 17:9 Comment(2)
While new in Python 3.2, abstractclassmethod and abstractstaticmethod were rapidly deprecated in Python 3.3, as well as abstractproperty. docs.python.org/3/library/abc.htmlLorenelorens
FYI: bugs.python.org/issue11610 describes the deprication, and new way to do it...Circassia
E
1

The documentation says below:

When abstractmethod() is applied in combination with other method descriptors, it should be applied as the innermost decorator, ...

So, @abstractmethod must be the innermost decorator as shown below:

from abc import ABC, abstractmethod

class Person(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

Then, you need to override them in the child class as shown below:

class Student(Person):
    
    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

Then, you can instantiate the child class and call them as shown below:

obj = Student("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"))

Output:

Test1
Test2
John 
Tom  
False

You can see my answer which explains about abstract property.

Endstopped answered 22/11, 2022 at 16:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.