0

I am trying to learn metaclasses in python, from my research i found a example like follow.

i have a Base and Derived classes like follow

class Base():
    def foo(self):
        return self.bar() 

class Derived(Base):
    def foo2(self):
        return "i am foo2"

now, when i want to make sure that whoever extending Base class, must need to implement bar() method, so i created the meta class to hook the constuction of derived class,so now Base class looks like follow with BaseMeta meta class.

class BaseMeta(type):
  def __new__(cls, name, bases, body):
    if not "bar" in body:
      raise TypeError("bar not implemented")
    return super().__new__(cls, name, bases, body)

class Base(metaclass=BaseMeta):
   def foo(self):
        return self.bar() 

The problem is when i get looks into body it returns 2 records for Base and Derived class, like follow.

 {'__module__': '__main__', '__qualname__': 'Base', 'foo': <function 
 Base.foo at 0x7ffbaae436a8>}
 {'__module__': '__main__', '__qualname__': 'Derived', 'bar': <function 
 Derived.bar at 0x7ffbaae437b8>}

my code in __new__ breaks since Base not have bar, but i want to check only in the Derived class so i rewrite my metaclass like follow.

def __new__(cls, name, bases, body):
    if name !="Base" and not "bar" in body:
      raise TypeError("bar not implemented")
    return super().__new__(cls, name, bases, body)

I am checking name != Base in my __new__ method.

Is that the right way to do it or we can use some other best way?

Hari
  • 1,545
  • 1
  • 22
  • 45
  • fyi, what you are talking about is not a metaclass but a baseclass. [Metaclass](https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python) has a very specific meaning and refers to something completely different. – Arne Jul 18 '18 at 07:34
  • We can also use `metaclass` to achieve this right? – Hari Jul 18 '18 at 07:40
  • Theoretically, I am sure. But metaclasses are intended to achieve something different called *class alteration*. Sorry that I am so vague, but I don't understand their use very well myself and just trust the top-voted answer from the question I linked which says that [99% of the time you need class alteration, you are better off using \[monkey patching or decorators\]. But 98% of the time, you don't need class alteration at all](https://stackoverflow.com/a/6581949/962190) and go about my day ignoring that metaclasses exist. – Arne Jul 18 '18 at 07:58
  • got it, Actually i followed [this](https://www.youtube.com/watch?v=cKPlPJyQrt4), video link but thanks for extra notes – Hari Jul 18 '18 at 08:02
  • 1
    That looks interesting, I'll try to have a look and get back if it turns out that I misunderstood your question. – Arne Jul 18 '18 at 08:28
  • 1
    Note that you could implement [`__init_subclass__`](https://docs.python.org/3/reference/datamodel.html#customizing-class-creation) to verify assertions about a child without defining a metaclass. – chepner Jul 20 '18 at 23:18
  • @chepner exactly `__init_subclass__` is an alternative to metaclass in python. `__builtclass__` also one more option. – Hari Jul 21 '18 at 04:41

1 Answers1

2

You can use the abc module in the stdlib, which has tools for doing exactly this. https://docs.python.org/3/library/abc.html

import abc

class Base(abc.ABC):

    @abc.abstractmethod
    def bar(self):
         pass

class Derived(Base):
    pass

# This will raise an error because foo is not implemented
# >>> Derived() 
# TypeError: Can't instantiate abstract class Derived with abstract methods bar

Another strategy would be to have a bar method on your Base class that raises a NotImplementedError. The main difference is that no error is raised until you actually call something that requires bar. e.g.

class Base():
    def foo(self):
        return self.bar() 

    def bar(self):
        raise NotImplementedError
Thtu
  • 1,992
  • 15
  • 21
  • I want to enforce constraint for `derived` class without implementing `bar` method in my `Base` class, and i am confused about example 1, i think it enforce `foo` right? but i need to enforce `bar()` – Hari Jul 18 '18 at 09:43
  • 1
    To enforce `bar` for a derived class, declare an abstract method on your base class for that method. Edited response to reflect this. – Thtu Jul 20 '18 at 22:55