2010-06-03 4 views
1

J'ai le même code, écrit en utilisant win32com et xlrd. xlrd préforme l'algorithme en moins d'une seconde, tandis que win32com prend quelques minutes.Pourquoi win32com est-il plus lent que xlrd?

Voici le win32com:

def makeDict(ws): 
"""makes dict with key as header name, 
    value as tuple of column begin and column end (inclusive)""" 
wsHeaders = {} # key is header name, value is column begin and end inclusive 
for cnum in xrange(9, find_last_col(ws)): 
    if ws.Cells(7, cnum).Value: 
     wsHeaders[str(ws.Cells(7, cnum).Value)] = (cnum, find_last_col(ws)) 
     for cend in xrange(cnum + 1, find_last_col(ws)): #finds end column 
      if ws.Cells(7, cend).Value: 
       wsHeaders[str(ws.Cells(7, cnum).Value)] = (cnum, cend - 1) 
       break 
return wsHeaders 

Et le xlrd

def makeDict(ws): 
"""makes dict with key as header name, 
    value as tuple of column begin and column end (inclusive)""" 
wsHeaders = {} # key is header name, value is column begin and end inclusive 
for cnum in xrange(8, ws.ncols): 
    if ws.cell_value(6, cnum): 
     wsHeaders[str(ws.cell_value(6, cnum))] = (cnum, ws.ncols) 
     for cend in xrange(cnum + 1, ws.ncols):#finds end column 
      if ws.cell_value(6, cend): 
       wsHeaders[str(ws.cell_value(6, cnum))] = (cnum, cend - 1) 
       break 
return wsHeaders 

Répondre

11

(0) Vous avez demandé "Pourquoi win32com est-il plus lent que xlrd?" ... cette question est un peu comme "Avez-vous arrêté de battre votre femme?" --- il est basé sur une présupposition qui peut ne pas être vraie; win32com a été écrit en C par un brillant programmeur, mais xlrd a été écrit en pur Python par un programmeur moyen. La vraie différence est que win32com doit appeler COM qui implique une communication inter-processus et a été écrit par you-know-who, alors que xlrd lit directement le fichier Excel. De plus, il y a une quatrième partie dans le scénario: VOUS. S'il vous plaît lisez la suite.

(1) Vous ne nous montrez pas la source de la fonction find_last_col() que vous utilisez répétitivement dans le code COM. Dans le code xlrd, vous êtes heureux d'utiliser la même valeur (ws.ncols) tout le temps. Donc, dans le code COM, vous devez appeler find_last_col(ws) une fois et ensuite utilisé le résultat retourné. Mise à jour Voir answer to your separate question sur la façon d'obtenir l'équivalent de Sheet.ncols de xlrd de COM. (2) L'accès à chaque valeur de cellule TWICE ralentit les deux codes. Au lieu de

if ws.cell_value(6, cnum): 
    wsHeaders[str(ws.cell_value(6, cnum))] = (cnum, ws.ncols) 

essayer

value = ws.cell_value(6, cnum) 
if value: 
    wsHeaders[str(value)] = (cnum, ws.ncols) 

Note: il y a 2 cas de ce dans chaque extrait de code.

(3) Il n'est pas du tout évident quel est le but de vos boucles imbriquées, mais il semble y avoir des calculs redondants, impliquant des extractions redondantes de COM. Si vous voulez nous dire ce que vous essayez de réaliser, avec des exemples, nous pourrions vous aider à le faire fonctionner plus rapidement. À tout le moins, extraire les valeurs de COM une fois puis les traiter dans des boucles imbriquées dans Python devrait être plus rapide. Combien de colonnes y a-t-il?

Update 2 Pendant ce temps les petits lutins ont pris à votre code avec le proctoscope, et sont venus avec le script suivant:

tests= [ 
    "A/B/C/D", 
    "A//C//", 
    "A//C//E", 
    "A///D", 
    "///D", 
    ] 
for test in tests: 
    print "\nTest:", test 
    row = test.split("/") 
    ncols = len(row) 
    # modelling the OP's code 
    # (using xlrd-style 0-relative column indexes) 
    d = {} 
    for cnum in xrange(ncols): 
     if row[cnum]: 
      k = row[cnum] 
      v = (cnum, ncols) #### BUG; should be ncols - 1 ("inclusive") 
      print "outer", cnum, k, '=>', v 
      d[k] = v 
      for cend in xrange(cnum + 1, ncols): 
       if row[cend]: 
        k = row[cnum] 
        v = (cnum, cend - 1) 
        print "inner", cnum, cend, k, '=>', v 
        d[k] = v 
        break 
    print d 
    # modelling a slightly better algorithm 
    d = {} 
    prev = None 
    for cnum in xrange(ncols): 
     key = row[cnum] 
     if key: 
      d[key] = [cnum, cnum] 
      prev = key 
     elif prev: 
      d[prev][1] = cnum 
    print d 
    # if tuples are really needed (can't imagine why) 
    for k in d: 
     d[k] = tuple(d[k]) 
    print d 

qui sort ceci:

Test: A/B/C/D 
outer 0 A => (0, 4) 
inner 0 1 A => (0, 0) 
outer 1 B => (1, 4) 
inner 1 2 B => (1, 1) 
outer 2 C => (2, 4) 
inner 2 3 C => (2, 2) 
outer 3 D => (3, 4) 
{'A': (0, 0), 'C': (2, 2), 'B': (1, 1), 'D': (3, 4)} 
{'A': [0, 0], 'C': [2, 2], 'B': [1, 1], 'D': [3, 3]} 
{'A': (0, 0), 'C': (2, 2), 'B': (1, 1), 'D': (3, 3)} 

Test: A//C// 
outer 0 A => (0, 5) 
inner 0 2 A => (0, 1) 
outer 2 C => (2, 5) 
{'A': (0, 1), 'C': (2, 5)} 
{'A': [0, 1], 'C': [2, 4]} 
{'A': (0, 1), 'C': (2, 4)} 

Test: A//C//E 
outer 0 A => (0, 5) 
inner 0 2 A => (0, 1) 
outer 2 C => (2, 5) 
inner 2 4 C => (2, 3) 
outer 4 E => (4, 5) 
{'A': (0, 1), 'C': (2, 3), 'E': (4, 5)} 
{'A': [0, 1], 'C': [2, 3], 'E': [4, 4]} 
{'A': (0, 1), 'C': (2, 3), 'E': (4, 4)} 

Test: A///D 
outer 0 A => (0, 4) 
inner 0 3 A => (0, 2) 
outer 3 D => (3, 4) 
{'A': (0, 2), 'D': (3, 4)} 
{'A': [0, 2], 'D': [3, 3]} 
{'A': (0, 2), 'D': (3, 3)} 

Test: ///D 
outer 3 D => (3, 4) 
{'D': (3, 4)} 
{'D': [3, 3]} 
{'D': (3, 3)} 
+0

+1 Je suis d'accord - le ralentissement n'est pas dû à COM mais à cause de l'utilisation de l'OP qui le rend plus lent. – Cam

+0

Il existe également des moyens de lire et d'écrire plusieurs valeurs depuis/vers Excel à l'aide de COM via safearrays, précisément en raison du surdébit IPC.Vous pouvez le faire de manière transparente via win32com, en spécifiant des plages au lieu de cellules individuelles. –

1

COM exige de parler à un autre processus qui gère en fait les demandes. xlrd travaille en cours sur les structures de données elles-mêmes.

+0

est-il donc impossible de le faire dans un laps de temps raisonnable, avec win32com? – Josh

0

PRIVER comme J'allais au lit la nuit dernière, et j'ai fini par l'utiliser. Une version de loin supérieure à mon original:

def makeDict(ws): 
"""makes dict with key as header name, 
    value as tuple of column begin and column end (inclusive)""" 
wsHeaders = {} # key is header name, value is column begin and end inclusive 
last_col = find_last_col(ws) 

for cnum in xrange(9, last_col): 
    if ws.Cells(7, cnum).Value: 
     value = ws.Cells(7, cnum).Value 
     cstart = cnum 
    if ws.Cells(7, cnum + 1).Value: 
     wsHeaders[str(value)] = (cstart, cnum) #cnum is last in range 
return wsHeaders 
+0

(1) Vous accédez toujours à la même cellule deux fois (2) Code précédent codé en dur 8e colonne et 6e rangée; celui-ci utilise respectivement 9 et 7; Pourquoi? –

Questions connexes