Live data from Hacker News

Making Python's __init__ method magical

blog.lerner.co.il

1–10 of 42 posts

Re: Making Python's __init__ method magical

#3
Really really minor nitpick, but when talking about languages and their traits these little details can become important for the reader to understand, lest it confuse them about other languages:

> While many people think of __init__ as a constructor, that’s not really the case. Rather, __init__ is invoked on our new object after it has been created

I'm not aware of a language (though I don't claim to know them all!) in which this is not exactly the definition of a constructor. In C++, `operator new` does the allocation, the constructor is called after. In java it's similar. In ruby it's Class#alloc (which is called by Class#new as roughly `ClassName.alloc.tap{|o| o.initialize(*args); o}`)

So the reader is correct to interpret it as a constructor. Constructors initialize the state of an uninitialized object.

Re: Making Python's __init__ method magical

#4

Really really minor nitpick, but when talking about languages and their traits these little details can become important for the reader to understand, lest it confuse them about other languages: > While many people think of __init__ as a constructor, that’s not really the case. Rather, __init__ is invoked on our new object after it has been created I'm not aware of a language (though I don't claim to know them all!)…

In Python you have a "real" constructor you can use: __new__. So I think it's good not to call __init__ a constructor. Unless of course you differ in my opinion that __new__ is a constructor?

Re: Making Python's __init__ method magical

#6
And here's a more magical version that wraps it with a decorator. If you're gonna do this I kind of like this way better, but I'm pretty sure your average python programmer finds it obscene:

    from pprint import pprint

    def default_init(init_func):
        arg_count = init_func.func_code.co_argcount
        arg_vars = init_func.func_code.co_varnames[1:1+arg_count]
        def wrap_init(self, *args, **kwargs):
            idx = 0
            for arg in arg_vars:
                if arg in kwargs:
                    setattr(self, arg, kwargs[arg])
                else:
                    setattr(self, arg, args[idx])
                    idx += 1

            init_func(self, *args, **kwargs)

        return wrap_init

    class testing(object):
        @default_init
        def __init__(self, a, b, c):
            pass

    x = testing(1, 2, 3)
    pprint(x.a)
    pprint(x.b)
    pprint(x.c)

    y = testing(1, 2, c=3)
    pprint(y.a)
    pprint(y.b)
    pprint(y.c)
[note: I'm aware this does not do even remotely a complete job as a decorator. Use at your own risk, it is a demonstration only.]

Re: Making Python's __init__ method magical

#7

Really really minor nitpick, but when talking about languages and their traits these little details can become important for the reader to understand, lest it confuse them about other languages: > While many people think of __init__ as a constructor, that’s not really the case. Rather, __init__ is invoked on our new object after it has been created I'm not aware of a language (though I don't claim to know them all!)…

In Python you have a "real" constructor you can use: __new__. So I think it's good not to call __init__ a constructor. Unless of course you differ in my opinion that __new__ is a constructor?

I do. __new__ is effectively an allocator, or an interface to it. It allocates an unconstructed object. Then the constructor, __init__ is called on that.

Edited to add: Reading around, it appears that smalltalk has no separation between allocation and construction, having only the equivalent to `operator new`/`Class#new`/`obj.__new`, with initialization also happening in overloads of that. So some confusion may stem from that. I do think the distinction is meaningful and important in languages that separate these two things out, though.

Re: Making Python's __init__ method magical

#8

And here's a more magical version that wraps it with a decorator. If you're gonna do this I kind of like this way better, but I'm pretty sure your average python programmer finds it obscene: from pprint import pprint def default_init(init_func): arg_count = init_func.func_code.co_argcount arg_vars = init_func.func_code.co_varnames[1:1+arg_count] def wrap_init(self, *args, **kwargs): idx = 0 for arg in arg_vars: if ar…

Very nice; it does seems far more reasonable to implement this as a decorator than as an explicit function call __init__.

Re: Making Python's __init__ method magical

#9

Earlier quoted context omitted.

In Python you have a "real" constructor you can use: __new__. So I think it's good not to call __init__ a constructor. Unless of course you differ in my opinion that __new__ is a constructor?

I do. __new__ is effectively an allocator, or an interface to it. It allocates an unconstructed object. Then the constructor, __init__ is called on that. Edited to add: Reading around, it appears that smalltalk has no separation between allocation and construction, having only the equivalent to `operator new`/`Class#new`/`obj.__new`, with initialization also happening in overloads of that. So some confusion may stem…

I'll have to check my terminology to understand this better. But I can assure you that most of the programmers who take my Python classes (who tend to come from Java, C#, and C++) are rather surprised that the constructor modifies an existing object, and doesn't actually create the object.

That said, I'm totally willing to believe that neither they nor I understood the difference between an "allocator" and a "constructor." I'll check into this further, both for myself and for the sake of future writing and lectures!

Re: Making Python's __init__ method magical

#10

Earlier quoted context omitted.

In Python you have a "real" constructor you can use: __new__. So I think it's good not to call __init__ a constructor. Unless of course you differ in my opinion that __new__ is a constructor?

I do. __new__ is effectively an allocator, or an interface to it. It allocates an unconstructed object. Then the constructor, __init__ is called on that. Edited to add: Reading around, it appears that smalltalk has no separation between allocation and construction, having only the equivalent to `operator new`/`Class#new`/`obj.__new`, with initialization also happening in overloads of that. So some confusion may stem…

In Smalltalk, 'new' is a class method which allocates a new instance, but which then has no special access to the internals of that newly created instance. An instance method, 'initialize' is then almost always used to set the initial state (by convention - this is not an actual language feature). Most commonly, initialize is called by new, but this again is just a convention, and a fairly recent one.
Post reply on HN