2

I have defined a base class that handles generic content, and children classes that are more specific in which content (object type) they accept. However, when calling the function defined in the parent class, the parent variables are used instead of the child variables. How do I get Python to use the child variable inside the parent functions ?

Example code :

class BaseField(object):
    __accepted_types = (object,)

    def __init__(self, description=""):
        self.content = None
        self.description = description

    def set(self, content):
        if isinstance(content, self.__accepted_types):
            print(type(content), self.__accepted_types)
            print(type(self))
            self.__set(content)
        else:
            raise ValueError("wrong type")

    def __set(self, content):
        pass

class StringField(BaseField):
    __accepted_types = (basestring,)

    def __init__(self, description=""):
        super().__init__(description)
        print(self.__accepted_types)

if __name__ == '__main__':
    x = StringField("a test field")
    x.set(3)

I expect this code to raise a ValueError (I'm trying to pass an int to a class that only accepts str), however I get the following result:

(<class 'str'>,)
<class 'int'> (<class 'object'>,) <class '__main__.StringField'>

Note that the function correctly returns that it's within "StringField" but uses the "BaseField" value for __accepted_types. The behavior is the same if I declare __accepted_types as an instance variable.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
pills
  • 656
  • 1
  • 5
  • 10

2 Answers2

2

You're falling victim to name mangling; the names are getting mangled inside the class body and self.__accepted_types get's transformed to _BaseField__accepted_types inside set.

Change all occurrences of __accepted_types to accepted_types and it'll work just fine.

p.s If you're indeed using Python 3, basestring doesn't exist, use str.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • Can you be more specific? When changing `self.__accepted_types` to `__accepted_types` or `accepted_types` in `set(self, content)` method, I get `NameError: name '_BaseField__accepted_types' is not defined` – pills May 02 '17 at 15:09
  • 1
    Just drop the "__" prefix for accepted_types everywhere. – jsbueno May 02 '17 at 15:10
  • Yes, change the occurence of `__accepted_types` to `accepted_types` everywhere as jsbueno noted. – Dimitris Fasarakis Hilliard May 02 '17 at 15:11
  • The trap you've fallen in is: your probably came in from a language where attributes could be "protected" and "privileged" - Python does not work that way. It has a name-mangling mechanism that could allow exactly for base class to see the non-overiden versions of attributes it defines - the "__" prefix for attributes. The oposite of what you want. Fact is early Python tutorials mistake this mechanism with some form of "protected" attribute access, which it is not. – jsbueno May 02 '17 at 15:12
  • Thank you, this does the trick (and thanks also for the `basestring`/`str` use in Python 3). As jsbueno mentioned, I assumed that __ was roughly the equivalent of private or protected, whereas it's almost the exact opposite of protected... – pills May 02 '17 at 15:19
  • @pills If you desire to "mark" the `accepted_types` attribute as something that child classes ought not mess with, it is considered normative to use a single underscore in the name, which will not get mangled: `_accepted_types`. However, this is only a convention; there is nothing about the single underscore that prevents child classes from reaching in and mucking about. – Rick May 02 '17 at 16:09
1

As a followup to the other answer, I highly recommend watching this talk from PyCon 2013, in which the purpose and use of the Python name-mangling paradigm is discussed.

To summarize, using double underscore for your object variables it not a way to keep them "private". In fact, private variables in Python are not "a thing"; when coming from other languages, using Python effectively requires a shift in thinking on this and other topics.

The purpose of name-mangling it to prevent parent and child classes from falling into the trap of namespace wars, in which, for example, some child class is using an attribute name for a different purpose than the parent class.

For further reading, see these other questions and answers:

Underscore vs Double underscore with variables and methods

What is the meaning of a single- and a double-underscore before an object name?

Community
  • 1
  • 1
Rick
  • 43,029
  • 15
  • 76
  • 119