Verifique si todos los atributos existen como claves de dictado en un

Verifique si todos los atributos existen como claves de dictado en un (y solo uno) índice de tupla anidada

Tengo la siguiente tupla de Python:

my_tuple = ( "key1", ("nested_key1", "nested_key2"), "key3")

Tengo que comprobar si un dictado contiene "key1", todos los elementos de ("nested_key1", "nested_key2") o "key3". Si hay una coincidencia para todos los elementos en cualquiera de los índices de la tupla raíz, entonces el algoritmo solo debe evaluarse como True si NO hay otras coincidencias en otros índices. Si hay claves adicionales no especificadas en la tupla, se pueden descartar para fines de coincidencia.

Lo que significa que...

Deberían devolver True:

matching_dict_root = {"key1": 1}
matching_dict_nested = {"nested_key1": 2, "nested_key2": 3}
unspecified_keys_are_allowed = {"key1": 1, "99problems": 99}

Deberían ser falsos:

too_many = {"key1": 1, "nested_key1": 1, "nested_key2": 2}
also_wrong = {"key1": 1, "nested_key2": 1}

Puedo suponer (para mi caso actual, pero las soluciones generales son bienvenidas):

  • solo 1 nivel anidado
  • todos los atributos individuales (en todas las profundidades) son globalmente únicos

Python-3.6, Python-2.7 también es útil, pero no obligatorio. Si es posible (supongo que lo es, y me patearé a mí mismo), std. liberación solo.

Mostrar la mejor respuesta

Me gusta:

sum( keys in D if not isinstance(keys, tuple) 
               else all( key in D for key in keys )
     for keys in my_tuple ) == 1

El motivo de sum( ) == 1 en lugar de any( ) es:

solo evalúe como Verdadero si NO hay otras coincidencias en otros índices

Lo que significa que solo debe haber un índice en my_tuple que sea True el resto debe ser False.

Esto da una salida incorrecta para {"key1", "nested_key2"}.

@ Aran-Fey, correcto, ese es el caso que falta en esta solución. Actualicé la pregunta para corregir los dictados derpy que escribí.

La descripción del problema establece que el ejemplo sería Verdadero.

@DanD., consulte {"key1": 1, "nested_key2": 1}. He notado que esto debería devolver False.

Me ha estado volviendo loco, pero aquí está...

Para obtener una lista plana de la tupla, usamos esta función auxiliar de Flatten ( una irregular) lista de listas:

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el

Esto permitirá probar intersecciones no válidas usando frozenset.isdisjoint() contra una sublista de la lista plana.

La solución final, después de expandir la útil solución de @DanD., es por lo tanto:

def all_attributes_exist_with_null_intersection(iterable, d):
    return sum((i in d if not isinstance(i, tuple) else (all(sub_i in d for sub_i in i)))
               and frozenset(d.keys()).isdisjoint(flatten([x for x in i if x != i]))
               for i in iterable) == 1

Pruebas:

sample_tuple = ("key1", ("nested_key1", "nested_key2"), "key3")

true_tests = [
    {"key1": 1},
    {"nested_key1": 2, "nested_key2": 3},
    {"key1": 1, "99problems": 99}
]

false_tests = [
    {"key1": 1, "nested_key1": 1, "nested_key2": 2},
    {"key1": 1, "nested_key2": 1}
]

trues = [all_attributes_exist_with_null_intersection(sample_tuple, d) for d in true_tests]
falses = [all_attributes_exist_with_null_intersection(sample_tuple, d) for d in false_tests]

print("Trues:\n{trues}\n\nFalses:\n{falses}".format(
    trues=trues,
    falses=falses
)

Salida:

Trues:
[True, True, True]

Falses:
[False, False]

Advertencias:

  • Se requiere Python 3.3 para yield from en la función auxiliar