Eine ganz naive Möglichkeit wäre:

import re, sys

def preProcess(srcFile):
  inclPat = re.compile(r"#include\s*([^\s]+)\s*$")
  for line in open(srcFile).readlines():
    mat = inclPat.match(line)
    if mat:
      sys.stdout.write(open(mat.group(1)).read())
    else:
      sys.stdout.write(line)

if __name__=="__main__":
  for srcFile in sys.argv[1:]:
    preProcess(srcFile)

Ein wesentlicher Nachteil dieses Ansatzes (die Fragen der fehlenden Fehlerbehandlung wollen wir mal ignorieren) ist, dass includes in inkludierten Dateien nicht mehr gesehen werden. Probiert das und überlegt euch vorm Weiterlesen kurz, wie ihr das beheben könntet.

Wenn ihr jetzt “Rekursion” gesagt habt, liegt ihr richtig – wenn ihr statt der Zeile mit dem open der inkludierten Datei einfach preProcess(mat.group(1)) sagt, funktionierts – aber ihr habt die mögliche Fehlerquelle, dass sich jetzt eine unendliche Rekursion ergeben kann

Mit einem import hat das relativ wenig zu tun. Ein import legt ein neues Modul an und bevölkert dessen Namespace mit den in der betreffenden Datei definierten Objekten. Das include hinterlässt all die Objekte im Namespace des includenden Moduls.

Insofern erinnert es an from x import * – aber es bleibt der Unterschied, dass Code, der auf Modul-Level (also außerhalb jeder Funktion) steht, im includenden statt im importierenden Code ausgeführt wird. Was das bedeutet, könnt ihr euch vielleicht am üblichen Vergleich des __name__ klar machen.