0

Here's the code I have

class Human(object):
  def __init__(self, name, gender):
    self.name = name
    self.gender = gender
    print 'Hi there, I am '+ self.name

  def ThankHeavens(self):
    return 'Thanks Gods!'

class Soldier(Human):
  def __init__(self, name, gender, rank):
    super(Soldier, self).__init__(name, gender)
    self.rank = rank
    print self.rank + ' reporting!'

class Officer(Soldier):
  def __init__(self, name, gender, rank, num_subordinates):
    super(Officer, self).__init__(name, gender, rank)
    self.num_subordinates = num_subordinates

  def __setattr__(self, rank, value):
    if rank not in ['lieutenant', 'captain', 'major', 'colonel', 'commander', 'admiral']:
      print 'Invalid officer rank'
    else:
      super(Officer, self).__setattr__(rank, value)

It breaks whenever I try to create an officer:

helo = Officer(name='Agathon',
               gender='m',
               rank='lieutenant',
               num_subordinates=0)

Traceback (most recent call last):
  File "hw7.py", line 39, in <module>
    num_subordinates=0)
  File "hw7.py", line 20, in __init__
    super(Officer, self).__init__(name, gender, rank)
  File "hw7.py", line 14, in __init__
    super(Soldier, self).__init__(name, gender)
  File "hw7.py", line 7, in __init__
    print 'Hi there, I am '+ self.name
AttributeError: 'Officer' object has no attribute 'name'

Why doesn't it recognize the name I've entered when defining helo?

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
andhudhow
  • 21
  • 1
  • 3

2 Answers2

5

What you haven't posted is the two messages created by your __setattr__:

Invalid officer rank
Invalid officer rank

These come from following lines:

self.name = name
self.gender = gender

Since these fail, you cannot retrieve the name later:

 print 'Hi there, I am '+ self.name

Fix:

def __setattr__(self, key, value):
    if key == "rank" and value not in [.....]
Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
0

Your __setattr__ is called when any attribute of the Officer is set at any time in any way, including during initialization via the superclass __init__.

When __setattr__ is called, self will be the Officer instance, rank will be the name of the attribute being set, and value will be the value desired for that attributed.

What you have done is make the Officer class such that it can have a .lieutenant etc. for values in your list, but not a .rank (or .name or .gender) - and you have not actually restricted the value of that attribute at all.

This is deeper magic than you were looking for; you clearly wanted to restrict the values used when setting the .rank attribute specifically. To do that, use a property.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153