Python отображает файловую систему в структуру каталогов: работает, но как?

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

То, что я пытаюсь сделать, это оставить пустые папки

import os
def get_directory_structure(rootdir):
 """
 Creates a nested dictionary that represents the folder structure of rootdir
 """
 dir = {}
 rootdir = rootdir.rstrip(os.sep)
 start = rootdir.rfind(os.sep) + 1
 for path, dirs, files in os.walk(rootdir):
 folders = path[start:].split(os.sep)
 subdir = dict.fromkeys(files)
 parent = reduce(dict.get, folders[:-1], dir)
 parent[folders[-1]] = subdir
 return dir

dir устанавливается на то же значение, что и родительское по строке:

parent[folders[-1]] = subdir

Как получилось?

dir изменен и принят как вход в строке reduce, но он не установлен там, а в следующей строке.

Любая идея?

Я хочу иметь возможность оставить пустые папки и лучше найти элегантный способ сделать это; Должен ли я сдаться и пропустить через дикт в качестве второго прохода?

[Edit after resolved], как указал Ханс и Адрин, сокращение фактически делает родительскую точку для dir, поэтому они являются одним и тем же объектом, и любое обновление родительских обновлений dir.

В итоге я сохранил тот же код, но переименовал vars для ясности:

dirtoken_dict folderspath_as_list subdirfiles_in_dir parentfull_dir (и я возвращаю full_dir)

Больше набирать текст, но в следующий раз, когда я посмотрю, я сразу же пойду.

2 ответа

Вы передаете dir в функцию reduce. Значит, вы передаете указатель на объект функции, и функция может его изменить.

Посмотрите на реализацию функции reduce здесь. И обратите внимание на строку:

accum_value = function(accum_value, x)

В этот момент accum_value указывает на то же место, что и initializer, которое является вашим dir, и передается функции, которая в вашем случае dict.get.


Немного об уменьшении словаря для любого, кто мало знаком с уменьшением:

Прежде чем мы перейдем к фрагменту, вы можете немного уменьшить функцию.

Reduce will apply a function of two arguments cumulatively to the items of a sequence, from left to right, so as to reduce the sequence to a single value.

Вот синтаксис:

reduce(function, sequence[, initial]) -> value

Если начальное значение присутствует, оно помещается перед элементами последовательности в вычислении, и служит по умолчанию, когда последовательность пуста.

Без начального:

>>> reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
15
>>>
smiliar to ((((1+2)+3)+4)+5)

С начальным:

>>> reduce(lambda x, y: x+y, [], 1) 
1
>>>

Это о списке, когда речь идет о словаре:

Сначала давайте посмотрим, что может сделать метод dict.get():

>>> d = {'a': {'b': {'c': 'files'}}}
>>> dict.get(d,'a')
{'b': {'c': 'files'}}
>>>

Итак, когда вы вставляете метод dict.get внутри reduce, это происходит:

>>> d = {'a': {'b': {'c': 'files'}}}
{'b': {'c': 'files'}}
>>> reduce(dict.get, ['a','b','c'], d)
'files'
>>>

Что похоже на:

>>> dict.get(dict.get(dict.get(d,'a'),'b'),'c')
'files'
>>>

и когда вы получите пустой список, вы получите пустой dict, который является значением по умолчанию:

>>> reduce(dict.get, [], {})
{}
>>>

Вернемся к вашему фрагменту:

dir в вашей функции snippet!= встроенный dir(), это просто имя, связанное с пустым словарем.

parent = reduce(dict.get, folders[:-1], dir)

Итак, в приведенной выше строке папки [: - 1] - это всего лишь список каталогов. и dir is empty_dictionary.

Пожалуйста, дайте мне знать, если это поможет в любом случае.

licensed under cc by-sa 3.0 with attribution.