Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will python class __init__ method implicitly return None?

Tags:

python

I tried to return a value in a class constructor (init):

class A:
  def __init__(self):
    return 1

but there is a run-time error saying init should return None. If this is the case, how to understand:

a=A()

where "a" is assigned as the class instance?

like image 703
Hailiang Zhang Avatar asked Oct 24 '12 05:10

Hailiang Zhang


1 Answers

Strictly speaking, it's not A.__new__() that's creating the the instance a.

When you define class A(object): (or class A: as well if you are using Python3, class A: is the old-style class that has been deprecated), it is __new__ from the inheritedobject.__new__() that is being called to create the instance a.

When a = A() is executed, what happens is:

  1. A() is a shorthand for A.__call__
  2. object.__new__(cls, *args, **kwargs) where cls=A, is what actually happens under hood to create instance a. It allocates the memory for the new object, and should then return a new object (the instance).
  3. if and only if the newly created object is returned does __init__(self) then get called with the newly created object passed to it to "initilize" the object.

Consider the following demo:

  • when we override __new__ and no longer returns an object, __init__ will not get called:

    class A(object):
    
        def __new__(cls, *args, **kwargs):
            print cls, args, kwargs
    
        def __init__(self):
            self.x = 'init!'
            print self.x
    
    
    In : a = A()
    <class '__main__.A'> () {}
    
    # note that "init!" has not appeared here because __new__ didn't return an
    # new instance
    
  • now, return a new instance by using object.__new__, and you will see that after __new__, __init__ would be called as well:

    class A(object):
    
        def __new__(cls, *args, **kwargs):
            print cls, args, kwargs
            return object.__new__(cls, args, kwargs)
    
        def __init__(self):
            self.x = 'init!'
            print self.x
    
    In : a = A()
    <class '__main__.A'> () {}
    init!
    

Here is another demo to display the difference, note that instance a can be created without calling __init__():

class A(object):
    def __init__(self):
        self.x = "init!"
        print self.x

In : a = object.__new__(A)

In : a
Out: <__main__.A at 0x103466450>

In : a.__dict__
Out: {}


In : aa = A()
init!

In : aa
Out: <__main__.A at 0x1033ddf50>

In : aa.__dict__
Out: {'x': 'init!'}

Now for the inquisitive (and also to refresh my own memory =]):

Roughly speaking, there are two main ways to create new objects in Python:

Create new object (type / class) by subclassing:

class statements tells Python to create a new type / class object(by subclassing an existing type/class such as object):

class Hello(object):
    pass
>>> Hello.__class__
<type 'type'>

In fact all class/type object have type type. The type of type (type(type)) is still type.

You can subclass a type / class object.

Create new object (instance) by instantiating:

You can also create a new object by instatiating an existing type object. This is done via the using the __call__ operator (shorthand by ()):

>>> h = hello()
>>> type(h)
<class '__main__.Hello'>
>>> type(int('1'))
<type 'int'>

You cannot subclass an instance object.

(note that you can also create a new instance object by some other means such as using the list operator [1,2,3], in this case it creates an list instance)

You can check an object's type by type(my_obj) or my_object.__class__.


Now you know how an instance object is created, but what really creates the type / class object (that allows to create instance objects)?

In fact, these objects are created by instantiation as well, albeit it is a slightly different kind of instantiation from what was mentioned earlier.

Aside from class statement, you can also use type(cls_name, parent_class_tuple, attr_dict) to create a new class. For eg:

type('Hello', (object,), {})

will create the Hello class same as the one shown earlier.

What is type? Enter metaclass.

type is a metaclass, which is the class of class, i.e., classes are instances of metaclasses. The __class__ of type is still type.

So here is a graph that shows the relationships between metaclass, class, instance:

            instantiate             instantiate
metaclass   --------------> class   ---------------->    instance
            type.__new__()          object.__new__()

When metaclass type is called to create a new class, the similar flow goes:

  1. type.__call__() is excuted
  2. type.__new__() allocates memory and then returns new a class (a metaclass instace), and then calls type.__init__().
  3. type.__init__() initalizes the newly created class that was passed from step 2.

You can even create a new metaclass by subclassing type:

class MyMeta(type):
    def __new__(meta, name, bases, dct):
        # do something
        return super(MyMeta, meta).__new__(meta, name, bases, dct)
    def __init__(cls, name, bases, dct):
        # do something
        super(MyMeta, cls).__init__(name, bases, dct)

then you can create a new class from this MyMeta metaclass just like you do with type:

MyClass = MyMeta('MyClass', (object, ), {'x': 1})

Or, use __metaclass__ when defining your class, which has exactly the same effect as what was shown above:

class MyClass(object):
    __metaclass__ = MyMeta
    x = 1
like image 183
K Z Avatar answered Sep 20 '22 09:09

K Z