python - 来自 csv 的 Python 方法
问题描述
我正在做一个任务,我使用 .csv 中的行创建城市的“实例”,然后在方法中使用这些实例来计算距离和人口变化。创建实例工作正常(使用下面的步骤 1-4),直到我尝试调用 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)
当我在 shell 中输入 instances[0].printDistance(instances 1 ) 时,出现错误:
`NameError: name 'instances' is not defined`
这是缩进问题吗?我应该从代码中调用函数,而不是 shell?
解决方案
这与其说是缩进问题,不如说是一个通用的代码结构问题。你嵌套了很多:
- 所有实际工作都在一条令人难以置信的长线上(有错误)
- 函数内部(正确)
printDistance
- 构造函数内部
__init__
- 在类定义内部(正确)
City
try
块内with
块内
我认为这就是你想要做的:
- 创建一个City类,可以打印自己到其他城市的距离
- 从以某种方式同时具有距离和人口的 .csv 生成这些 City 对象的列表(您可能应该提供数据示例)
- 以容错和干净的方式这样做(因此 the
try
和 thewith
)
您instances
不工作的原因是,与您想象的不同,它可能没有正确创建,或者至少没有在正确的上下文中创建。由于所有的嵌套,它肯定不会在 CLI 上提供给您。
您的代码中有许多明显的错误:
(self.lat, self.lon, othercity.lat, othercity.lon)
最后一行的末尾是什么?- 你为什么要打开文件阅读两次?你甚至没有使用第一个
reader
- 您直截了当地将列标题从 a
.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
现在是一个方法,它在 inCity
的每个实例上调用(这是我重命名的)。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
)以及适当的构造函数,该构造函数使用正确的类型分配所需的内容,而读者决定哪些字段去哪里。
最后,作为奖励:没有人应该编写这样的距离计算。有很多库在这方面做得更好,比如 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) 进行计算,并且数学错误的几率大大降低。如果您必须使用一个简单的球体,该库也有相应的功能。
推荐阅读
- azure - 有没有办法知道文件是否已上传到 Azure 中的网络驱动器
- c++ - c ++包含标头失败
- c++ - Char 表示来自 int 的十六进制数,前缀为 '0x0'
- c# - ArgumentNullException:值不能为空,但它不是
- java - 如何在 Java 中使用 androidx.lifecycle.ViewModelProvider
- java - 使用占位符 jpa 命名查询将列名作为参数传递,返回不正确的数据
- winforms - 在 Winforms ReportViewer 中为当前页码添加偏移量
- vue.js - 更改 Axios 默认参数在 Vuex 操作事件上未更改
- node.js - 如何在 Heroku 上设置环境变量?
- html - 突出显示垫行单元格的全宽