Delicious
Delicious

Reputation: 967

Python - Inherit an Already Initialized Class Without Calling Super Constructor

I'm trying to create a Python gazetteer module. I have the following classes: State, County, and City. I need a State to know all of its counties, a County to know all of its cities, and a City to know its County & State. (Python 3.x)

Here is what I have:

class State(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def state_name(self):
        return self.state


class County(State):
    def __init__(self, state_class, **kwargs):
        self.__bases__ = (state_class, )
        self.__dict__.update(kwargs)


class City(County):
    def __init__(self, county_class, **kwargs):
        self.__bases__ = (county_class, )
        self.__dict__.update(kwargs)

Then I do:

fl = State(state='Florida', abb='FL', fips='12')
mdc = County(fl, county='Miami-Dade', fips='086')
setattr(fl, 'miami_dade', mdc)
rmd = City(mdc, city='Richmond', fips='60230')
setattr(mdc, 'richmond', rmd)


print(fl.state, fl.abb, fl.fips, fl.miami_dade.county, fl.miami_dade.richmond.city)
# Florida FL 12 Miami-Dade Richmond
print(fl.state_name())
# Florida
print(fl.__dict__)
# {'abb': 'FL', 'miami_dade': <__main__.County object at 0x0000000002C44A20>, 'fips': '12', 'state': 'Florida'}
print(fl.miami_dade.__dict__)
# {'__bases__': (<__main__.State object at 0x000000000358A048>,), 'fips': '086', 'county': 'Miami-Dade', 'richmond': <__main__.City object at 0x0000000002C44A90>}
print(fl.miami_dade.richmond.__dict__)
# {'fips': '60230', 'city': 'Richmond', '__bases__': (<__main__.County object at 0x0000000002C44A20>,)}
print(isinstance(fl.miami_dade.richmond, State))
# True

All good so far... but then:

print(fl.miami_dade.richmond.state_name())
# AttributeError: 'City' object has no attribute 'state'

I want my City class to use methods in the State and County classes. My best guess is that the code print(fl.miami_dade.richmond.state_name()) is executing the state_name() method using the attributes in the City instance and not the attributes in the State instance.

I don't understand what I'm doing wrong here. Is it even possible for a County class to inherit the methods and attributes of a already initialized State class?


More info:
I don't want to do super().__init__() in City and County e.g. a = City('Seattle', 'King', 'Washington') because of the following.
I have data with 56 states (includes US territories), 3,234 counties, and 163,252 cities. It's a waste of memory if I have every City have a copy of its County and State. Also, I have coordinates that form a polygon for every county (~20MB) the average county has ~50 cities; that's over a Gig if every city has a copy.


Ok, this is not a good structure for what I'm doing and I'm changing it but I don't like it when I don't understand something. I want to understand what's going on here.
Here is a simplified version:

class A:
    def __init__(self, name):
        self.name = name

    def getA(self):
        return self.name

class B(A):
    def __init__(self, aname, bname):
        self.name = bname
        super().__init__(aname)

b = B('AAA', 'BBB')
print(b.getA())
# AAA

Ok, I get this just fine. But then here:

class A2:
    def __init__(self, name):
        self.name = name

    def getA2(self):
        return self.name

class B2(A2):
    def __init__(self, A2Obj, name):
        self.__bases__ = (A2Obj, )
        self.name = name

a2 = A2('AAA2')
b2 = B2(a2, 'BBB2')
print(b2.getA2())
# BBB2
print(a2.name)
# AAA2

a2 knows its name is AAA2. Why is b2.getA2() giving me BBB2? It's like b2 inherited the methods of a2 but not the attributes. How do I make b2 behave like the example above?

Upvotes: 2

Views: 1359

Answers (1)

Bruce
Bruce

Reputation: 7132

You likely use the wrong structure : your city is not a county, your county is not a state. You should use composition instead of derivation

class State(object):
  def __init__(self, **kwargs):
    self.__dict__.update(kwargs)

  def state_name(self):
    return self.state


class County(object):
  def __init__(self, state, **kwargs):
    self.state=state # this will just reference the state object
    self.__dict__.update(kwargs)
  def getStateName(self):
    return self.state.state


class City(object):
  def __init__(self, county, **kwargs):
    self.county = county
    self.__dict__.update(kwargs)
  def getStateName(self):
    return self.county.getStateName()

If you want to be able to retrieve them later without keeping track of the objects, you can use a static dictionary and add created object during init (eg "FL" -> State("Florida")). Then add a static method to retrieve the correct object.

Upvotes: 4

Related Questions