Question récurrente, voici mon implémentation en Python 3. Ça fait réinvention de la roue comparer à ce que la bibliothèque standard propose déjà depuis un moment :

import os 

for root, _, files in os.walk(path):
    print(root, len(files))

Mais depuis la version 3.5 de Python, os.scandir a été grandement optimisée. Je m'y suis donc attelé :

#!/usr/bin/env python3
# coding: utf-8

""" Compter le nombre de fichiers de chaque dossier.
    Les liens symboliques sont ignorés.
"""

import os.path
from os import scandir


class Counter(object):
    """ Classe qui gère le listage et le comptage de chaque dossier. """

    def __init__(self, path):
        """ Initialisation des variables. """

        if not path:
            raise ValueError('A girl needs a path.')

        self.path = path
        self.files = 0

    def work(self):
        """ La méthode qui scanne le dossier et fait le décompte. """

        # `entry` contient pas mal d'informations. Voir :
        #  https://docs.python.org/3/library/os.html#os.DirEntry
        for entry in scandir(self.path):
            
            # S'il s'agit d'un dossier qui n'est pas un lien symbolique...
            if entry.is_dir() and not entry.is_symlink():
            
                # ... On instancie une autre classe de `Counter` afin de faire
                # le décompte des fichiers de ce dossier.
                path = os.path.join(self.path, entry.name)
                counter = Counter(path)
                
                # `yield from` permet de capter et renvoyer le `yield self` du
                # compteur que l'on vient d'instancier. Pour faire plus simple,
                # on récupère la classe une fois que le décompte du dossier
                # est terminé.
                yield from counter.work()
            else:
                # Il s'agit d'un fichier, on incrémente le compteur.
                self.files += 1

        # On renvoie la classe elle-même. On pourrait aussi renvoyer seulement
        # les infos nécessaires avec un `yield (self.path, self.files)`.
        yield self

    def __str__(self):
        """ Représentation de la classe. Permet de faire :
            >>> print(cls)
            /etc 115
        """

        return '{} {}'.format(self.path, self.files)

Exemple 1

Admettons que nous voulions une liste de chaque dossier suivi du nombre de fichiers qu'il contient :

counter = Counter(path)
for cls in counter.work():
    # Afficher seulement les dossiers non vides
    if cls.files:
        print(cls)

Ce qui donnera, par exemple :

python3 exemple1.py "/etc"
...
/etc/apt/apt.conf.d 7
/etc/apt/trusted.gpg.d 7
/etc/apt/sources.list.d 2
/etc/apt/preferences.d 1
/etc/apt 4
/etc/rc6.d 37
/etc/rcS.d 24
/etc/UPower 1
/etc/wpa_supplicant 3
/etc/openvpn 4
/etc 115

Exemple 2

Maintenant, nous voudrions savoir combien de fichiers contient un dossier (sous-dossiers inclus) :

counter = Counter(path)
total = 0
for cls in counter.work():
    total += cls.files
print(path, total)

Ce qui donnera, par exemple :

python3 exemple2.py "/etc"
/etc 3155

Note : pour cet exemple, il aurait été intéressant d'implémenter Counter.__iter__() et Counter.__add__(), puis d'utiliser reduce ; mais le temps aurait été bien trop décuplé (de l'ordre de 10).


Exemple 3

Efin, calqué sur l'exemple précedent, comment savoir combien de fichiers contient un dossier ?

counter = Counter(path)
for cls in counter.work():
    pass
print(counter)

Ce qui donnera, par exemple :

python3 exemple3.py "/etc"
/etc 115