AlphaSocket
AlphaSocket

Reputation: 23

python 3 Call child attribute from parent method

Can't call child attribute in parent method, here's the test:

#!/usr/bin/env python3

class A():
    def getPath(self):
        return self.__path

class B(A):

    def __init__( self, name, path):
        self.__name = name
        self.__path = path

instance = B('test', '/home/test/Projects')

print(instance.getPath())

Running python test file $ ./test.py returns

./test.py 
Traceback (most recent call last):
  File "./test.py", line 17, in <module>
    print(instance.getPath())
  File "./test.py", line 6, in getPath
    return self.__path
AttributeError: 'B' object has no attribute '_A__path'

Upvotes: 2

Views: 4235

Answers (2)

Aaron Bentley
Aaron Bentley

Reputation: 1380

You are getting this because you are using a private attribute. If you do it using a non-private attributes, it will succeed.

Private attributes in Python are designed to allow each class to have its own private copy of a variable without that variable being overridden by subclasses. So in B, __path means _B__path, and in A, __path means __A_path. This is exactly how Python is intended to work. https://docs.python.org/3/tutorial/classes.html#tut-private

Since want A to be able to access __path, you should not use double-underscores. Instead, you can use a single underscore, which is a convention to indicate that a variable is private, without actually enforcing it.

#!/usr/bin/env python3

class A():
    def getPath(self):
        return self._path

class B(A):

    def __init__( self, name, path):
        self.__name = name
        self._path = path

instance = B('test', '/home/test/Projects')

print(instance.getPath())

$ ./test.py
/home/test/Projects

Upvotes: 5

chepner
chepner

Reputation: 530922

Don't use __path unless you know why you need it. _path is sufficient to mark it as "private". However, A.getPath should not be trying to return anything that that isn't defined in A itself. Instead, call A.__init__ from B.__init__ to ensure any A-specific initialization is done correctly:

class A:
    def __init__(self, path):
        self._path = path

    def get_path(self):
        return self._path

class B(A):

    def __init__(self, name, path):
        super().__init__(path)
        self._name = name

instance = B('test', '/home/test/Projects')

print(instance.get_path())

Now that _path is properly isolated to A, you could switch back to __path if, for example, you were concerned about a child class adding its own _path attribute.

class A:
    def __init__(self, path):
        self.__path = path

    def get_path(self):
        return self.__path

class B(A):
    def __init__(self, name, path):
        super().__init__(path)
        self._name = name
        self._path = "foo"

 instance = B('test', '/home/test/Projects')

 print("{}, not {}".format(instance.get_path(), instance._path))

Upvotes: 2

Related Questions