Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> Who here has another language that they knew pretty well before learning Python? (most hands go up) Great! Terrific! That’s a superpower you have that I can never have. I can never unlearn Python, become fluent in another language, and then learn Python again. You have this perspective that I can’t have.

It felt a little strange to read this. I knew many languages "pretty well" (or would have said so at the time) before Python, but it's been my primary programming language pretty much the entire time since I first picked it up 20 years ago. So I do have that perspective (I also spend a fair amount of time thinking about PL design in the abstract), but I don't feel "PSL" at all.

> Does the value stored in `a` know the name of that variable?

Okay, I'm stumped - in what other language would it "know" (I assume this means automatically recording the string "a" as part of the object's state in some way)? Or is this just referring to debugging information added in some environments?

> Python = is like Java, it’s always a reference and never a copy (which it is by default in C++).

Java has primitive types, for which it is a copy. But it's more accurate IMO to attribute this as a property of names, rather than a property of assignment. Python's names (and most Java values, and some C# values) have reference semantics. Having understood that properly, saying that Python (like Java) is "pass by assignment" then just follows naturally, and all the usual confusion around this point vanishes. Passing an argument to a function is simply assigning it to the corresponding parameter. (C++'s idea of "references" in the type system is far more complex IMO - e.g. https://langdev.stackexchange.com/q/3798/_/3807#3807 .)

> We have both reference counting and garbage collection in Python.

This is strange phrasing, because reference counting is a garbage collection strategy. I assume that here "garbage collection" is meant to refer to mark-and-sweep etc. strategies that operate in a separate pass, timed independently of the explicit code. But also, the reference-counting system is a CPython implementation detail. (Although it does look like PyPy tries to emulate that, at least: https://doc.pypy.org/en/default/discussion/rawrefcount.html )

> I’ll translate this one to the transcript later - for now you’ll have to watch it because the visual is important: explicit super.

Raymond Hettinger's video is also a useful reference here: https://www.youtube.com/watch?v=EiOglTERPEo

> It takes less characters in Python!

This checks out, but it would be fairer to show what it looks like on the declaration side rather than just the usage side.



> This is strange phrasing, because reference counting is a garbage collection strategy.

Oof, don't open that can of worms. Let me summarize the result: 1) the distinction is arbitrary and 2) people are very passionate about their personal view being considered canonically correct. To the extent i've started referred to refer to both mark-and-sweep/boehm garbage collection and reference counting as "automatic memory management".


I think people get unhappy that their favorite language doesn’t have real GC and it becomes an extension of their language advocacy. Python didn’t have GC for a long time.


Not everyone wants GC either, so the mix of feelings must have been complex.

I didn't much follow these kind of holy wars, but I've seen people leave Java partly to get away from GC, same as people in the Objective C community being pretty happy to get ARC instead of heavier GC systems.


Let's just agree to disagree. I don't see any value fighting over these words.


> Java has primitive types, for which it is a copy. But it's more accurate IMO to attribute this as a property of names, rather than a property of assignment. Python's names (and most Java values, and some C# values) have reference semantics. Having understood that properly, saying that Python (like Java) is "pass by assignment" then just follows naturally, and all the usual confusion around this point vanishes. Passing an argument to a function is simply assigning it to the corresponding parameter. (C++'s idea of "references" in the type system is far more complex IMO - e.g. https://langdev.stackexchange.com/q/3798/_/3807#3807 .)

I use pass-by-label for this term, after a visual demonstration of variables that showed them as labels that attach to different values/objects. But I like "pass-by-assignment" a lot — it gives the correct intuition that whatever is happening when I do `x = 5` is also happening when I do `def func(x): pass; func(5)`.


Well, except in the case of variable shadowing.


In C# you can do

string foo = "bar";

string nameOfFoo = nameof(foo); // “foo”

Kinda nice for iterating through lists of variables and saving them to a key/value map


nameof() is a compile time method, not a runtime method like most dynamic language equivalents.


Interesting, but it isn't causing the value (here, the "bar" string object) to know about that name.


Name of! Never knew this existed. I think it would make writing some debug statements easier and immune to renames.


> > Does the value stored in `a` know the name of that variable?

> Okay, I'm stumped - in what other language would it "know" (I assume this means automatically recording the string "a" as part of the object's state in some way)? Or is this just referring to debugging information added in some environments?

Python is (or can be) the anomaly here because of descriptors, specifically __set_name__: https://docs.python.org/3/reference/datamodel.html#object.__...

I'm partial to using descriptors to reference attribute names (as opposed to using arbitrary strings) so that refactoring tools recognize them. That looks roughly like:

    _T_co = TypeVar("_T_co", covariant=True)

    class Parameter(Generic[_T_co]):
        """
        Here we push descriptors a bit further by using overloads to distinguish
        type-level vs. instance-level references. Given:

            ```
            class C:
                param: Parameter[Param] = Param(...)

            c = C(param=Param.VALUE)
            ```

            `c.param` operates as attribute accesses normally do, invoking the
            descriptor with `instance=c` and transparently returning `Param.VALUE`.

            `C.param`, on the other hand,  calls `__get__` with `instance=None`,
            which is recognized by the overload as returning `Param` itself.

            Thus `C.param.parameter_name` returns the string `param`, is typed
            correctly, and if you need to find or update references to `param`
            in the future, `setattr(c, C.param.parameter_name)` will be handled,
            where `setattr(c, "param")` wouldn't have been.
        """

        @overload
        def __get__(self, instance: None, owner: type) -> Param: ...

        @overload
        def __get__(self, instance: Any, owner: type) -> _T_co: ...

        def __get__(self, instance: object, owner: type) -> Param | _T_co: ...

        def __set__(self, instance: object, value: Param | _T_co) -> None: ...

        parameter_name: str
        """The parameter name this param has been assigned to."""

        def __set_name__(self, _owner: type, parameter_name: str) -> None:
            # Descriptor protocol to capture the field name as a string for reference
            # later. This spares us having to write parameter names as strings, which
            # is both error-prone and not future-proof.

            # The type may be immutable, with dot assignment and normal setattr not
            # available.
            object.__setattr__(self, "parameter_name", parameter_name)


Neat. That actually was beyond what I'd committed to memory about the object/class model. But this only applies within a class body, which seems to be excluded in the article's example (since it isn't indented).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: