0

In the following toy example, the last line B().show() doesn't call the appropriate version of show function. It's supposed to call the child version rather than the parent version. I guess I should do something like __class_method() but couldn't figure out a complete answer.

I can certainly overwrite show function in B. But that essentially means to copy and paste show function. It's not elegant.

## version one ##
class A(object):

    def method(self):
        print("hello")

    def show(self):
        self.method()

class B(A):

    def method(self):
        print("goodbye")


A().show() ## print out hello
B().show() ## print out goodbye

## version two ##    
class A(object):

    def __method(self):
        print("hello")

    def show(self):
        self.__method()

class B(A):

    def __method(self):
        print("goodbye")


A().show() ## print out hello
B().show() ## print out hello
J. Lin
  • 139
  • 3
  • 11
  • The problem here is that the method `A.show` cannot access the private `B.__method` and invokes `A.__method` instead. As suggested, you will have to overload the `show` as well. – Sianur Jun 30 '18 at 04:48
  • 1
    What exactly is your purpose in using `__method` style names? I ask because there isn't really a "fix" to your issue other than "don't do that". You should almost never use name mangling, except for the rare situation where you have an internal variable in a proxy or mixin class that you don't want to accidentally collide with some unknown other class's variables. – Blckknght Jun 30 '18 at 04:54
  • @Blckknght First intention is trying to hide some attributes to users (e.g., users cannot see it through calling __dict__ – J. Lin Jun 30 '18 at 05:03
  • @Blckknght Second intention is that A itself is inherited from an outside library. Since that library use double underscore to hide similar attributes, I think it favorable to inherit this convention too. – J. Lin Jun 30 '18 at 05:16
  • 2
    *I think it's favorable to inherit this convention* -- I disagree ([for the reasons here](https://stackoverflow.com/questions/7456807/python-name-mangling)). – jedwards Jun 30 '18 at 05:24
  • 2
    And user *can* see it, you're just making it more difficult (e.g. see `dir(B)`). – jedwards Jun 30 '18 at 05:26
  • 1
    @J.Lin I believe I have pretty clearly outlined your options in my answer, but the important things are that you should definitely not use `_class__method`, and if you are trying to do exactly this, _elegantly_, then the most elegant way is for `__method` to be public. – MoxieBall Jun 30 '18 at 05:28

2 Answers2

0

If you start a method name with two underscores, it makes it accessible from only that class. Consider the following example:

class A():
    def __m(self):
        pass
    def m(self):
        print(self.__m)

A().m()  # <bound method A.__m of <__main__.A object at 0x10e0c1898>>
A().__m()  # AttributeError: 'A' object has no attribute '__m'

So what happened to A().__m? Check out A.__dict__, where attributes are looked up:

>>> A.__dict__
mappingproxy({'__module__': '__main__', '_A__m': <function A.__m at 0x10e0ab730>, 'm': <function A.m at 0x10e0ab7b8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})

Most importantly:

'_A__m': <function A.__m at 0x10e0ab730>

So the function you wrote, __m, is renamed to _A__m. You are not meant to access these methods from anywhere outside of the class.

If you have a method show in your class that calls a method __method (that starts with a __), it will only ever call that method of that class because it will never know about _B__method, only about _A__method.

Note that you absolutely should never use _A__method to call this method from outside of the class. If you need to do what you are doing, you should use one underscore.

If you really need to have the __method method of B be private, then yes, B should override show as well:

class A(object):
    def __method(self):
        print("hello")
    def show(self):
        self.__method()

class B(A):
    def __method(self):
        print("goodbye")
    def show(self):
        self.__method()

A().show() ## print out hello
B().show() ## print out goodbye
MoxieBall
  • 1,907
  • 7
  • 23
0

To preserve the private methods of each class, and be able to access them, you could do something like this:

class A(object):

    def __method(self):
        print("hello")
    method = __method

    def show(self):
        self.method()


class B(A):

    def __method(self):
        print("goodbye")
    method = __method

output:

hello
goodbye
Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
  • 1
    This makes the private method public: `B().method()` prints `goodbye`. – Sianur Jun 30 '18 at 04:47
  • Yes, it creates an alias for the private method; this alias is public. You cannot call the private method directly, therefore, you could (if it was useful), replace with `method = __blah`, `__blah` being some other private method of B, without breaking client code. – Reblochon Masque Jun 30 '18 at 04:54
  • Why would you declare a method as private and then provide a way for it to be accessed from anywhere? How is this any different than just not declaring it private? – MoxieBall Jun 30 '18 at 05:11
  • This is a good question @MoxieBall; I would not do this; however, there may be cases where you need to modify the behavior of your class without breaking client code, or subclasser's code, as alluded in my earlier comment. – Reblochon Masque Jun 30 '18 at 05:18
  • @ReblochonMasque I think that, if it is necessary to keep the `__method` convention for making methods private, the real solution is that every method that calls a `__method` must be overridden in B as well. I have just added this as an addendum to my answer – MoxieBall Jun 30 '18 at 05:21