在Python中动态编辑dict树

I have a piece of PHP code that I am trying to port over to Python that I am unsure how to get working without references.

Essentially it's a config class that works like a tree, each key can have a simple value, or it's own set of keys and values. Part of the class requires being able to set one specific part of the tree without having to send an entire new dict for a root key.

{ "caching": { "enabled": true }}

For example, the above could be a simple configuration. And calling the below code would change true to false

Config.set('caching:enabled', false);

In order to accomplish this in PHP I use references

class Config
{
    private static $aValues;

    public static function set($key, $value)
    {
        if(strpos($key, ':')) {
            $aKeys  = explode(':', $key);
            $iCount = count($aKeys);
        } else {
            $aKeys  = array($key);
            $iCount = 1
        }

        $mData  = &self::$aValues
        for($i = 0; $i < $iCount; ++$i)
        {
            if(!isset($mData[$aKeys[$i])) {
                $mData[$aKeys[$i]]  = array();
            }

            $mData  = &$mData[$aKeys[$i]];

            if($i == ($iCount - 1)) {
                $mData  = $value;
            }
        }
    }
}

But if I try to do something similar in Python

_dmValues = dict()

def set(key, value):
    global _dmValues

    if key.find(':'):
        aKey    = key.split(':')
        iCount  = len(key)
    else:
        aKey    = (key,)
        iCount  = 1

    mData   = _dmValues;
    for i in range(0, iCount):
        if key[i] not in mData.keys():
            mData[key[i]]   = dict()

        mData   = mData[key[i]]

        if i == (iCount - 1):
            mData   = value

It doesn't work, mData is the right value, but since I have written to it, it is no longer a reference.

How can I go about doing this? Is it even possible in Python, or should I just re-write my logic from scratch and give up on a perfect port?

I played around with it more and realised I had the solution, I was just applying it improperly.

Each dictionary, even if it's part of a key of another dictionary, can be passed around by reference. Meaning if I change a key in that dictionary, it will change in the parent as well. Unfortunately I was changing the variable that was a reference to the dictionary, not the dictionary itself.

This works perfectly

mData = _dm_Values
for i in range(0, iCount):
    if i == (iCount - 1):
        mData[key[i]] = value
    else:
        if key[i] not in mData.keys():
            mData[key[i]] = dict()
        mData = mData[key[i]]

You can make your set method as follows:

_dmValues = { "caching": { "enabled": True }}

def set(key, value):
    global _dmValues

    key1,key2    = key.split(':')    

    mData   = _dmValues;

    if key1 in mData:
        if key2 in mData[key1]:
            mData[key1][key2] = value


set('caching:enabled', False)

print(_dmValues)  # {'caching': {'enabled': False}}    

Though probably it would be better to remove the global value and pass reference to the dict as an argument:

def set(mData, key, value):       
    key1,key2    = key.split(':')           
    if key1 in mData:
        if key2 in mData[key1]:
            mData[key1][key2] = value


set(_dmValues, 'caching:enabled', False)

print(_dmValues) # {'caching': {'enabled': False}}