【问题标题】:How to read the contents of a csv file into a class with each csv row as a class instance如何将 csv 文件的内容读入一个类,每个 csv 行作为一个类实例
【发布时间】:2018-05-06 20:01:36
【问题描述】:

我是一名 Python 新手,几天来一直在努力完成课堂作业。我有一个 csv 文件,其中包含如下数据:

id,latitude,longitude,city,label,yr1970,yr1975,yr1980,yr1985,yr1990,yr1995,yr2000,yr2005
1,35.6832085,139.8089447,Tokyo,Tokyo,23.3,26.61,28.55,30.3,32.53,33.59,34.45,35.62

此文件中有大约 40 行,每行包含与世界城市相关的数据。如您所见,第一行是标题。我应该在 Python 中创建一个类并将 csv 文件读入该类,其中每一行都成为该类的一个实例。然后我将类实例存储在一个列表中。我已经能够创建一个存储所有数据的实例,但我似乎无法为每一行创建一个实例(而且我显然不想手动创建)。

这是我目前所得到的:

import csv
Cities = []


with open('filepath','rb') as f:
cityList = csv.reader(f)
for row in cityList:
    if row != 'label':
        for row in cityList:
            citysName = row[3]


            class City:

                def __init__(self, cityName=row[3], Label=row[4], Lat=row[1],
                         Lon=row[2], yr1970=row[5], yr1975=row[6], yr1980=row[7],
                             yr1985=row[8], yr1990=row[9], yr1995=row[10], yr2000=row[11],
                             yr2005=row[12], yr2010=row[13]):
                    self.cityName = cityName
                    self.label = Label
                    self.lat = Lat
                    self.lon = Lon
                    self.yr1970 = yr1970
                    self.yr1975 = yr1975
                    self.yr1980 = yr1980
                    self.yr1985 = yr1985
                    self.yr1990 = yr1990
                    self.yr1995 = yr1995
                    self.yr2000 = yr2000
                    self.yr2005 = yr2005
                    self.yr2010 = yr2010

            citysName = City()

            Cities.append(citysName.cityName)
            Cities.append(citysName.label)
            Cities.append(citysName.lat)
            Cities.append(citysName.lon)
            Cities.append(citysName.yr1970)
            Cities.append(citysName.yr1975)
            Cities.append(citysName.yr1980)
            Cities.append(citysName.yr1985)
            Cities.append(citysName.yr1990)
            Cities.append(citysName.yr1995)
            Cities.append(citysName.yr2000)
            Cities.append(citysName.yr2005)
            Cities.append(citysName.yr2010)

        print Cities

再说一次,我对 Python 很陌生(和一般的编码),我意识到这段代码不好,但我很难找到将 csv 文件读入 Python 类的技巧。

【问题讨论】:

  • 您应该在循环之前声明一次类类型。然后创建实例,将行元素作为参数传递。
  • 您的数据不匹配。你有 9 年,只有 8 个数据值。

标签: python list class csv oop


【解决方案1】:

你可以试试这个:

import csv
class City:
   def __init__(self, row, header):
        self.__dict__ = dict(zip(header, row))

data = list(csv.reader(open('file.csv')))
instances = [City(i, data[0]) for i in data[1:]]

但是,由于您提到有很多行,您可能希望为每个城市创建一个 id,作为您在列表中的字符串表示形式:

import csv
class City:
   def __init__(self, row, header, the_id):
       self.__dict__ = dict(zip(header, row)) 
       self.the_id = the_id
   def __repr__(self):
       return self.the_id

data = list(csv.reader(open('file.csv')))
instances = [City(a, data[0], "city_{}".format(i+1)) for i, a in enumerate(data[1:])]

您的输出将是这样的列表:

[city_1, city_2, city_3...]

任何属性都可以这样调用:

instances[1].latitude

关于您最近的评论,要按城市名称访问城市属性,您可以稍微重组instances

instances = {a[3]:City(a, data[0], "city_{}".format(i+1)) for i, a in enumerate(data[1:])}

【讨论】:

  • 这似乎有效,谢谢!您知道是否有任何方法可以通过城市名称调用属性? (即,instances['Tokyo'].latitude)
  • 取出了 'City' 之前的 '[',效果很好,非常感谢!
【解决方案2】:

您可以使用csv.DictReader 来做到这一点,它将每一行作为字典返回。它还允许您根据 csv 文件的第一行或标题行中定义的字段名轻松定义类,而不是将它们硬编码到您的程序中。

您仍然可以使用“点”语法(例如 city.latitudecity.label 等)引用任何记录的字段。

以下是 Python 2 代码。对于 Python 3,使用以下命令打开文件:

with open('cities.csv', 'r', newline='') as f:

而不是显示的内容。

import csv

with open('cities.csv', 'rb') as f:
    reader = csv.DictReader(f)
    fieldnames = reader.fieldnames

    class City:
        def __init__(self, **fields):
            self.__dict__.update(**fields)

        def __repr__(self):  # Added to make printing instances show their contents.
            fields = ', '.join(('{}={!r}'.format(fieldname, getattr(self, fieldname))
                                   for fieldname in fieldnames))
            return('{}({})'.format(self.__class__.__name__, fields))

    Cities = [City(**row) for row in reader]

print(Cities)

示例输入文件的示例输出:

[City(id='1', latitude='35.6832085', longitude='139.8089447', city='Tokyo', label='Tokyo', yr1970='23.3', yr1975='26.61', yr1980='28.55', yr1985='30.3', yr1990='32.53', yr1995='33.59', yr2000='34.45', yr2005='35.62')]

【讨论】:

    【解决方案3】:

    如果您的数据只是不可变的记录,请使用namedtuple

    >>> from collections import namedtuple
    
    >>> City = namedtuple('City', 'lat lon cityName label '
    ...                   'yr1970 yr1975 yr1980 yr1985 yr1990 yr1995 yr2000 yr2005 yr2010')
    

    您可以对不需要第一个值的行进行切片,然后使用* 解包:

    >>> row = ['1', '35.6832085', '139.8089447', 'Tokyo', 'Tokyo',
    ...        '23.3', '26.61', '28.55', '30.3', '32.53', '33.59', '34.45', '35.62', '35.7']
    
    >>> city = City(*row[1:])
    
    >>> city
    City(lat='35.6832085', lon='139.8089447', cityName='Tokyo', label='Tokyo',
         yr1970='23.3', yr1975='26.61', yr1980='28.55', yr1985='30.3', yr1990='32.53',
         yr1995='33.59', yr2000='34.45', yr2005='35.62', yr2010='35.7')
    

    您只需要将这个对象添加到您的城市列表中,而不是每个属性:

    >>> cities.append(city)
    

    将它与过滤掉标签行的列表理解放在一起:

    import csv
    from collections import namedtuple
    
    City = namedtuple('City',
                      'lat lon cityName label '
                      'yr1970 yr1975 yr1980 yr1985 yr1990 yr1995 yr2000 yr2005 yr2010')
    
    with open('filepath') as f:
        cities = [City(*row[1:]) for row in csv.reader(f)
                  if row[0] != 'label']
    

    【讨论】:

      【解决方案4】:
      import csv
      
      
      class City:
          def __init__(self, **kwargs):
              self.id = kwargs.get('id')
              self.latitude = kwargs.get('latitude')
              self.longitude = kwargs.get('longitude')
              self.city = kwargs.get('city')
              self.label = kwargs.get('label')
              self.year_1970 = kwargs.get('yr1970')
              self.year_1975 = kwargs.get('yr1975')
              self.year_1980 = kwargs.get('yr1980')
              self.year_1985 = kwargs.get('yr1985')
              self.year_1990 = kwargs.get('yr1990')
              self.year_1995 = kwargs.get('yr1995')
              self.year_2000 = kwargs.get('yr2000')
              self.year_2005 = kwargs.get('yr2005')
      
          def __str__(self):
              return self.label
      
      
      if __name__ == '__main__':
          with open('filepath', 'r') as csvfile:
              reader = csv.DictReader(csvfile)
      
              for row in reader:
                  city = City(**row)
                  print(city)
      

      【讨论】:

        【解决方案5】:

        一些帮助你清理代码的技巧:

        1. 而不是这个:

          self.yr1970
          

          定义一个列表来跟踪年份及其值:

          tokyo_years = {
              1970: 23.3,
              1975: 26.61,
              # ...
          }
          

          现在将这个结构与每个城市配对:

          cities = [
              { 'city': 'Tokyo',     'years': tokyo_years },
              { 'city': 'Vancouver', 'years': vancouver_years },
              # ...
          ]
          
        2. 不要嵌套得那么深。此外,以下内容真的很奇怪:

          for row in cityList:
              if row != 'label':
                  for row in cityList:
          

          您正在循环某些东西,然后在循环时再次循环它......!

        3. 类属于顶级。这意味着class前面应该有0个空格。

                   class City:
          

          应该是:

          class City:
          

        我提到这一切的原因是因为试图用混乱的代码做任何进一步的事情只会导致代码更混乱。 :) 尝试通过以下方式改进您当前的代码:

        1. 使用数据结构(列表、字典)。
        2. 将嵌套代码的级别限制为最多 2 个。 (考虑使用函数来帮助您。)
        3. 将课程置于顶层。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-09-13
          • 2019-07-08
          • 1970-01-01
          • 2023-01-10
          • 2018-07-18
          • 1970-01-01
          • 2017-04-24
          相关资源
          最近更新 更多