How to change the value of the key in a Python dictionary?

Asked

Viewed 2,343 times

4

Are there any methods in Python that change the dictionary key name? I just know how to change the value.

Exemplo:
RE2 =   {
       "Nome": "Leon Scott Kennedy",
       "Ocupação": "Oficial de Polícia (R.P.D)",
       "Nascimento": 1977
       }

print (RE2)

I wanted to change the key Birth for Date of Birth

2 answers

9

There is no Rename, but it is possible to store the value in a new key and delete the old one in a single operation if the method is used pop of dictionaries:

RE2["Data de Nascimento"] = RE2.pop("Nascimento")

This saves a line, and the need for del. In addition to the pop allows you to place a default argument - so, if by chance the old key is not set, you will not have an error - instead, the default value is used:

RE2["Data de Nascimento"] = RE2.pop("Nascimento", "")

Will create the entrance "Data de Nascimento" with the value "" (empty string), if there was no key "Nascimento" in the original dictionary.

If it’s an operation you’ll do often, it can be more elegant (but not always), create a class that extends the original Python dictionaries and add a method that does this. Internally, the best way will still be the use of pop and creation of the new key - but your dictionary may have a "Rename" method that does this:

class MeuDict(dict):
   def rename(self, old_key, new_key):
        self[new_key] = self.pop(old_key)

And this can be used directly, if only its original object is of this new class. Since normal Python dictionaries accept dictionaries as parameters for initialization, a normal dictionary can be converted in this simple with:

RE2 = MeuDict(RE2)

and then:

RE2.rename("Nascimento", "Data de nascimento")

If you are going to use this code in "real" production, that is, you need something reliable, with more functionality, you can take advantage that the logic is all in one place, and make the code "thread safe" - that is, in the case of a program with several threads, make the Rename an operation "atomic" - with the use of a lock - this avoids a chance that the value being renamed is neither in one key nor in another at any given time.

We can also use the "Sentinel Pattern" to repeat in the method rename the behaviour of the parameter default of pop.

import threading
_sentinel = object()

class MeuDict(dict):
   def __init__(self, *args, **kw):
       self.lock = threading.Lock()
       super().__init__(*args, **kw)

   def rename(self, old_key, new_key, default=_sentinel):
        pop_sentinel = {} if default is _sentinel else {'default': default}
        with self.lock:
             # cria a nova chave -  entre essa linha e a próxima as
             # duas chaves existem. Se a thread mudar bem nesse ponto, o valor
             # vai ser encontrado nas duas - o que é melhor 
             # que "em nenhuma", que acontece com o `pop`
             self[new_key] = self.get(old_key, **pop_sentinel)
             del old_key

If an atomic operation is really necessary

After answering, I realized that the threading.Lock alone only prevents the same lock from being used in another thread - there would still be the risk of in a second thread some code "look" the dictionary between the two key modifiements. That’s why I changed the code of pop for copy + del - the two keys will exist at that transition time.

If you really need a thread-safe object that does not allow any race-condition in which the dictionary is "viewed" without any or without two keys, the only way is to increase class specialization - you have to create a "Mapping" (an object like a dictionary), which uses the thread Locking at all points where a dictionary item is to be consulted - then, using the same lock, it will have to wait for the end of the update before making the query available. In this case, it is not worth inheriting from a dictionary (Dict) - that because of internal optimizations, does not have a single point of reference of the keys and values, and yes, inherit from collections.abc.MutableMapping, implement the rename as above, and, dntro do __getitem__, __iter__ and __delitem__ also make use of the same lock.

The biggest difference is that Collections.abc.Mutablemapping allows you to implement an object that works like a dictionary, but itself is not a dictionary - in the case of inheriting from dict, manipulate the keys in the methods using the self. In this other implementation, the recommendation is to keep an internal dictionary in a private attribute.

  • it was not necessary to explain so thoroughly as to have imported a library, but okay.

  • 3

    For what you want, just removing one key and creating another is enough. Other people seeking the same question may have a scenario where they need atomic operation, or they want to understand how Python works from the inside.

7


You can use the del to remove after placing the value in a new key leaving +/- like this:

RE2 =   {
   "Nome": "Leon Scott Kennedy",
   "Ocupação": "Oficial de Polícia (R.P.D)",
   "Nascimento": 1977
}

RE2["Data de Nascimento"] = RE2["Nascimento"]
#    ^ key nova                  ^ key antiga

del RE2["Nascimento"]
#        ^ delete key antiga

print(RE2)

Online example


You can also use the method pop for this staying +/- like this:

RE2 =   {
   "Nome": "Leon Scott Kennedy",
   "Ocupação": "Oficial de Polícia (R.P.D)",
   "Nascimento": 1977
}

RE2["Data de Nascimento"] = RE2.pop("Nascimento")
#    ^ key nova                      ^ key antiga
print(RE2)

Online example 2

  • 1

    Thanks for the help!

Browser other questions tagged

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