Hacker News new | ask | show | jobs
by guyzmo 3565 days ago
The part of the article about an issue with name mangling of private fields is somehow misleading.

The feature is just some syntactic sugar.

When within a class, private fields such as:

    class Foo:
        def __init__(self):
            self.__bar
are accessible from within other methods of class Foo as `self.__bar`. But that's just syntactic sugar, the real name of `self.__bar` is `self._Foo__bar`.

So from the outside "world", including `hasattr()`, you can still access `self.__bar` as `Foo()._Foo__bar`.

    >>> class Foo():
    ...   def __init__(self):
    ...     self.__bar = 'hello'
    ...   def show(self):
    ...     print(1, self.__bar)
    ...     print(2, getattr(self, '__bar'))
    ... 
    >>> foo = Foo()
    >>> foo._Foo__bar
    True
    >>> foo.show()
    1 hello
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in show
    AttributeError: 'Foo' object has no attribute '__bar'
    >>> foo.__bar = 'world'
    >>> foo.show()
    1 hello
    2 world
In the end, when `x.__private` is setup outside of the class definition, obviously, it's a new member as its name differs from the internal name `__private` (which really is `_X__private`).

From within the code doing `getattr('X', '__private')` will return the `__private` setup from outside the class, and `getattr('X', '_X__private')` the one defined from within the class.

The whole point of that feature is to ensure that members defined within a class that are not part of the public API are left untouched when that class get subclassed, to avoid unexpected behaviours.

Here's an example of why this has been designed:

  >>> class A:
  ...   def __init__(self):
  ...     self.__internal = "this is a"
  ...   def show(self):
  ...     print(1, "A", self.__internal)
  ... 
  >>> class B(A):
  ...   def __init__(self):
  ...     super(B, self).__init__()
  ...     self.__internal = "this is b"
  ...   def show(self):
  ...     super(B, self).show()
  ...     print(2, "B", self._A__internal)
  ...     print(3, "B", self.__internal)
  ... 
  >>> B().show()
  1 A this is a
  2 B this is a
  3 B this is b
  >>> 
There's nothing that should be surprising or asymmetrical to anybody who've read the python documentation, and use that feature appropriately. It's maybe a weird feature, but it's still a coherent and homogeneous behaviour and actually adding more safety to codes.

Documentation references:

  * https://docs.python.org/3/faq/programming.html#i-try-to-use-spam-and-i-get-an-error-about-someclassname-spam
  * https://docs.python.org/3/reference/expressions.html#atom-identifiers
4 comments

"Private" fields and methods should use one underscore. Two underscores are for name mangling issues and __method__ is for "magic" methods.
I'd say that private methods are double underscored, and protected methods are single underscored, since the goal of the __ is to prevent child classes from being able to use the parent implementation via self.__meth.
Not prevented from being able to use, but from accidentally using or overriding the parent's. The child can still use the mangled name.
Well, private fields are all members starting with an underscore and that are not publicised in the documentation.

/That/ includes double underscores mangled members and and "magic" methods.

> The feature is just some syntactic sugar.

I would not call it "syntactic sugar", but rather a leak of implementation details. It could be deliberate, like Perl did for its OO (showing the entrails of all its objects), but it's not particularly sugary-sweet-yummy.

It's not an implementation detail, it is a deliberate, specified and documented feature of the language dedicated to namespace conflict resolutions in the context of inheritance.

ref: https://docs.python.org/2/reference/expressions.html#atom-id...

ref: https://docs.python.org/2/faq/programming.html#i-try-to-use-...

It's so crude that, again, it looks like implementation details having been promoted to specs. Field visibility and name mangling done with very magic underscores just doesn't look right, to me.
> Field visibility

It has nothing whatsoever to do with field visibility.

The single leading underscore?
Is conventional, it doesn't do anything at the language level.
Yes! Exactly! I added the py3 links of those in my big lengthy parent comment, so people get twice the chance to RTFM !
Yes, it is intended and fully part of the philosophy behind the language's design (as shown in PEP-20, aka the Zen of Python):

> Explicit is better than implicit

So there's no "leaking" of implementation details, because the implementation shall always be fully exposed.

As said in a sibling post, the private fields are just public fields, which are not documented as part of the public API and start with a `_`.

And as I said in the parent post, the reason to use the name mangling mechanism on top of that is to ensure that those variables won't be used by descendants in the class hierarchy, when a given class is intended to be subclassed by a peer.

The the sugar /is/ actually sugary-sweet-yummy, as it's preventing potential faults from people who blindly subclass stuff they haven't read the source code of.

I get full well what it does, but I find the syntax "magic" and not very attractive. Why no special keyword for visibility, proper namespaces etc.?

In c++ and likes, I know there's a vtable somewhere, and I get why there must be one for virtual funcs, but I don't want to deal with it directly, it's the compiler job. Same for Python, I know it must prevent fields from getting clobbered when inheriting, but I don't want to be exposed to its mangling or whatever other mechanism it uses.

> Same for Python, I know it must prevent fields from getting clobbered when inheriting, but I don't want to be exposed to its mangling or whatever other mechanism it uses.

Python does not prevent anything, it gives the developer a name mangling tool, it's up to the developer whether they want to use it or not. By default, identical names will conflict and you will clobber supertype fields or methods.

Again, that's very Perl-ish, to me. "Here are some tools and tricks, use them to do your own OO if you want--but you can easily bypass it all every time you wish".
And name-mangling is less about privacy than about preventing accidental overrides.
Two spaces indentation will give you the proper rendering for code snippets.
just been fixing that, though I've added four, I hope it's ok
Yes, looks much better now.