Предположим, у меня есть список имен файлов: [exia.gundam, dynames.gundam, kyrios.gundam, virtue.gundam]
или [exia.frame, exia.head, exia.swords, exia.legs, exia.arms, exia.pilot, exia.gn_drive, lockon_stratos.data, tieria_erde.data, ribbons_almark.data, otherstuff.dada]
.
На одной итерации я хотел бы иметь все файлы *.gundam или *.data, тогда как с другой стороны я хотел бы сгруппировать файлы exia. *. Какой самый простой способ сделать это, помимо повторения списка и размещения каждого элемента в словаре?
Вот что я имел в виду:
def matching_names(files):
'''
extracts files with repeated names from a list
Keyword arguments:
files - list of filenames
Returns: Dictionary
'''
nameDict = {}
for file in files:
filename = file.partition('.')
if filename[0] not in nameDict:
nameDict[filename[0]] = []
nameDict[filename[0]].append(filename[2])
matchingDict = {}
for key in nameDict.keys():
if len(nameDict[key]) > 1:
matchingDict[key] = nameDict[key]
return matchingDict
Ну, полагая, что я должен использовать это, есть ли простой способ его инвертировать и иметь расширение файла как ключ вместо имени?
В моей первой версии, похоже, я неверно истолковал ваш вопрос. Поэтому, если у меня это правильно, вы пытаетесь обработать список файлов, чтобы вы могли легко получить доступ ко всем именам файлов с заданным расширением или ко всем именам файлов с определенной базой ( "базой", являющейся частью до период)?
Если в этом случае я бы рекомендовал этот способ:
from itertools import groupby
def group_by_name(filenames):
'''Puts the filenames in the given iterable into a dictionary where
the key is the first component of the filename and the value is
a list of the filenames with that component.'''
keyfunc = lambda f: f.split('.', 1)[0]
return dict( (k, list(g)) for k,g in groupby(
sorted(filenames, key=keyfunc), key=keyfunc
) )
Например, учитывая список
>>> test_data = [
... exia.frame, exia.head, exia.swords, exia.legs,
... exia.arms, exia.pilot, exia.gn_drive, lockon_stratos.data,
... tieria_erde.data, ribbons_almark.data, otherstuff.dada
... ]
эта функция создаст
>>> group_by_name(test_data)
{'exia': ['exia.arms', 'exia.frame', 'exia.gn_drive', 'exia.head',
'exia.legs', 'exia.pilot', 'exia.swords'],
'lockon_stratos': ['lockon_stratos.data'],
'otherstuff': ['otherstuff.dada'],
'ribbons_almark': ['ribbons_almark.data'],
'tieria_erde': ['tieria_erde.data']}
Если вы хотите индексировать имена файлов по расширению, небольшая модификация сделает это для вас:
def group_by_extension(filenames):
'''Puts the filenames in the given iterable into a dictionary where
the key is the last component of the filename and the value is
a list of the filenames with that extension.'''
keyfunc = lambda f: f.split('.', 1)[1]
return dict( (k, list(g)) for k,g in groupby(
sorted(filenames, key=keyfunc), key=keyfunc
) )
Единственное различие заключается в строке keyfunc = ...
, где я изменил ключ от 0 до 1. Пример:
>>> group_by_extension(test_data)
{'arms': ['exia.arms'],
'dada': ['otherstuff.dada'],
'data': ['lockon_stratos.data', 'ribbons_almark.data', 'tieria_erde.data'],
'frame': ['exia.frame'],
'gn_drive': ['exia.gn_drive'],
'head': ['exia.head'],
'legs': ['exia.legs'],
'pilot': ['exia.pilot'],
'swords': ['exia.swords']}
Если вы хотите получить обе эти группы одновременно, я думаю, что было бы лучше избежать понимания списка, потому что это может обрабатывать их так или иначе, он не может построить два разных словаря сразу.
from collections import defaultdict
def group_by_both(filenames):
'''Puts the filenames in the given iterable into two dictionaries,
where in the first, the key is the first component of the filename,
and in the second, the key is the last component of the filename.
The values in each dictionary are lists of the filenames with that
base or extension.'''
by_name = defaultdict(list)
by_ext = defaultdict(list)
for f in filenames:
name, ext = f.split('.', 1)
by_name[name] += [f]
by_ext[ext] += [f]
return by_name, by_ext
Предположим, например, что в результате вы хотите получить список списков имен файлов, сгруппированных по расширению или корневому имени:
import os.path
import itertools as it
def files_grouped_by(filenames, use_extension=True):
def ky(fn): return os.path.splitext(fn)[use_extension]
return [list(g) for _, g in it.groupby(sorted(filenames, key=ky), ky)]
Теперь files_grouped_by(filenames, False)
вернет список группировок списков по имени root, а если второй аргумент True или отсутствует, группировка будет выполняться по расширению.
Если вы хотите вместо этого dict, ключи являются либо корневыми именами или расширениями, либо значениями соответствующих списков имен файлов, подход очень похож:
import os.path
import itertools as it
def dict_files_grouped_by(filenames, use_extension=True):
def ky(fn): return os.path.splitext(fn)[use_extension]
return dict((k, list(g))
for k, g in it.groupby(sorted(filenames, key=ky), ky)]
Я не уверен, что полностью получаю то, что вы хотите сделать, но если я правильно понял, что-то вроде этого может работать:
from collections import defaultdict
files_by_extension = defaultdict(list)
for f in files:
files_by_extension[ f.split('.')[1] ].append(f)
Это создает хеш-ключ с расширением файла и заполняет его, итерируя через список за один проход.
startswith
иendswith
строки вместо регулярных выражений, что может сэкономить немного времени на вычисления, но код будет длиннее (но я мог бы отредактировать это кстати тоже, если хотите).