9

I have a variable which may or may not get a value in the instance:

class EC():
   __init__(self, a=False):
   ...
   if a: self.__var = ...

Later I want to check if the __var exists in the instance. Because prepending __ to the name changes the internal name to _EC__var the checking code becomes a little bit messy:

if ''.join(['_',self.__class__.__name__,'__name']) in self.__dict__: ...

Is code above considered normal or not? If not what are the preferred alternatives?

One option I can think of is to give __var some value anyway, for example:

_no_value = object()
...
   def __init__(self, a):
      self.__var = _no_value
      ...
      if a: self.__var = ...

So later I can compare __var to _no_value instead of a mess with internal variables.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
Phoenix
  • 751
  • 1
  • 7
  • 13

4 Answers4

14

Just use hasattr(self, '_var') to see if it exists - it may be set to None but it will exist if hasattr says it does.

E.g.:

>>> class a():
...   def __init__(self):
...      self.a = 3
...      self._a_ = 4
...      self.__a__ = 'Fred'
...
>>> A=a()
>>> hasattr(a, 'a')
False
>>> hasattr(A, 'a')
True
>>> hasattr(A, '_a_')
True
>>> hasattr(A, '__a__')
True
>>> hasattr(A, '__b__')
False
>>>
Steve Barnes
  • 27,618
  • 6
  • 63
  • 73
  • 1
    This would work for single underscore, but it doen't work for double – Phoenix Aug 01 '13 at 14:29
  • I don't think you read the question properly, or understand what Python does to a `__var` double-underscore attribute on a class. No, it is not that simple. – Martijn Pieters Aug 01 '13 at 14:32
  • As you can see from my expanded answer it works fine here with single and double underscores. – Steve Barnes Aug 01 '13 at 14:45
  • Double-underscore-prefix-*and-suffixed* members don't get mangled; they're treated as magic methods. – ecatmur Aug 01 '13 at 14:50
  • Learn something new every day - I would make 2 points though - 1) If you need to access an attribute externally it is probably better not to name it in a way that magically gets mangled specifically to keep it private and is probably better practice not to do so 2) if you absolutely __have__ to use that naming convention you would probably be better off and a lot clearer to use a class that overrides `__getattr__` as discussed in http://stackoverflow.com/questions/2405590/how-do-i-override-getattr-in-python-without-breaking-the-default-behavior – Steve Barnes Aug 01 '13 at 18:51
14

You've forgotten the EAFP principle:

try:
    value = self.__var
except AttributeError:
    # do something else

If you're determined to use a sentinel, you can combine it with a class variable:

class EC():
    __var = object():
    ...
    if self.__var is not EC.__var:
        ...
KenHBS
  • 6,756
  • 6
  • 37
  • 52
ecatmur
  • 152,476
  • 27
  • 293
  • 366
4

Just set it to None on the class:

 class EC():
    __var = None

    __init__(self, a=False):
        ...
        if a: self.__var = ...

then test for if self.__var is not None.

If None should be a valid value for the attribute, use a different singleton sentinel:

_sentinel = object()

 class EC():
    __var = _sentinel

    __init__(self, a=False):
        ...
        if a: self.__var = ...

and test for if self.__var is not _sentinel.

This way, all references to __var are properly rewritten to include the class name.

The other path would be to not use double-underscore names for your attributes. __var should only be used for attributes you want to namespace to your specific class so that subclasses do not accidentally clobber it with their own attributes.

In other words, do not use double-underscore names unless you really understand what they are for and actually need it. Any code that is not part of a framework for wider consumption by unknown third parties? Just stick to single underscores instead.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
1

I guess there is a simple way to check this out. This is the way I tried it.

class Test:

    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

        if self.c:
            self.d = "The variable exists"
        if 'd' in self.__dict__:
            print(self.d)

Now instantiate the above class:

t = Test('123', 'asd', True)

The above code outputs something like:

The variable exists

If you want to see the contents of self.__dict__. Just type: print(self.__dict__) The output of above code will be like:

{'a': '123', 'b': 'asd', 'c': True, 'd': 'The variable exists'}

All the instance variables are stored in the format of dictionary in self.dict

I tried this out in python 3.8.1 and python 2.6.6. It worked out. If there is any misconception with the answer, please report back by comment.

rammanoj
  • 479
  • 8
  • 26