Это не столько проблема отступа, сколько проблема общей структуры кода.Вы много вложили:
- Вся фактическая работа на невероятно длинной строке (с ошибками)
- Внутри функции (правильно)
printDistance
- Внутри конструктора
__init__
- Внутри определения класса (правильно)
City
- Внутри блока
try
- Внутри блока
with
Я думаю, это то, что вы пытаетесь сделать:
- создать класс City, который может выводить расстояние до других городов
- создать список этих объектов City из .csv, который каким-то образом имеет как расстояния, так и население (вам, вероятно, следует привести пример данных)
- сделать это отказоустойчивым и чистым способом (следовательно,
try
и with
)
Причина, по которой ваш instances
не работает, заключается в том, что, в отличие от вашего взгляда, он, вероятно, создается неправильно или, по крайней мере, не в правильном контексте.И, конечно, он не будет доступен вам в CLI из-за всей вложенности.
В вашем коде есть несколько явных ошибок:
- Что такое
(self.lat, self.lon, othercity.lat, othercity.lon)
в конце последней строки? - Почему вы открываете файл для чтения дважды?Вы даже не используете первые
reader
- . Вы прямо назначаете заголовки столбцов из
.csv
в качестве атрибутов объекта, но неправильно используете их (lat
вместо latitude
и lon
вместо longitude
)
Это похоже на то, как много кода, найденного в разных местах, склеено в один комок - вот как это выглядит при очистке:
import csv
import math
class City:
def print_distance(self, other_city):
print(f'{self.city} to {other_city.city}')
# what a mess...
print(math.acos(
(math.sin(math.radians(float(self.latitude)))) * (math.sin(math.radians(float(other_city.latitude)))) + (
math.cos(math.radians(float(self.latitude)))) * (math.cos(math.radians(float(other_city.latitude)))) * (
math.cos(math.radians(float(self.longitude) - float(other_city.longitude))))) * 6300)
def __init__(self, values, attribute_names):
# this is *nasty* - much better to add the attributes explicitly, but left as original
# also, note that you're reading strings and floats here, but they are all stored as str
self.__dict__ = dict(zip(attribute_names, values))
with open('CityPop.csv', 'r', newline='') as f:
try:
reader = csv.reader(f)
header = next(reader)
cities = [City(row, header) for row in reader]
for city_1 in cities:
for city_2 in cities:
city_1.print_distance(city_2)
except Exception as e:
print(f'Apparently were doing something with this error: {e}')
Обратите внимание, что print_distance
теперь является методом City
, который вызывается в каждом экземпляре City
в cities
(это то, к чему я переименовал instances
).
Теперь, если вы действительно пытаетесь, это имеет больше смысла:
import csv
import math
class City:
def print_distance(self, other_city):
print(f'{self.name} to {other_city.name}')
# not a lot better, but some at least
print(
math.acos(
math.sin(math.radians(self.lat)) *
math.sin(math.radians(other_city.lat))
+
math.cos(math.radians(self.lat)) *
math.cos(math.radians(other_city.lat)) *
math.cos(math.radians(self.lon - other_city.lon))
) * 6300
)
def __init__(self, lat, lon, name):
self.lat = float(lat)
self.lon = float(lon)
self.name = str(name)
try:
with open('CityPop.csv', 'r', newline='') as f:
reader = csv.reader(f)
header = next(reader)
cities = [City(lat=row[1], lon=row[2], name=row[4]) for row in reader]
for city_1 in cities:
for city_2 in cities:
city_1.print_distance(city_2)
except FileNotFoundError:
print(f'Could not find the input file.')
Обратите внимание на очищенные вычисления, обнаружение ошибки, которая может произойти (с with
внутри try
block) и правильный конструктор, который назначает то, что ему нужно, с правильным типом, в то время как читатель решает, куда и куда идти.
Наконец, в качестве бонуса: никто не должен писать вычисления расстояний, подобные этой.Существует множество библиотек, которые справляются с этой задачей гораздо лучше, например GeoPy.Все, что вам нужно сделать, это pip install geopy
, чтобы получить его, и затем вы можете использовать это:
import csv
import geopy.distance
class City:
def calc_distance(self, other_city):
return geopy.distance.geodesic(
(self.lat, self.lon),
(other_city.lat, other_city.lon)
).km
def __init__(self, lat, lon, name):
self.lat = float(lat)
self.lon = float(lon)
self.name = str(name)
try:
with open('CityPop.csv', 'r', newline='') as f:
reader = csv.reader(f)
header = next(reader)
cities = [City(lat=row[1], lon=row[2], name=row[4]) for row in reader]
for city_1 in cities:
for city_2 in cities:
print(city_1.calc_distance(city_2))
except FileNotFoundError:
print(f'Could not find the input file.')
Обратите внимание, что я также убрал print
из метода, так как имеет смысл вычислятьв объекте и распечатайте снаружи.Приятная вещь во всем этом заключается в том, что в вычислениях теперь используется правильная геодезическая (WGS-84) для выполнения вычислений, и вероятность математических ошибок значительно сокращается.Если вы должны использовать простую сферу, в библиотеке также есть функции для этого.