Copy class dict variable without inheritance

Question:

I have simple class with default_model like this:

class User(BaseModel):
    class Meta:
        collection = db.client.users
        default_model = {
            'cooldowns': {
                'bonus': 0,
                'transfer': 0,
            }
        }

how do I copy Meta.default_model without linking it to the class itself? That is, that the dictate would be one-time, and not inherited from the class itself. I’ve tried two attempts, but they are all tied to the class anyway(

#first try

model = {"user_id": 1}

for k, v in cls.Meta.default_model.items():
    if k not in model:
        model[k] = v

#second try

model = {"user_id": 1} | cls.Meta.default_model.copy()

What i mean:

class User:
    class Meta:
        default_model = {
            "cooldowns": {
                "bonous": 0
            }
        }


model = {}

for k, v in User.Meta.default_model.items():
    model[k] = v

# It changes User.Meta.default_model    
model['cooldowns']['bonous'] = 1
print(User.Meta.default_model)
Asked By: Lumo

||

Answers:

I believe what you’re looking for is deepcopy

from copy import deepcopy


class User:
    class Meta:
        default_model = {
            "cooldowns": {
                "bonus": 0,
                "transfer": 0,
            }
        }


print(User.Meta.default_model)

new = deepcopy(User.Meta.default_model)
new["cooldowns"]["bonus"] = 5
print(new)  # bonus now 5

print(User.Meta.default_model)  # bonus still 0

This gives output:

{'cooldowns': {'bonus': 0, 'transfer': 0}}
{'cooldowns': {'bonus': 5, 'transfer': 0}}
{'cooldowns': {'bonus': 0, 'transfer': 0}}
Answered By: theherk

You are seeing this behavior (values changed in the class are also changed in the new dict model) because you have a nested dict. The value of cooldowns is itself a dict, but your copy operation only applies to that outer dict. The inner dict is not copied, and is in fact the same dict for both.

If you use deepcopy on the dict rather than its copy method, that will ensure that the inner dict isn’t tied to the version in the class.

from copy import deepcopy

model = {**deepcopy(MainClass.InnerClass.default_model), 'user_id': 1}

Otherwise, you can add the copy operation to your for loop. However, this will require you to consider if there could be other types within default_model that could need to be copied separately. The below code would not do a deep copy of a nested list, for example. If possible, deepcopy is the better way to handle this.

model = {"user_id": 1}

for k, v in cls.Meta.default_model.items():
    if k not in model:
        model[k] = v if not isinstance(v, dict) else v.copy()
Answered By: Eric Steele
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.