2014-12-11 6 views
8

Ho una breve funzione per verificare se una parola è una parola reale confrontandola con il corpus WordNet dal Natural Language Toolkit. Sto chiamando questa funzione da un thread che convalida i file txt. Quando eseguo il mio codice, la prima volta che la funzione viene chiamata getta un AttributeError con il messaggioCosa potrebbe causare che WordNetCorpusReader non abbia alcun attributo LazyCorpusLoader?

"'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args'" 

Quando ho sospendere l'esecuzione, la stessa riga di codice non genera un errore, quindi immagino che il corpus è non ancora caricato sulla mia prima chiamata che ha causato l'errore.

Ho provato a utilizzare nltk.wordnet.ensure_loaded() per forzare il caricamento del corpus, ma sto ancora ottenendo lo stesso errore.

Ecco la mia funzione:

from nltk.corpus import wordnet as wn 
from nltk.corpus import stopwords 
from nltk.corpus.reader.wordnet import WordNetError 
import sys 

cachedStopWords = stopwords.words("english") 

def is_good_word(word): 
    word = word.strip() 
    if len(word) <= 2: 
     return 0 
    if word in cachedStopWords: 
     return 0 
    try: 
     wn.ensure_loaded() 
     if len(wn.lemmas(str(word), lang='en')) == 0: 
      return 0 
    except WordNetError as e: 
     print "WordNetError on concept {}".format(word) 
    except AttributeError as e: 
     print "Attribute error on concept {}: {}".format(word, e.message) 
    except: 
     print "Unexpected error on concept {}: {}".format(word, sys.exc_info()[0]) 
    else: 
     return 1 
    return 1 

print (is_good_word('dog')) #Does NOT throw error 

Se ho un'istruzione di stampa nello stesso file in ambito globale, che non genera l'errore. Tuttavia, se lo chiamo dal mio thread, lo fa. Di seguito è riportato un esempio minimo per riprodurre l'errore. Ho provato sulla mia macchina e dà l'uscita

Attribute error on concept dog: 'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args' 
Attribute error on concept dog: 'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args' 
Attribute error on concept dog: 'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args' 
Attribute error on concept dog: 'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args' 
Attribute error on concept dog: 'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args' 
Attribute error on concept dog: 'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args' 
Attribute error on concept dog: 'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args' 
Attribute error on concept dog: 'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args' 
Attribute error on concept dog: 'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args' 

Esempio minimo:

import time 
import threading 
from filter_tag import is_good_word 

class ProcessMetaThread(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 

    def run(self): 
     is_good_word('dog') #Throws error 


def process_meta(numberOfThreads): 

    threadsList = [] 
    for i in range(numberOfThreads): 
     t = ProcessMetaThread() 
     t.setDaemon(True) 
     t.start() 
     threadsList.append(t) 

    numComplete = 0 
    while numComplete < numberOfThreads: 
     # Iterate over the active processes 
     for processNum in range(0, numberOfThreads): 
      # If a process actually exists 
      if threadsList != None: 
       # If the process is finished 
       if not threadsList[processNum] == None: 
        if not threadsList[processNum].is_alive(): 
         numComplete += 1 
         threadsList[processNum] = None 
     time.sleep(5) 

    print 'Processes Finished' 


if __name__ == '__main__': 
    process_meta(10) 

risposta

12

Ho eseguito il codice e ottenere lo stesso errore. Per una soluzione di lavoro, vedi sotto. Ecco la spiegazione:

LazyCorpusLoader è un oggetto proxy che rappresenta un oggetto corpus prima del caricamento del corpus. (Ciò impedisce al NLTK di caricare massicci corpora in memoria prima di averne bisogno.) La prima volta che si accede a questo oggetto proxy, tuttavia, diventa il corpus che si intende caricare. Vale a dire, l'oggetto proxy LazyCorpusLoader trasforma i suoi valori __dict__ e __class__ nel numero __dict__ e __class__ del corpus che si sta caricando.

Se si confronta il codice con gli errori sopra riportati, è possibile vedere che si sono ricevuti 9 errori quando si è tentato di creare 10 istanze della classe. La prima trasformazione dell'oggetto proxy LazyCorpusLoader in un oggetto WordNetCorpusReader ha avuto esito positivo. Questa azione è stata innescata quando si accede WordNet per la prima volta:

il primo thread

from nltk.corpus import wordnet as wn 
def is_good_word(word): 
    ... 
    wn.ensure_loaded() # `LazyCorpusLoader` conversion into `WordNetCorpusReader` starts 

il secondo thread

Quando si inizia ad eseguire la funzione is_good_word in un secondo thread , tuttavia, il primo thread non ha completamente trasformato l'oggetto proxy LazyCorpusLoader in un WordNetCorpusReader. wn è ancora un oggetto proxy LazyCorpusLoader, quindi inizia nuovamente il processo __load. Una volta raggiunto il punto in cui tenta di convertire il valore __class__ e __dict__ in un oggetto WordNetCorpusReader, tuttavia, il primo thread ha già convertito l'oggetto proxy LazyCorpusLoader in un WordNetCorpusReader.La mia ipotesi è che si esegue in un errore nella linea con il mio commento qui sotto:

class LazyCorpusLoader(object): 
    ... 
    def __load(self): 
     ... 
     corpus = self.__reader_cls(root, *self.__args, **self.__kwargs) # load corpus 
     ... 
     # self.__args == self._LazyCorpusLoader__args 
     args, kwargs = self.__args, self.__kwargs      # most likely the line throwing the error 

Una volta che il primo thread ha trasformato l'oggetto LazyCorpusLoader delega in un oggetto WordNetCorpusReader, i nomi alterati non potranno più lavorare. L'oggetto WordNetCorpusReader non avrà LazyCorpusLoader ovunque nei suoi nomi storti. (self.__args è equivalente a self._LazyCorpusLoader__args mentre l'oggetto è un oggetto LazyCorpusLoader.) In questo modo si ottiene il seguente errore:

AttributeError: 'WordNetCorpusReader' object has no attribute '_LazyCorpusLoader__args' 

Un'alternativa

Alla luce di questo problema, si vuole l'accesso l'oggetto wnprima dello si entra nel threading. Ecco il codice modificato in modo appropriato:

from nltk.corpus import wordnet as wn 
from nltk.corpus import stopwords 
from nltk.corpus.reader.wordnet import WordNetError 
import sys 
import time 
import threading 

cachedStopWords = stopwords.words("english") 


def is_good_word(word): 
    word = word.strip() 
    if len(word) <= 2: 
     return 0 
    if word in cachedStopWords: 
     return 0 
    try: 
     if len(wn.lemmas(str(word), lang='en')) == 0:  # no longer the first access of wn 
      return 0 
    except WordNetError as e: 
     print("WordNetError on concept {}".format(word)) 
    except AttributeError as e: 
     print("Attribute error on concept {}: {}".format(word, e.message)) 
    except: 
     print("Unexpected error on concept {}: {}".format(word, sys.exc_info()[0])) 
    else: 
     return 1 
    return 1 


class ProcessMetaThread(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 

    def run(self): 
     is_good_word('dog') 


def process_meta(numberOfThreads): 
    print wn.__class__   # <class 'nltk.corpus.util.LazyCorpusLoader'> 
    wn.ensure_loaded()   # first access to wn transforms it 
    print wn.__class__   # <class 'nltk.corpus.reader.wordnet.WordNetCorpusReader'> 
    threadsList = [] 
    for i in range(numberOfThreads): 
     start = time.clock() 
     t = ProcessMetaThread() 
     print time.clock() - start 
     t.setDaemon(True) 
     t.start() 
     threadsList.append(t) 

    numComplete = 0 
    while numComplete < numberOfThreads: 
     # Iterate over the active processes 
     for processNum in range(0, numberOfThreads): 
      # If a process actually exists 
      if threadsList != None: 
       # If the process is finished 
       if not threadsList[processNum] == None: 
        if not threadsList[processNum].is_alive(): 
         numComplete += 1 
         threadsList[processNum] = None 
     time.sleep(5) 

    print('Processes Finished') 


if __name__ == '__main__': 
    process_meta(10) 

Ho testato il codice sopra riportato e non ho ricevuto errori.

+2

Ottima spiegazione! Ma puoi spiegare come 'wm' può essere in uno stato incoerente? I suoi dicts sono globali a livello di thread, quindi contiene gli attributi manglessi di 'WordnetCorpusReader'. Ma quali informazioni sul thread locale lo fanno apparire come un "LazyCorpusLoader' sul secondo thread? – alexis

+0

@alexis ottima domanda! Vedrò se riesco a ridimensionarlo un po 'non appena ne avrò l'occasione. (È molto presto qui.) La linea di fondo è che wn è ancora un oggetto proxy quando il secondo thread lo accede per la prima volta nonostante abbia avuto la sua classe e sia cambiato, ma i dettagli sono una cosa importante da includere qui. –

+0

Grazie, non vedo l'ora di leggerlo. Non ho idea di quale tipo di "oggetto proxy" possa rompere in modo così realistico. (Una condizione di competizione, con il thread 2 che vede l'oggetto a metà trasformazione, non dovrebbe dare errori così consistenti.) – alexis