#python #csv #methods #instance
#python #csv #методы #экземпляр
Вопрос:
Я работаю над заданием, в котором я создаю «экземпляры» городов, используя строки в .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)
Когда я ввожу экземпляры [0].printDistance (экземпляры1) в командной строке, я получаю сообщение об ошибке:
`NameError: name 'instances' is not defined`
Это проблема с отступом? Должен ли я вызывать функцию из кода, а не из оболочки?
Комментарии:
1.
def printDistance
Действительно ли они вложены внутрьdef __init__
?2. Определения классов и функций обычно не должны находиться внутри
with
илиtry
.3. Не должны
try
иметь отступов, чтобы они были внутриwith
?4. Ваш отступ настолько запутан, что невозможно сказать, что вы на самом деле пытаетесь сделать. То, как вы это написали,
instances
является локальной переменной внутри__init__
метода. Но это не имеет смысла, потому что это вызываетCity()
, что вызовет бесконечную рекурсию.5.
printDistance()
принимает 2 аргумента, вы вызываете его только с одним аргументом. Но он никогда не используетinstances
аргумент, так почему же этот параметр там?
Ответ №1:
Вложенные функции не должны содержать self в качестве параметра, поскольку они не являются функциями-членами. Класс не может передавать им переменные экземпляра. Фактически вы передаете один и тот же self из родительской в дочернюю функцию.
Также вы не должны вкладывать конструктор, это только для целей инициализации. Действительно, создайте отдельный метод.
И попробуйте создать переменную экземпляра внутри конструктора, для этого и нужен init !
self.instances = [self.getInstance(i, data[0]) for i in data[1:]]
Также создайте отдельную функцию для создания экземпляра
@classmethod
def getInstance(cls,d1,d2):
return cls(d1,d2)
Комментарии:
1. если возможно, пожалуйста, используйте pandas и считывайте данные в dataframe и применяйте вычисления, это было бы намного быстрее и потребовало бы меньше строк кода. Также вы определяете для csv значение n, а не ‘ ‘
Ответ №2:
Это не столько проблема с отступами, сколько скорее общая проблема структуры кода. Вы слишком много вкладываете:
- Вся фактическая работа над невероятно длинной строкой (с ошибками)
- Внутри функции (правильно)
printDistance
- Внутри конструктора
__init__
- Внутри определения класса (правильно)
City
- Внутри
try
блока - Внутри
with
блока
Я думаю, это то, что вы пытаетесь сделать:
- создайте класс City, который может сам выводить расстояние до других городов
- сгенерируйте список этих городских объектов из .csv, который каким-то образом содержит как расстояния, так и население (вероятно, вам следует предоставить пример данных)
- делайте это отказоустойчивым и чистым способом (отсюда
try
иwith
)
Причина, по которой ваш instances
не работает, заключается в том, что, в отличие от того, что вы думаете, он, вероятно, создается неправильно или, по крайней мере, не в правильном контексте. И это, конечно, не будет доступно для вас в командной строке из-за всей вложенности.
В вашем коде есть ряд вопиющих ошибок:
- Что
(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
теперь работает метод 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
блока) и надлежащим конструктором, который присваивает то, что ему нужно, с правильным типом, в то время как читатель решает, какие поля куда идут.
Наконец, в качестве бонуса: никто не должен писать вычисления расстояния, подобные этому. Существует множество библиотек, которые намного лучше справляются с этой задачей, например 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) для выполнения вычислений, и вероятность математических ошибок резко снижается. Если вам необходимо использовать простую сферу, в библиотеке также есть функции для этого.
Комментарии:
1. Да, это то, к чему я стремлюсь. Я добавил пример данных в исходное сообщение. Я также попробую переработать экземпляры. Спасибо!