python: Как прочитать файл и сохранить каждую строку, используя функцию карты? - PullRequest
0 голосов
/ 26 февраля 2020

Я пытаюсь восстановить программу, которую я написал, но избавляюсь от циклов. Исходный код читает файл с тысячами строк, которые структурированы как: Пример. 2 строки файла:

enter image description here

Как видите, первая строка начинается с LPPD; LEMD, а вторая начинается с DAAE; LFML. Меня интересует только самый первый и второй элемент каждой строки.

Исходный код, который я написал:

# Libraries
import sys
from collections import Counter
import collections
from itertools import chain
from collections import defaultdict
import time

# START

# @time=0 
start = time.time()

# Defining default program argument
if len(sys.argv)==1:
    fileName = "file.txt"
else:
    fileName = sys.argv[1]

takeOffAirport = []
landingAirport = []

# Reading file
lines = 0 # Counter for file lines
try:
    with open(fileName) as file:
        for line in file:
            words = line.split(';')
            # Relevant data, item1 and item2 from each file line
            origin = words[0]
            destination = words[1]
            # Populating lists
            landingAirport.append(destination)
            takeOffAirport.append(origin)
            lines += 1
except IOError:
    print ("\n\033[0;31mIoError: could not open the file:\033[00m %s" %fileName)

airports_dict = defaultdict(list)

# Merge lists into a dictionary key:value
for key, value in chain(Counter(takeOffAirport).items(), 
                    Counter(landingAirport).items()):
    # 'AIRPOT_NAME':[num_takeOffs, num_landings]
    airports_dict[key].append(value) 
# Sum key values and add it as another value
for key, value in airports_dict.items():
    #'AIRPOT_NAME':[num_totalMovements, num_takeOffs, num_landings]
    airports_dict[key] = [sum(value),value]  

# Sort dictionary by the top 10  total movements
airports_dict = sorted(airports_dict.items(), 
                        key=lambda kv:kv[1], reverse=True)[:10]
airports_dict = collections.OrderedDict(airports_dict)

# Print results 
print("\nAIRPORT"+ "\t\t#TOTAL_MOVEMENTS"+ "\t#TAKEOFFS"+ "\t#LANDINGS")
for k in airports_dict:
    print(k,"\t\t", airports_dict[k][0],
      "\t\t\t", airports_dict[k][1][1],
      "\t\t", airports_dict[k][1][0])


# @time=1
end = time.time()- start
print("\nAlgorithm execution time: %0.5f" % end)
print("Total number of lines read in the file: %u\n" % lines)
airports_dict.clear
takeOffAirport.clear
landingAirport.clear

Моя цель состоит в том, чтобы упростить программу, используя карту, уменьшить и фильтр. До сих пор я отсортировал создание двух независимых списков, по одному для каждого первого элемента каждой строки файла, и еще один список со вторым элементом каждой строки файла, используя:

# Creates two independent lists with the first and second element from each line
takeOff_Airport = list(map(lambda sub: (sub[0].split(';')[0]), lines))
landing_Airport = list(map(lambda sub: (sub[0].split(';')[1]), lines))

Я надеялся найти способ открыть файл и получить тот же результат, что и в исходном коде, благодаря возможности открывать файл через функцию map (), поэтому я мог передавать каждый список определенным выше картам; takeOff_Airport и посадка_Airport.

Ответы [ 2 ]

0 голосов
/ 27 февраля 2020

Итак, во-первых, вызывать split('\n') в каждой строке глупо; строка гарантированно содержит не более одного символа новой строки в конце и ничего после него, так что вы получите кучу ['all of line', ''] list с. Чтобы избежать пустой строки, просто strip перевод строки. Это не приведет к тому, что каждая строка будет заключена в list, но, честно говоря, я не могу представить, почему вы хотите a list из одного элемента list s, содержащего по одной строке каждый .

Итак, я собираюсь продемонстрировать, как map + strip, чтобы избавиться от перевода строки, , используя operator.methodcaller, чтобы выполнить strip в каждой строке:

from operator import methodcaller

def readFile(fileName):
    try:
        with open(fileName) as file:
            return list(map(methodcaller('strip', '\n'), file))
    except IOError:
      print ("\n\033[0;31mIoError: could not open the file:\033[00m %s" %fileName)

К сожалению, поскольку ваш файл управляется контекстом (что хорошо, здесь неудобно), вам нужно list если результат; map ленив, и если вы не сделали list ify до return, оператор with закроет файл, а извлечение данных из объекта map приведет к d ie с исключением.

Чтобы обойти это, вы можете реализовать ее как тривиальную функцию генератора, поэтому контекст генератора сохраняет файл открытым до тех пор, пока генератор не будет исчерпан (или явно close d, или сборщик мусора):

def readFile(fileName):
    try:
        with open(fileName) as file:
            yield from map(methodcaller('strip', '\n'), file)
    except IOError:
      print ("\n\033[0;31mIoError: could not open the file:\033[00m %s" %fileName)

yield from приведет к небольшому количеству накладных расходов по прямой итерации map, но не намного, и теперь вам не нужно отбрасывать весь файл, если вы этого не хотите; вызывающая сторона может просто повторить результат и получить разделенную строку на каждой итерации, не вытягивая весь файл в память. У него есть небольшая слабость в том, что открытие файла будет выполняться лениво, поэтому вы не увидите исключения (если оно есть), пока не начнете выполнять итерации. Это можно обойти, но это не стоит проблем, если вам это действительно не нужно.

Я бы обычно рекомендовал последнюю реализацию, поскольку она обеспечивает гибкость вызывающей стороны. В любом случае, если они хотят получить list, они просто упаковывают вызов в list и получают результат list (с небольшим количеством накладных расходов). Если они этого не сделают, они могут начать обработку быстрее и иметь гораздо меньшие требования к памяти.

Имейте в виду, вся эта функция довольно странная; замена IOError s на print s и (неявно) возврат None враждебны для потребителей API (теперь они должны проверять возвращаемые значения и фактически не могут сказать, что пошло не так). В реальном коде я бы, вероятно, просто пропустил функцию и вставил:

with open(fileName) as file:
    for line in map(methodcaller('strip', '\n'), file)):
        # do stuff with line (with newline pre-stripped)

inline в вызывающей стороне; возможно определить split_by_newline = methodcaller('split', '\n') глобально, чтобы использовать более дружественное имя. Это не , что много кода, и я не могу себе представить, что это специфическое c поведение необходимо во многих независимых частях вашего файла, и его встраивание устраняет проблемы, связанные с открытием и закрытием файла .

0 голосов
/ 26 февраля 2020

Таким образом, если у нас есть файл как таковой

line 1
line 2
line 3
line 4

, и мы делаем это так

open(file_name).read().split('\n')

, мы получаем это

['line 1', 'line 2', 'line 3', 'line 4', '']

Это то, что вы в розыске?

Редактировать 1

Я чувствую, что это несколько избыточно, но так как map применяет функцию к каждому элементу итератора, нам нужно иметь имя нашего файла в списке, и мы, конечно, определяем нашу функцию

def open_read(file_name):
    return open(file_name).read().split('\n')

print(list(map(open_read, ['test.txt'])))

Это дает нам

>>> [['line 1', 'line 2', 'line 3', 'line 4', '']]
...