1

Say I have a json file look like:

{
    "foo": ["hi", "there"],
    "bar": ["nothing"]
}

I'd like to create an abstract base class (ABC), where the name of abstract methods are the keys of the json above, i.e.:

from abc import ABCMeta, abstractmethod

class MyABC(metaclass=ABCMeta):

    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

The problem is the json file actually has lots of keys. I wonder if there's any way like:

import json

with open("the_json.json") as f:
    the_json = json.load(f)

class MyABC(metaclass=ABCMeta):

    # for k in the_json.keys():
    #     create abstract method k

Thanks for the suggestions from the comments, but somehow it doesn't work as expected. Here is what I tried:

class MyABC(metaclass=ABCMeta):
    pass

def f(self):
    pass

setattr(MyABC, "foo", abstractmethod(f))
# I also tried
# setattr(MyABC, "foo", abstractmethod(lambda self: ...))

# Try to define another class that inherits MyABC
class MyClass(MyABC):
    pass

c = MyClass()
# Now this should trigger TypeError but it doesn't
# I can even call c.foo() without getting any errors
ytu
  • 1,822
  • 3
  • 19
  • 42
  • @Nair I'd say more a duplicate of https://stackoverflow.com/questions/533382/dynamic-runtime-method-creation-code-generation-in-python – ChatterOne Oct 24 '19 at 09:22
  • 1
    @ChatterOne, I agree that the link and it is closely duplicate then what I mentioned. – S.N Oct 24 '19 at 09:23
  • 1
    The way a decorator works is by calling it on the function. `func = decorator(function)` is the same as `@decorator def func(): pass`. Use `setattr` by calling `abstractmethod(method)`. – Diptangsu Goswami Oct 24 '19 at 09:30
  • Try `...` instead of `pass`. `...` is called `Ellipsis`. – Diptangsu Goswami Oct 24 '19 at 10:11
  • @DiptangsuGoswami Thank you, but that doesn't work quite as expected. Would you please see my edits above and show me what I did wrong? – ytu Oct 24 '19 at 10:26
  • OK I might just find the reason. "Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are not supported." according to [the documentation](https://docs.python.org/3/library/abc.html#abc.abstractmethod). – ytu Oct 24 '19 at 13:42

1 Answers1

0

This may work :

from abc import ABCMeta, abstractmethod

with open("the_json.json") as f:
    the_json = json.load(f)

class MyABC(metaclass=ABCMeta):

    def func(self):
        pass

    for k in the_json:
        locals()[k] = abstractmethod(func)

# Delete attribute "func" is a must
# Otherwise it becomes an additional abstract method in MyABC
delattr(MyABC, "func")
delattr(MyABC, "f")
delattr(MyABC, "k")

class MyClass(MyABC):
    pass

MyClass()
# TypeError: Can't instantiate abstract class MyClass with abstract methods bar, foo

It will correctly throw an error if you try to instantiate MyABC, or a subclass of MyABC that doesn't implement the abstract methods.

ytu
  • 1,822
  • 3
  • 19
  • 42
Ewaren
  • 1,343
  • 1
  • 10
  • 18
  • Thanks. But as my edits above show, if you define a new class like `class MyClass(MyABC): pass`, you can initiate `MyClass()` without triggering any errors. This should not be right, should it? – ytu Oct 24 '19 at 11:00
  • Why should it trigger an error according to you ? Do you actually *want* it to trigger an error or are you just expecting it ? – Ewaren Oct 24 '19 at 11:01
  • As [PEP 3119](https://www.python.org/dev/peps/pep-3119/#the-abc-module-an-abc-support-framework) indicates, there _should_ be `TypeError` because `MyClass` has methods that have not been implemented. "The `abc` module also defines a new decorator, `@abstractmethod`, to be used to declare abstract methods. A class containing at least one method declared with this decorator that hasn't been overridden yet cannot be instantiated." – ytu Oct 24 '19 at 11:15
  • Hey @ytu, I have edited my answer with a new solution that I think works as you intend. If so, could you please mark it as accepted ? – Ewaren Oct 24 '19 at 19:28