Question

i created a class, whose attributes are all optional. At the moment, my code is thoroughly afflicted by try: ... except AttributeError: ... blocks, but I'm wondering if this is the best approach to take.

I am now changing my approach to using the None type for every unknown attribute which makes my code look better in my opinion, but I'm still wondering if there's a better approach or if I just have to deal with the tests that come with the optionality.

I'm trying to make a Coordinates, which has to be modified in special ways and which are often not known in advance, but have to be calculated with help from some other instances, which is why the values have to be optional.

It would be great to hear about your experiences and suggestions.

Edit:

Thank you all for your answers! You all were really fast... while i am a very slow, sorry. Since the topic is quite abstract, i had to take some more time to think about it. I accept your answer, Ethan as solution, because I think that's the next direction I will investigate in. I will just post some code to clarify the following statements. My old code for the __add__-routine looked like this:

def __add__(self, other):
  """Add the given *masses* and calculate the resulting center of
  gravity. *other* must be a :meth:`putzmeister.Masse` instance or 0.
  """
  if other == 0:
    result = self.copy()
    result.label = None
    return result
  elif not isinstance(other, type(self)):
    raise TypeError('Error: second operand is not a Masse instance')
  mass = self.masse + other.masse
  result = type(self)(masse=mass)
  try:   result.x = (self.x*self.masse + other.x*other.masse)/mass
  except AttributeError: pass
  try:   result.y = (self.y*self.masse + other.y*other.masse)/mass
  except AttributeError: pass
  try:   result.z = (self.z*self.masse + other.z*other.masse)/mass
  except AttributeError: pass
  result._set_categories( self, other, action='add')
  return result

Now it looks like this:

def __add__(self, other):
  """Overwrite operator "+": add the given masses and calculate the resulting center of
  gravity.
  """
  if other == 0:
    result = self.copy()
    result.label = None
    return result
  elif not isinstance(other, type(self)):
    raise TypeError('Error: second operand is not a Masse instance')
  mass = self.masse + other.masse
  result = type(self)(masse=mass)
  for a in ('x','y','z'):
    c1 = getattr(self, a)
    c2 = getattr(other,a)
    if c1 is None or c2 is None: setattr(result, a, None)
    else: setattr(result, a, (c1*self.masse + c2*other.masse)/mass )
  result._set_categories( self, other, action='add')
  return result

The None type would be alright as <unset state>. The Problem now is rather that 0 is a valid value for all coordinates, so i always have to check if attribute is not None rather than if attribute which would be i think as nicest as the code could get.

Yet my dream would be that in the assignment z = x + y my code would be able to first check if z exists and has the right type, and then if yes it sets the values of x and y that can be derived by the mathematic statement (for example for y: if z has some attributes and x has the same ones...) If z does not exists it is created and all settable values are set. Not quite sure if something like this can be done at all...

Again, thank you all for your answers.

Was it helpful?

Solution

Do they really not need to be there, or should they be there with a default value? Either way, it seems you have learned the better approach -- it is better if all instances of the same class have the same attributes.

Using None is a standard way of dealing with uninitialized names, but if None is possible as a valid value you can make your own:

class UnInit(object):
    """
    No value yet given to this attribute
    """

class Coordinate(object):
    x = UnInit
    y = UnInit
    z = UnInit
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top