这与其说是缩进问题,倒不如说是一般的代码结构问题。你嵌套了很多:
- 所有实际工作都在一条令人难以置信的长线上(有错误)
- 函数内部(正确)
printDistance
- 在构造函数内部
__init__
- 类定义内部(正确)
City
-
try 块内部
-
with 块内部
我认为这就是你想要做的:
- 创建一个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 的一个方法,它在cities 中的每个City 实例上调用(我将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 块内)和正确的构造函数,它使用正确的类型分配所需的内容,而读者决定哪些字段去哪里。
最后,作为奖励:没有人应该编写这样的距离计算。有很多库在这方面做得更好,比如 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) 进行计算,并且数学错误的几率大大降低。如果您必须使用简单的球体,那么库也有相应的功能。