Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between __new__ and __init__ order in Python2/3

In Python 3, if any value that is not an instance of cls is returned, the __init__ method is never called. So I can, for example, do this:

class Foo:
    @staticmethod
    def bar(n):
        return n * 5

    def __new__(cls, n):
        return Foo.bar(n)

print(Foo(3))  # => 15

I was under the impression that the order was __call__ (if it's an instance) -> __new__ -> __init__.

However, in Python 2, this seems to raise a TypeError: this constructor takes no arguments due to the lack of an __init__. I can fix that by inheriting from object. So, running this:

class Foo:
    def __new__(cls, *args, **kwargs):
        print("new called")

    def __init__(self, *args, **kwargs):
        print("init called")

Foo()
"""
Python2: "init called"
Python3: "new called"
"""

In Python 2, I even messed around with metaclasses.

Meta = type("Meta", (type,), dict(__call__=lambda self, x: x * 5))

class Foo(object):
    __metaclass__ = Meta

print(Foo(4))  # => 20

But this does not work in Python3 because the init/new methods seem to be reversed.

Is there any Python2/3 compatible way of doing this?

Solution:

This is the way I did it. I don't like it, but it works:

class Foo(object):
    @staticmethod
    def __call__(i):
        return i * 5

    def __new__(cls, i):
        return Foo.__call__(i)

Surely there is a more pythonic way of doing this.

like image 610
Goodies Avatar asked Jul 22 '16 17:07

Goodies


1 Answers

In Python 2, you need to use new-style classes to make classes work properly. That means you need to define your class as class Foo(object). Then your first example will work in both Python 2 and Python 3.

like image 196
BrenBarn Avatar answered Oct 16 '22 15:10

BrenBarn