Методы Python из csv

#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`
  

Это проблема с отступом? Должен ли я вызывать функцию из кода, а не из оболочки?

Пример того, как данные хранятся в csv

Комментарии:

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. Да, это то, к чему я стремлюсь. Я добавил пример данных в исходное сообщение. Я также попробую переработать экземпляры. Спасибо!