Comparison between Dictionary and List

Asked

Viewed 160 times

1

I’m doing a show where the idea is to start by asking if the person is already registered, otherwise they go through one. I’m having trouble checking if the username is already existing, based on a list of users, the idea is that if the name is already in the list of registered users, this name cannot be re-registered. The conflicting lines are with "ERROR" comment. Here is the code:

#  Arquivo de usuários CADASTRADOS
usuarios = list({'nome': 'marcelo', 'senha': 'marcelo123'})
usuario = dict()


#  Função de CADASTRAR
def cadastro():
    global usuarios
    global usuario
    while True:
        usuario.clear()
        usuario['nome'] = str(input('Digite o nome de usuário que deseja ter: '))
#  A LINHA ABAIXO DA ERRO!
        if usuario['nome'] not in usuarios['nome']:
            break
        print('Nome de usuário já existente! Por favor, tente outro nome de usuário.')
    usuario['senha'] = str(input('Digite a senha que deseja ter: '))
    usuarios.append(usuario.copy)
    print(usuario)


#  O usuário tem CADASTRO?
while True:
    temcadastro = str(input('Você já está cadastrado?[Sim/Não]: ')).upper().strip()[0]
    if temcadastro in 'SN':
        break
    print('Por favor, responda somente com Sim ou Não.')
if temcadastro == 'N':
#  A LINHA ABAIXO DA ERRO!
    cadastro()
print('Fim do programa (por enquanto).')

2 answers

0


usuarios is a list of dictionaries, and not a dictionary. Therefore, the syntax usuarios['nome'] will fail.

To get user names, you will need to iterate on this list and collect the value associated with the key 'nome' of every dictionary within it:

usuarios_existentes = []
for user in usuarios:
    usuarios_existentes.append(user['nome'])
if usuario['nome'] not in usuarios_existentes:
    # . . .

(used the variable user in English to differentiate it from its variable usuario)

Or, as is common in Python, use a list comprehension to do this same operation on a line:

usuarios_existentes = [user['nome'] for user in usuarios]
if usuario['nome'] not in usuarios_existentes:
    # . . .
  • Just to remind you that the operation in is more performative when applying the sets (O(1)) than in lists (O(n)). So you could just exchange for usuarios_existentes = {user['nome'] for user in usuarios} if your user list is too large. ;)

  • PS: I don’t think I should add that to the answer, it’s just to leave an observation for future visitors.

0

TL;DR

Change:

usuarios = list({'nome': 'marcelo', 'senha': 'marcelo123'})

# por:

usuarios = [{'nome': 'marcelo', 'senha': 'marcelo123'}]

And:

if usuario['nome'] not in usuarios['nome']:

# por:

if usuario['nome'] not in {u['nome'] for u in usuarios}:

It seems you still don’t quite understand how to use python’s data structures. I’ll try to explain what I think you still don’t understand.

Creation of list of dictionaries

Your code:

usuarios = list({'nome': 'marcelo', 'senha': 'marcelo123'})

The content of usuarios now is ['nome', 'senha'], and it’s not what you expected. To understand the reason we need to understand what list() ago.

To documentation of list() shows that list receives an iterable as parameter, so you could pass any iterable that list() will iterate over the received argument and add each item of that iteration to the new created list.

For teaching purposes, it would be like:

def list(iteravel):
    nova_lista = []  # cria a nova lista

    for item in iteravel:
       nova_lista.append(item)  # adiciona o item à nova lista

    return nova_lista  # retorna a nova lista criada

Knowing this, we went back to your code. You’re passed a dictionary to list(), how dictionaries are iterable is okay, but when you iterate over a dictionary, you iterate over your keys. In documentation of dict you will see that call iter(d) is just a shortcut to iter(d.keys). Example:

dicionario = {
    'chave_1': 'Valor 1',
    'chave_2': 'Valor 2',
    'chave_3': 'Valor 3',
}

for item in dicionario:
    print(item)

# chave_1
# chave_2
# chave_3

So your code:

usuarios = list({'nome': 'marcelo', 'senha': 'marcelo123'})

It would be like you were doing:

usuario = []

for chave in {'nome': 'marcelo', 'senha': 'marcelo123'}:
    usuario.append(chave)

# usuario = ['nome', 'senha']

What you really want is a list of dictionaries, where each dictionary corresponds to a user of your system. To do this:

usuarios = [{'nome': 'marcelo', 'senha': 'marcelo123'}]

Checking of user existence

Checking a user’s existence on your system will depend on the structure used to store your users.

I will take it as true that you have corrected your code and are using a list of dictionaries, as the previous response section explained.

The operator in, when used in sequences test if an item in the list has value equal to the searched item. That is, 1 in [0, 1, 2] would be like testing:

def contem(valor, sequencia):
    for item in sequencia:
        if item == valor
            return True
    return False

contem(1, [0, 1, 2])  # True
contem(5, [0, 1, 2])  # False

I explain this because if you look at Dict documentation you’ll see that it says:

Dictionaries compare Equal if and only if they have the same (key, value) pairs (regardless of Ordering).

Free translation:

Dictionaries are treated as equals in a comparison if, and only if, they have the same key and value pairs (regardless of their order).

That is, if you test yourself dict_a == dict_b the result will be based on your keys and values, even if they refer to different objects in memory (my answer and the answer accepts of this question explain a little more about this). So:

dict_1 = {'chave_1': 1, 'chave_2': 2}
dict_2 = {'chave_2': 2, 'chave_1': 1}

dict_1 == dict_2
# True

dict_1 is dict_2
id(dict_1) == id(dict_2)
# False para ambos (mesmo valor, mas objetos diferentes)

Moral of the story, if you test your object directly in the user list will only work if your object also has the key senha as the correct value in the listing.

usuarios = list({'nome': 'marcelo', 'senha': 'marcelo123'})

'marcelo' in usuarios
# False

{'nome': 'marcelo'} in usuarios
# False

{'nome': 'marcelo', 'senha': 'marcelo123'} in usuarios
# True

A good way out is to create a set() with the names already registered and test the existence in this set(). Your code would look like this:

cadastros = {u['nome'] for u in usuarios}
if usuario['nome'] not in cadastros:
    break

You can see more about set() and comprehensiset on in the documentation.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.