Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: type() takes 1 or 3 arguments

Tags:

python

I have a TypeClass to make Class:

class MyMetaClass(type):

    def __new__(cls, *args, **kwargs):
        print('call __new__ from MyMetaClass.')
        return type(cls.__name__, *args, **kwargs)

but when use it :

Foo= MyMetaClass('Foo', (), {'name':'pd'})

raise Error :

TypeError: type() takes 1 or 3 arguments

if Change it like :

class MyMetaClass(type):
    def __new__(cls, *args, **kwargs):
        print('call __new__ from MyMetaClass.')
        return type(cls.__name__, (), {})

it will works okey ! where is problem ?

like image 427
Zero Days Avatar asked Sep 07 '15 12:09

Zero Days


1 Answers

The __new__ method is passed 3 positional arguments in args; the class name, the baseclasses and the class body. The cls argument is bound to the metaclass, so MyMetaClass here.

You are adding another name to that sequence; drop the name, or remove the first argument from args:

class MyMetaClass(type):
    def __new__(cls, *args, **kwargs):
        print('call __new__ from MyMetaClass.')
        return type(*args, **kwargs)

or

class MyMetaClass(type):
    def __new__(cls, *args, **kwargs):
        print('call __new__ from MyMetaClass.')
        return type(cls.__name__, *args[1:], **kwargs)

The cls argument is the metaclass object however, so unless you want all your classes to be called MyMetaClass I'd stick with the first option.

See the Customizing class creation section of the Python data model:

These steps will have to be performed in the metaclass’s __new__() method – type.__new__() can then be called from this method to create a class with different properties. This example adds a new element to the class dictionary before creating the class:

class metacls(type):
    def __new__(mcs, name, bases, dict):
        dict['foo'] = 'metacls was here'
        return type.__new__(mcs, name, bases, dict)

and the object.__new__ documentation:

__new__() is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument. The remaining arguments are those passed to the object constructor expression (the call to the class).

where class of which an instance was requested is your metaclass (producing a class object).

Demo:

>>> class MyMetaClass(type):
...     def __new__(cls, *args, **kwargs):
...         print('call __new__ from MyMetaClass.')
...         return type(*args, **kwargs)
... 
>>> class Foo(object):
...     __metaclass__ = MyMetaClass
... 
call __new__ from MyMetaClass.
>>> Foo
<class '__main__.Foo'>
>>> class MyMetaClass(type):
...     def __new__(cls, *args, **kwargs):
...         print('call __new__ from MyMetaClass.')
...         return type(cls.__name__, *args[1:], **kwargs)
... 
>>> class Foo(object):
...     __metaclass__ = MyMetaClass
... 
call __new__ from MyMetaClass.
>>> Foo
<class '__main__.MyMetaClass'>
>>> # Note the  ^^^^^^^^^^^^ class.__name__ attribute here
...
like image 83
Martijn Pieters Avatar answered Nov 10 '22 21:11

Martijn Pieters