# Retrieve filetype:pdf from library

I retrieved all the books in pdf from my library with a recursive function.

import os

def walk(dirname):
for name in os.listdir(dirname):
path = os.path.join(dirname, name)

if os.path.isfile(path) and path.endswith('pdf'):
books.append(path)
if os.path.isdir(path):
walk(path)
def main():
dir = '~/Documents/MyLibrary/'
global books
books = []
walk(dir)
print(books)

if __name__ == "__main__":
main()


Similarly to a feature in most UNIX shells, Python has a globbing module, called glob.

It even supports the extended globbing (using ** to recursively descend into sub-folders) since Python 3.5, which you can use like this:

import os
from glob import glob

if __name__ == "__main__":
dir = os.path.expanduser('~/Documents/MyLibrary/')
books = glob(os.path.join(dir, "**/*.pdf"), recursive=True)
print(books)


Here I also used the os.path module to correctly join the paths and expand the ~ symbol to the users home directory.

Alternatively, you could use the pathlib.Path object (introduced in Python 3.4), which also supports globbing:

from pathlib import Path

if __name__ == "__main__":
dir = Path('~/Documents/MyLibrary/').expanduser()
books = list(dir.glob('**/*.pdf'))
print(books)


Note the list call, which is necessary, because the Path.glob method returns a generator (in contrast to glob.glob, which returns a list).

• This is a solution I dare not to imagine, surprise :> – AbstProcDo Jan 2 '18 at 12:41

You are essentially imitating the behaviour of os.walk function, which is already present in python.

Here is a minor implementation of fnmatch to filter out a glob-based file-pattern from the os.walk's result. Since, you do not need an exclude section, your implementation would be easier. Although, personally; I'd go with .endswith check as it is easier to read (and more pythonic).

More importantly, using global variable is a very bad design practice and must be avoided. You can define a new function get_files_of_type, which takes a parameter of type of files to filter out, and another parameter for the source path, and simply returns the list generated after os.walk. A rough implementation would look like (untested):

def get_files_of_types(path='.', file_types=None):
"""
path: string depicting the path which is to be walked
file_types: list of extensions to be checked against
"""
if file_types is None:
file_types = ['pdf']
book_list = []
for root, sub_dirs, files in os.walk(path):
books = [
os.path.join(root, f)
for f in files if f.endswith(tuple(file_types))
]
book_list.append(books)
return book_list

• How about using os.path.splitext instead of str.endswith? – Daniel Jan 2 '18 at 12:21
• @Coal_ with .endswith, a user may chose to provide either .pdf or pdf. – hjpotter92 Jan 2 '18 at 12:24
• A docstring would be really helpful for this method, because get_files_of_type(".", ".pdf") would produce interesting results (since tuple will be called on file_types, it produces ('.', 'p', 'd', 'f')). – Graipher Jan 2 '18 at 12:56
• Also, I would call it get_files_of_types to make it clearer that an iterable of types must be supplied. – Graipher Jan 2 '18 at 13:23