Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need to implement proper __new__ handling for instance classes #606

Open
pfalcon opened this issue May 11, 2014 · 7 comments
Open

Need to implement proper __new__ handling for instance classes #606

pfalcon opened this issue May 11, 2014 · 7 comments

Comments

@pfalcon
Copy link
Contributor

pfalcon commented May 11, 2014

Snippet from CPy stdlib urlparse.py:

class Quoter(collections.defaultdict):
    def __init__(self, safe):
        """safe: bytes object."""
        self.safe = _ALWAYS_SAFE.union(safe)

You see, it doesn't call super().__init__(). That's because it relies on __new__ to initialize base class automatically. Specifically for CPy, collections.defaultdict is at all native type, and to emulate its behavior we need __new__.

Another testcase:

class Base:

    def __new__(cls):
        print("Base __new__")

class Sub(Base):
    pass


o = Sub()
@pfalcon
Copy link
Contributor Author

pfalcon commented May 11, 2014

Relevant clauses from https://docs.python.org/3.4/reference/datamodel.html :

Classes are callable. [...] The arguments of the call are passed to new() and, in the typical case, to init() to initialize the new instance.

https://docs.python.org/3.4/reference/datamodel.html#object.__new__

That doc doesn't saying anything about implicitly calling base types' __new__ in case current instance lacks it, but that's expected from usual MRO. It does says that if __new__ is defined, then it is its responsibility to call superclass' __new__ (which is also "as expected" in Python).

@pfalcon
Copy link
Contributor Author

pfalcon commented May 19, 2014

Initial implementation pushed in 13684fd , likely more elaboration needed is we want to achieve real Python compliance.

@pfalcon
Copy link
Contributor Author

pfalcon commented May 20, 2014

#622 split off from this.

@pfalcon
Copy link
Contributor Author

pfalcon commented May 20, 2014

And what's left here is support proper __new__ vs __init__ separation for native types. In 52386ca I made attempt to address this issue in adhoc way, but that doesn't scale of course - this just duplicates part of make_new() code.

We also don't want to 2 methods for native types - that will hurt performance. How it is now (make_new represents both new + init) is ok for 95% of cases, we just need to allow to call "new" vs "init" parts separately for remaining 5% cases. Proposal is to use the same tricks as with print method - pass additional enum value which will specify which ops to perform - new, init, or both. And for init case, the make_new should just expect that it will be given an object, not type, pointer.

@dpgeorge : Any objections?

@pfalcon
Copy link
Contributor Author

pfalcon commented May 20, 2014

Example of currently broken code due to lack of the above: 66ab571

@pfalcon
Copy link
Contributor Author

pfalcon commented May 22, 2014

And what's left here is support proper new vs init separation for native types. In 52386ca I made attempt to address this issue in adhoc way,

In the meantime, more adhockery: a8408a8 & 806ea1f

@pfalcon
Copy link
Contributor Author

pfalcon commented May 22, 2014

Also, it turns out that argument handling to (object's) __init__ & __new__ is subject to heavy idiosyncrasies:

Canonical reference:

http://hg.python.org/cpython/file/44ed0cd3dc6d/Objects/typeobject.c#l2818

Questions & commentary:

I have no idea how we'd handle this mind-boggling stuff, apparently, we just should accept arbitrary params to object.__init__ & object.__new__, voila.

pfalcon referenced this issue Dec 12, 2017
This patch cleans up and generalises part of the code which handles
overriding and calling a native base-class's __init__ method.  It defers
the call to the native make_new() function until after the user (Python)
__init__() method has run.  That user method now has the chance to call the
native __init__/make_new and pass it different arguments.  If the user
doesn't call the super().__init__ method then it will be called
automatically after the user code finishes, to finalise construction of the
instance.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant