Implement ls(path), mkdir(path), add_content_to_file(path, content), and read_content_from_file(path). ls on a file returns [filename]; on a directory returns sorted children. mkdir and add_content create missing parents.
02Key insight
Model it as a trie keyed by path segments, where each node is either a directory (children map) or a file (content buffer). All operations become a walk over split path components.
from __future__ import annotations
class_Inode:
__slots__ = ("children", "content", "is_dir")
def__init__(self, is_dir: bool) -> None:
self.children: dict[str, _Inode] = {}
self.content: list[str] = [] # list of chunks avoids O(n^2) appendsself.is_dir = is_dir # explicit: an empty directory is still a directoryclassFileSystem:
def__init__(self) -> None:
self.root = _Inode(is_dir=True)
@staticmethoddef_parts(path: str) -> list[str]:
return [p for p in path.split("/") if p]
def_walk(self, path: str, create: bool = False) -> _Inode:
node = self.root
for part inself._parts(path):
ifnot node.is_dir:
raise NotADirectoryError(path)
if part notin node.children:
ifnot create:
raise FileNotFoundError(path)
node.children[part] = _Inode(is_dir=True)
node = node.children[part]
return node
defls(self, path: str) -> list[str]:
node = self._walk(path)
ifnot node.is_dir:
return [self._parts(path)[-1]]
returnsorted(node.children)
defmkdir(self, path: str) -> None:
node = self._walk(path, create=True)
ifnot node.is_dir:
raise FileExistsError(path)
defadd_content_to_file(self, path: str, content: str) -> None:
*dirs, name = self._parts(path) or [""]
parent = self._walk("/".join(dirs), create=True)
ifnot parent.is_dir:
raise NotADirectoryError(path)
node = parent.children.setdefault(name, _Inode(is_dir=False)) if name else parent
if node.is_dir:
raise IsADirectoryError(path)
node.content.append(content)
defread_content_from_file(self, path: str) -> str:
node = self._walk(path)
if node.is_dir:
raise IsADirectoryError(path)
return"".join(node.content)
Try it first.
TimeO(depth) per op (+ k log k for ls sort)
SpaceO(nodes + content)
tests.py · 4 passing
tests.pypython18 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fs = FileSystem()
assert fs.ls("/") == []
fs.mkdir("/a/b/c")
fs.add_content_to_file("/a/b/c/d", "hello")
fs.add_content_to_file("/a/b/c/d", " world")
assert fs.ls("/") == ["a"] and fs.ls("/a/b/c") == ["d"] and fs.ls("/a/b/c/d") == ["d"]
assert fs.read_content_from_file("/a/b/c/d") == "hello world"try:
fs.ls("/missing"); assertFalseexcept FileNotFoundError:
pass
fs.mkdir("/empty")
for bad in ("/empty", "/"):
try:
fs.add_content_to_file(bad, "x"); assertFalseexcept IsADirectoryError:
passassert fs.ls("/empty") == [] and fs.ls("/") == ["a", "empty"]
04Staff-level follow-up
Concurrent writers? Per-inode locks taken in path order (hand-over-hand) to avoid deadlock; real systems add a separate inode table so rename/hard links are O(1) pointer moves.