I am working on an assignment where I create "instances" of cities using rows in a .csv, then use these instances in methods to calculate distance and population change. Creating the instances works fine (using steps 1-4 below), until I try to call printDistance:
##Step 1. Open and read CityPop.csv
with open('CityPop.csv', 'r', newline='') as f:
try:
reader = csv.DictReader(f)
##Step 2. Create "City" class
class City:
##Step 3. Use _init method to assign attribute values
def __init__(self, row, header):
self.__dict__ = dict(zip(header, row))
##Step 4. Create "Cities" list
data = list(csv.reader(open('CityPop.csv')))
instances = [City(i, data[0]) for i in data[1:]]
##Step 5. Create printDistance method within "Cities" class
def printDistance(self, othercity, instances):
dist=math.acos((math.sin(math.radians(self.lat)))*(math.sin(math.radians(othercity.lat)))+(math.cos(math.radians(self.lat)))*(math.cos(math.radians(othercity.lat)))*(math.cos(math.radians(self.lon-othercity.lon)))) * 6300 (self.lat, self.lon, othercity.lat, othercity.lon)
When I enter instances[0].printDistance(instances1) in the shell, I get the error:
`NameError: name 'instances' is not defined`
Is this an indentation problem? Should I be calling the function from within the code, not the shell?
This is not so much an indentation problem, but more of a general code structure problem. You're nesting a lot:
printDistance
__init__
City
try
blockwith
blockI think this is what you are trying to do:
try
and thewith
)The reason your
instances
isn't working is because, unlike you think, it's probably not being created correctly, or at least not in the correct context. And it certainly won't be available to you on the CLI due to all of the nesting.There's a number of blatant bugs in your code:
(self.lat, self.lon, othercity.lat, othercity.lon)
at the end of the last line?reader
.csv
as object attributes, but are misspelling their use (lat
instead oflatitude
andlon
instead oflongitude
)It looks a bit like a lot of code found in various places got pasted together into one clump - this is what it looks like when cleaned up:
Note how
print_distance
is now a method ofCity
, which is called on each instance ofCity
incities
(which is what I renamedinstances
to).Now, if you are really trying, this makes more sense:
Note the cleaned up computation, the catching of an error that could be expected to occur (with the
with
insides thetry
block) and a proper constructor that assigns what it needs with the correct type, while the reader decides which fields go where.Finally, as a bonus: nobody should be writing distance calculations like this. Plenty libraries exist that do a much better job of this, like GeoPy. All you need to do is
pip install geopy
to get it and then you can use this:Note that I moved the
print
out of the method as well, since it makes more sense to calculate in the object and print outside it. The nice thing about all this is that the calculation now uses a proper geodesic (WGS-84) to do the calculation and the odds of math errors are drastically reduced. If you must use a simple sphere, the library has functions for that as well.Nested functions must not contain self as parameter because they are not member functions. Class cannot pass instance variables to them. You are infact passing the same self from parent to child function.
Also you must not nest constructor, this is only for initiation purpose. Create a separate method indeed.
And try creating instance variable inside the constructor, and that is what init for !
Also create seperate function for instantiation