Python에서 디렉토리 트리 구조를 나열 하시겠습니까?
우리는 일반적으로 GNU 트리 만 사용하는 것을 선호하지만 항상 tree
모든 시스템에 있는 것은 아니며 때때로 Python 3을 사용할 수 있습니다. 여기에 좋은 대답은 쉽게 복사하여 붙여 넣을 수 있으며 GNU tree
를 요구 사항으로 만들지 않습니다 .
tree
의 출력은 다음과 같습니다.
$ tree
.
├── package
│ ├── __init__.py
│ ├── __main__.py
│ ├── subpackage
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ └── module.py
│ └── subpackage2
│ ├── __init__.py
│ ├── __main__.py
│ └── module2.py
└── package2
└── __init__.py
4 directories, 9 files
내가 호출하는 디렉토리 아래의 홈 디렉토리에 위의 디렉토리 구조를 만들었습니다 pyscratch
.
나는 또한 그런 종류의 출력에 접근하는 다른 답변을 여기에서 볼 수 있지만 더 간단하고 현대적인 코드와 느리게 평가하는 접근 방식으로 더 잘할 수 있다고 생각합니다.
Python의 트리
먼저 다음과 같은 예를 사용하겠습니다.
- Python 3
Path
객체를 사용 합니다.
- (생성기 함수를 만드는)
yield
및 yield from
표현식을 사용합니다.
- 우아한 단순성을 위해 재귀를 사용합니다.
- 추가 명확성을 위해 주석 및 일부 유형 주석을 사용합니다.
from pathlib import Path
# prefix components:
space = ' '
branch = '│ '
# pointers:
tee = '├── '
last = '└── '
def tree(dir_path: Path, prefix: str=''):
"""A recursive generator, given a directory Path object
will yield a visual tree structure line by line
with each line prefixed by the same characters
"""
contents = list(dir_path.iterdir())
# contents each get pointers that are ├── with a final └── :
pointers = [tee] * (len(contents) - 1) + [last]
for pointer, path in zip(pointers, contents):
yield prefix + pointer + path.name
if path.is_dir(): # extend the prefix and recurse:
extension = branch if pointer == tee else space
# i.e. space because last, └── , above so no more |
yield from tree(path, prefix=prefix+extension)
그리고 지금:
for line in tree(Path.home() / 'pyscratch'):
print(line)
인쇄물:
├── package
│ ├── __init__.py
│ ├── __main__.py
│ ├── subpackage
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ └── module.py
│ └── subpackage2
│ ├── __init__.py
│ ├── __main__.py
│ └── module2.py
└── package2
└── __init__.py
얼마나 긴지 알아야하기 때문에 각 디렉토리를 목록으로 구체화해야하지만 나중에 목록을 버립니다. 깊고 광범위한 재귀의 경우 이것은 충분히 게 으르어야합니다.
주석과 함께 위의 코드는 우리가 여기서 무엇을하고 있는지 완전히 이해하기에 충분해야하지만, 필요하다면 디버거를 사용하여 더 잘 이해할 수 있습니다.
더 많은 기능
이제 GNU tree
는이 기능에 대해 갖고 싶은 몇 가지 유용한 기능을 제공합니다.
- 제목 디렉토리 이름을 먼저 인쇄합니다 (자동으로 수행하지만 우리는 인쇄하지 않음).
- 카운트를 인쇄합니다
n directories, m files
- 재귀를 제한하는 옵션,
-L level
- 디렉토리로만 제한하는 옵션,
-d
또한, 거대한 나무가있을 때, islice
텍스트로 인터프리터를 잠그는 것을 피하기 위해 반복을 제한하는 것이 유용합니다. 기본적으로이 값을 임의로 높게 설정할 수 있습니다 1000
.
따라서 이전 주석을 제거하고이 기능을 작성해 보겠습니다.
from pathlib import Path
from itertools import islice
space = ' '
branch = '│ '
tee = '├── '
last = '└── '
def tree(dir_path: Path, level: int=-1, limit_to_directories: bool=False,
length_limit: int=1000):
"""Given a directory Path object print a visual tree structure"""
dir_path = Path(dir_path) # accept string coerceable to Path
files = 0
directories = 0
def inner(dir_path: Path, prefix: str='', level=-1):
nonlocal files, directories
if not level:
return # 0, stop iterating
if limit_to_directories:
contents = [d for d in dir_path.iterdir() if d.is_dir()]
else:
contents = list(dir_path.iterdir())
pointers = [tee] * (len(contents) - 1) + [last]
for pointer, path in zip(pointers, contents):
if path.is_dir():
yield prefix + pointer + path.name
directories += 1
extension = branch if pointer == tee else space
yield from inner(path, prefix=prefix+extension, level=level-1)
elif not limit_to_directories:
yield prefix + pointer + path.name
files += 1
print(dir_path.name)
iterator = inner(dir_path, level=level)
for line in islice(iterator, length_limit):
print(line)
if next(iterator, None):
print(f'... length_limit, {length_limit}, reached, counted:')
print(f'\n{directories} directories' + (f', {files} files' if files else ''))
이제 다음과 같은 출력을 얻을 수 있습니다 tree
.
tree(Path.home() / 'pyscratch')
인쇄물:
pyscratch
├── package
│ ├── __init__.py
│ ├── __main__.py
│ ├── subpackage
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ └── module.py
│ └── subpackage2
│ ├── __init__.py
│ ├── __main__.py
│ └── module2.py
└── package2
└── __init__.py
4 directories, 9 files
그리고 우리는 수준으로 제한 할 수 있습니다.
tree(Path.home() / 'pyscratch', level=2)
인쇄물:
pyscratch
├── package
│ ├── __init__.py
│ ├── __main__.py
│ ├── subpackage
│ └── subpackage2
└── package2
└── __init__.py
4 directories, 3 files
그리고 출력을 디렉토리로 제한 할 수 있습니다.
tree(Path.home() / 'pyscratch', level=2, limit_to_directories=True)
인쇄물:
pyscratch
├── package
│ ├── subpackage
│ └── subpackage2
└── package2
4 directories
회고전
돌이켜 보면 path.glob
매칭에 사용할 수있었습니다 . path.rglob
재귀 적 globbing 에도 사용할 수 있지만 다시 작성해야합니다. itertools.tee
디렉토리 내용 목록을 구체화하는 대신 사용할 수도 있지만 이는 부정적인 절충안을 가질 수 있으며 아마도 코드를 더욱 복잡하게 만들 것입니다.
의견을 환영합니다!