Remix.run Logo
xdennis 6 hours ago

I'm always surprised by how arrogant and unaware Python developers are. JavaScript/C++/etc developers are quite honest about the flaws in their language. Python developers will stare a horrible flaw in their language and say "I see nothing... BTW JS sucks so hard.".

Let me give you just one example of Python's stupid implementation of inheritance.

In Python you can initialize a class with a constructor that's not even in the inheritance chain (sorry, inheritance tree because Python developers think multiple inheritance is a good idea).

    class A:
        def __init__(self):
            self.prop = 1

    class B:
        def __init__(self):
            self.prop = 2

    class C(A):
        def __init__(self):
            B.__init__(self)


    c = C()
    print(c.prop) # 2, no problem boss
And before you say "but no one does that", no, I've see that myself. Imagine you have a class that inherits from SteelMan but calls StealMan in it's constructor and Python's like "looks good to me".

I've seen horrors you people can't imagine.

* I've seen superclass constructors called multiple times.

* I've seen constructors called out of order.

* I've seen intentional skipping of constructors (with comments saying "we have to do this because blah blah blah)

* I've seen intentional skipping of your parent's constructor and instead calling your grandparent's constructor.

* And worst of all, calling constructors which aren't even in your inheritance chain.

And before you say "but that's just a dumb thing to do", that's the exact criticism of JS/C++. If you don't use any of the footguns of JS/C++, then they're flawless too.

Python developers would say "Hurr durr, did you know that if you add a object and an array in JS you get a boolean?", completely ignoring that that's a dumb thing to do, but Python developers will call superclass constructors that don't even belong to them and think nothing of it.

------------------------------

Oh, bonus point. I've see people creating a second constructor by calling `object.__new__(C)` instead of `C()` to avoid calling `C.__init__`. I didn't even know it was possible to construct an object while skipping its constructor, but dumb people know this and they use it.

Yes, instead of putting an if condition in the constructor Python developers in the wild, people who walk among us, who put their pants on one leg at a time like the rest of us, will call `object.__new__(C)` to construct a `C` object.

    def init_c():
        c2 = object.__new__(C)
        c2.prop2 = 'three'
        print(c2.__dict__, type(c2)) # {'prop2': 'three'} <class '__main__.C'>
And Python developers will look at this and say "Wow, Python is so flawless".
dragonwriter 4 hours ago | parent | next [-]

> In Python you can initialize a class with a constructor that's not even in the inheritance chain

No, you can't. Or, at least, if you can, that’s not what you’ve shown. You’ve shown calling the initializer of an unrelated class as a cross-applied method within the initializer. Initializers and constructors are different things.

> Oh, bonus point. I've see people creating a second constructor by calling `object.__new__(C)` instead of `C()` to avoid calling `C.__init__`.

Knowing that there are two constructors that exist for normal, non-native, Python classes, and that the basic constructoe Class.__new__, and that the constructor Class() itself calls Class.__new__() and then, if Class.__new__() returns an instance i of Class, also calls Class.__init__(i) before returning i, is pretty basic Python knowledge.

> I didn't even know it was possible to construct an object while skipping its constructor, but dumb people know this and they use it.

I wouldn’t use the term “dumb people” to distinguish those who—unlike you, apparently—understand the normal Python constructors and the difference between a constructor and an initializer.

kccqzy 6 hours ago | parent | prev | next [-]

Oh I've seen one team constructing an object while skipping the constructor for a class owned by another team. The second team responded by rewriting the class in C. It turns out you cannot call `object.__new__` if the class is written in native code. At least Python doesn't allow you to mess around when memory safety is at stake.

4 hours ago | parent | prev | next [-]
[deleted]
maleldil 5 hours ago | parent | prev | next [-]

For what it's worth, pyright highlights the problem in your first example:

    t.py:11:20 - error: Argument of type "Self@C" cannot be assigned to parameter "self" of type "B" in function "__init__"
        "C*" is not assignable to "B" (reportArgumentType)
    1 error, 0 warnings, 0 information 
ty and pyrefly give similar results. Unfortunately, mypy doesn't see a problem by default; you need to enable strict mode.
drekipus 6 hours ago | parent | prev | next [-]

1. Your first example is very much expected, so I don't know what's wrong here.

2. Your examples / post in general seems to be "people can break semantics and get to the internals just to do anything" which I agree is bad, but python works of the principle of "we're all consenting adults" and just because you can, doesn't mean you should.

I definitely don't consent to your code, and I wouldn't allow it to be merged in main.

If you or your team members have code like this, and it's regularly getting pushed into main, I think the issue is that you don't have safeguards for design or architecture

The difference with JavaScript "hurr durr add object and array" - is that it is not an architectural thing. That is a runtime / language semantics thing. One would be right to complain about that

Spivak 4 hours ago | parent | prev [-]

I don't understand the problem with your first example. The __init__ method isn't special and B.__init__ is just a function. Your code boils down to:

    def some_function(obj):
      obj.prop = 2

    class Foo:
      def __init__(self):
        some_function(self)

    # or really just like

    class Foo:
      def __init__(self):
        self.prop = 2
Which like, yeah of course that works. You can setattr on any object you please. Python's inheritance system ends up being sane in practice because it promises you nothing except method resolution and that's how it's used. Inheritance in Python is for code reuse.

Your examples genuinely haven't even scratched the surface of the weird stuff you can do when you take control of Python's machinery—self is just a convention, you can remove __init__ entirely, types are made up and the points don't matter. Foo() isn't even special it's just __call__ on the classes type and you can make that do anything.

dragonwriter 4 hours ago | parent [-]

With the assumptions typical of static class-based OO (but which may or may not apply in programs in Python), this naively seems like a type error, an even when it isn't it introduces a coupling where the class where the call is made likely depends on the internal implementation (not just the public interface) of the called class, which is...definitely an opportunity to introduce unexpected bugs easily.