valor una resueltos listas lista eliminar elementos ejercicios diccionarios diccionario dentro convertir como clave agregar actualizar python object casting autovivification

una - lista de diccionarios python



Python: ¿Cómo actualizar el valor del par de valores clave en el diccionario anidado? (9)

Estoy tratando de hacer un índice de documento inverso, por lo tanto, necesito saber de todas las palabras únicas en una colección en qué documento ocurren y con qué frecuencia.

He usado esta respuesta para que dos creen un diccionario anidado. La solución provista funciona bien, aunque con un problema.

Primero abro el archivo y hago una lista de palabras únicas. Estas palabras únicas que quiero comparar con el archivo original. Cuando hay una coincidencia, el contador de frecuencia debe actualizarse y su valor debe almacenarse en la matriz de dos dimensiones.

La salida debería tener el siguiente aspecto:

word1, {doc1 : freq}, {doc2 : freq} <br> word2, {doc1 : freq}, {doc2 : freq}, {doc3:freq} etc....

El problema es que no puedo actualizar la variable del diccionario. Cuando intento hacerlo, obtengo el error:

File "scriptV3.py", line 45, in main freq = dictionary[keyword][filename] + 1 TypeError: unsupported operand type(s) for +: ''AutoVivification'' and ''int''

Creo que necesito lanzar de alguna manera la instancia de AutoVivification a int ...

¿Como ir?

gracias por adelantado

mi código:

#!/usr/bin/env python # encoding: utf-8 import sys import os import re import glob import string import sets class AutoVivification(dict): """Implementation of perl''s autovivification feature.""" def __getitem__(self, item): try: return dict.__getitem__(self, item) except KeyError: value = self[item] = type(self)() return value def main(): pad = ''temp/'' dictionary = AutoVivification() docID = 0 for files in glob.glob( os.path.join(pad, ''*.html'') ): #for all files in specified folder: docID = docID + 1 filename = "doc_"+str(docID) text = open(files, ''r'').read() #returns content of file as string text = extract(text, ''<pre>'', ''</pre>'') #call extract function to extract text from within <pre> tags text = text.lower() #all words to lowercase exclude = set(string.punctuation) #sets list of all punctuation characters text = ''''.join(char for char in text if char not in exclude) # use created exclude list to remove characters from files text = text.split() #creates list (array) from string uniques = set(text) #make list unique (is dat handig? we moeten nog tellen) for keyword in uniques: #For every unique word do for word in text: #for every word in doc: if (word == keyword and dictionary[keyword][filename] is not None): #if there is an occurence of keyword increment counter freq = dictionary[keyword][filename] #here we fail, cannot cast object instance to integer. freq = dictionary[keyword][filename] + 1 print(keyword,dictionary[keyword]) else: dictionary[word][filename] = 1 #extract text between substring 1 and 2 def extract(text, sub1, sub2): return text.split(sub1, 1)[-1].split(sub2, 1)[0] if __name__ == ''__main__'': main()


Creo que está intentando agregar 1 a una entrada de diccionario que aún no existe. Por alguna razón, su método getitem devuelve una nueva instancia de la clase AutoVivification cuando falla una búsqueda. Por lo tanto, intenta agregar 1 a una nueva instancia de la clase.

Creo que la respuesta es actualizar el método getitem para que establezca el contador en 0 si aún no existe.

class AutoVivification(dict): """Implementation of perl''s autovivification feature.""" def __getitem__(self, item): try: return dict.__getitem__(self, item) except KeyError: self[item] = 0 return 0

Espero que esto ayude.


En la clase AutoVivification, defines

value = self[item] = type(self)() return value

que devuelve una instancia de uno mismo, que es una AutoVivificación en ese contexto. El error se vuelve claro.

¿Seguro que quieres devolver una AutoVivificación en cualquier consulta de tecla faltante? Desde el código, supongo que desea devolver un diccionario normal con la clave de cadena y los valores int.

Por cierto, tal vez le interesaría la clase defaultdict .


Esta clase de AutoVivificación no es la magia que estás buscando.

Consulte collections.defaultdict de la biblioteca estándar. Sus dicts internos deberían ser predeterminados, por defecto, a valores enteros, y sus dicts externos serían por defecto los predeterminados para los valores internos.


No estoy seguro de por qué necesita dictados anidados aquí. En un escenario de índice típico, tiene una asignación de índice hacia adelante

identificación del documento -> [word_ids]

y un mapeo de índice inverso

word_id -> [document_ids]

No estoy seguro de si esto está relacionado aquí, pero al usar dos índices puede realizar todo tipo de consultas de manera muy eficiente y la implementación es sencilla ya que no necesita lidiar con estructuras de datos anidados.


Se podría usar el collection.defaultdict de Python en lugar de crear una clase AutoVivification y luego instanciar el diccionario como un objeto de ese tipo.

import collections dictionary = collections.defaultdict(lambda: collections.defaultdict(int))

Esto creará un diccionario de diccionarios con un valor predeterminado de 0. Cuando desee incrementar una entrada, use:

dictionary[keyword][filename] += 1


Sería mejor patear AutoVivification todos juntos, porque no agrega nada.

La siguiente línea:

if (word == keyword and dictionary[keyword][filename] is not None):

No funciona como se esperaba, debido a la forma en que funciona su clase, el dictionary[keyword] siempre devolverá una instancia de AutoVivification , al igual que el dictionary[keyword][filename] .


#!/usr/bin/env python # encoding: utf-8 from os.path import join from glob import glob as glob_ from collections import defaultdict, Counter from string import punctuation WORKDIR = ''temp/'' FILETYPE = ''*.html'' OUTF = ''doc_{0}''.format def extract(text, startTag=''<pre>'', endTag=''</pre>''): """Extract text between start tag and end tag Start at first char following first occurrence of startTag If none, begin at start of text End at last char preceding first subsequent occurrence of endTag If none, end at end of text """ return text.split(startTag, 1)[-1].split(endTag, 1)[0] def main(): DocWords = defaultdict(dict) infnames = glob_(join(WORKDIR, FILETYPE)) for docId,infname in enumerate(infnames, 1): outfname = OUTF(docId) with open(infname) as inf: text = inf.read().lower() words = extract(text).strip(punctuation).split() for wd,num in Counter(words).iteritems(): DocWords[wd][outfname] = num if __name__ == ''__main__'': main()


if (word == keyword and dictionary[keyword][filename] is not None):

ese no es un uso correcto, supongo que intente esto:

if (word == keyword and filename in dictionary[keyword]):

Porque, al verificar el valor de una clave que no existe, subir KeyError. : así que debes verificar si la clave existe en el diccionario ...


Estoy de acuerdo en que debes evitar las clases adicionales, y especialmente __getitem__ . (Pequeños errores conceptuales pueden hacer __getitem__ o __getattr__ bastante doloroso de depurar).

El dict Python parece bastante fuerte para lo que estás haciendo.

¿Qué pasa con dict.setdefault sencillo dict.setdefault

for keyword in uniques: #For every unique word do for word in text: #for every word in doc: if (word == keyword): dictionary.setdefault(keyword, {}) dictionary[keyword].setdefault(filename, 0) dictionary[keyword][filename] += 1

Por supuesto, esto sería donde el dictionary es solo un dict , y no algo de collections o una clase personalizada propia.

Por otra parte, ¿no es esto solo?

for word in text: #for every word in doc: dictionary.setdefault(word, {}) dictionary[word].setdefault(filename, 0) dictionary[word][filename] += 1

No hay razón para aislar instancias únicas, ya que el dict fuerza claves únicas de todos modos.