3

I wrote a class in python which inherits from type . I thought that this was the only requirement for a class so as to be called as a metaclass but had not defined a __new__ method for it. But on instantiating with this new class as the metaclass I got an error stating the below :

TypeError: type.__new__() takes exactly 3 arguments (0 given)

The following is my code :

class a(type) :
    pass 
c= a()

Now when the class statement is being processed , that the __new__ method of type is being called is my assumption. This is because the default metaclass of all classes in python is type .

Now when I am instantiating the class a , which I have assumed to be a metaclass under the assumption that any class inheriting from (type) is a metaclass , isn't it the same as creating a class ? Why should this not result in type.__new__ being called with correct arguments ?

1
  • Maybe there is a x-y problem going on here. Normally the need for metaclasses arise naturally if we have a problem that will actually need then. And in this case, we will have a good idea of what they are - that does not seem to be the case. What it is that you want to achieve with your metaclass? Commented Jan 14, 2019 at 13:04

2 Answers 2

6

This does not work:

class a(type) :
    pass 
c = a()

...for the same reason for which this does not work:

c = type()

In the end, both do the same.

To use it as a metaclass, do this:

>>> class Class(metaclass=a):
...     pass
...
>>> Class
<class '__main__.Class'>
>>> type(Class)
<class '__main__.a'>

You could also instantiate the class directly, as you tried, but you have to provide the correct arguments:

AnotherClass = type('AnotherClass', (), {})

YetAnotherClass = a('YetAnotherClass', (), {})
Sign up to request clarification or add additional context in comments.

4 Comments

@zvzone : In the second example here , you are calling the init of the type class which is implicitly calling up the new method right ?
@itpdusra __init__ does not call __new__. When an instance is constructed, __new__ is called first and then __init__ is called. __new__ creates a new instance and __init__ initializes it. In other words, result of __new__ is passed to __init__ as self. See Python doc on __new__ for more details.
Understood that , what I wanted to be sure of is whether with the second line of instantiation you are calling the constructor of the metaclass
@itpdusra I see. The metaclass constructor is called after the last line of class definition, i.e. after class Class(metaclass=a): and pass. Try creating a metaclass with prints in __new__ and __init__ and then see when they are printed.
1

This error is due to you not respecting type's signature.

Inheriting from type is indeed enough for a class to be used as a metaclass, but the thing is you actually have to use it as a metaclass.

type itself has "two working modes: if called with 3 positional arguments, it creates a new class. And then type is the metaclass of that class. If called with 1 positional argument, it creates no new class or object at all - instead, it just returns that object's class.

But it makes no sense calling type with no arguments at all. And the arguments in the modes above are not optional. So, you will get a TypeError if your try to call type with no arguments at all - and that is not a "TypeError because something went wrong with the type class" - it is a "TypeError because your call did not match the callable signature".

When you inherit from type and change nothing, you class will behave exactly the same as the original type: you can call it with either one or three positional arguments, and the code responsible for working in either mode lies in type.__new__.

Now, if you want to use your class as a metaclass, you can indeed call it, but in the three argument form: you ass it the new class name, its bases and its attributes - which can actually be all empty, but you have to pass a string, a tuple and a dictionary as these three arguments:

class A(type): pass

myclass = A("", (), {})

And now, A is working as the metaclass for myclass:

In [16]: type(myclass)                                                                                   
Out[16]: __main__.A

However, whenever one defines a metaclass it is more usual to use it with the metaclass= named argument when declaring a class body:

In [17]: class  MyOtherClass(metaclass=A): 
    ...:     pass 
    ...:                                                                                                 

In [18]: type(MyOtherClass)                                                                              
Out[18]: __main__.A

Python's runtime will then compile this class body, and when the bytecod for it is executed, it will make the call to your metaclass' __new__ (and then __init__, and before that its __prepare__) method, so that it works as a metaclass.

So, just in case it is not clear: when you derive a class from type intending to use it as a metaclass, there is no need to further instantiate it to say that "it is now a metaclass". A subclass of type already can be a metaclass, and its instances will be classes, that will have it as a metaclass.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.