Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The sensible thing is to throw on conflicting keys.


What? Have you never had two dicts and wanted vals from one dict override the other?


That’s semantically not an “add”, it’s a “merge” or “update”


What happens when you “merge” a pile of six pennies with a pile of three pennies? You get a pile of nine pennies. Six plus three is nine. Adding three pennies to six pennies results in a single group of nine pennies.

My point: don’t just pick words, explain why those words were chosen.


Penny piles can’t be merged together and remain pennies. If you merge a pile of 6 and a pile of 3, you get one metal lump. I actually really like the point you are making, just feel like your illustration works against you.


Problem with pennies as your example is it's hard to see how these work into a key value context.

If Penny is a class, then maybe you're asking what happens when you combine two arrays (or Python lists) of Penny objects? You get one array (list) containing all the Penny objects.

Perhaps you're getting at something like what if you have two "wallet" dictionaries you want to merge?

Let's say each wallet dict has a list of Penny objects, Dime objects, $5 notes, etc. If you are merging the wallets, maybe you don't want to overwrite the first wallet's Penny list with the second wallet's Penny list and instead you want to combine them.

This is where a dictionary comprehension can come in handy. Just iterate on the second wallet's items and add each item's value to the first wallet's corresponding value at the same key to create a new wallet object (or update one of your two existing wallets by setting the wallet equal to the comprehesion). You would have to add additional logic if you had any nested dictionaries in your wallet dictionary or another type that doesn't combine with the + operator, such as sets.

The other case is something like when your wallet gets sent off to the thief api. To make this more Pythonic, let's say you have a cached idea of your wallet's contents before your wallet itself is sent over the api. Once your actual wallet comes back and you pull it out to pay for something and realize it's empty, you merge the wallet you're holding with the cached idea of the wallet in your head, effectively updating the wallet to empty. If the key values are still on your returned wallet's dict, just now they're a bunch of empty lists as values, this will work fine:

    cached_wallet.update(my_wallet)
However, if the key value sets are removed entirely from the wallet returned by the thief api, you'd probably be better off doing:

    cached_wallet = my_wallet
This is because in Python the first example would have no affect on your cached idea of the wallet because an empty dict passed to the update method will not modify the dict you're attempting to update. So actually the second approach is much more robust here, unless of course it's problematic for some other part of your system to have a keyless wallet floating around (although I'd suggest fixing those other parts of your system by having defaults in place).

You could also use a dictionary comprehension in this case, making use of the get method with defaults while iterating on the emptied wallet you're holding like this maybe:

    cached_wallet.update({
        k: my_wallet.get(k, type(v)()) for k, v in cached_wallet.items()
    })
If the thief put a new key value pair in your wallet, you wouldn't get it in the above comprehension, so you might want to do this in some cases:

    cached_wallet.update(
        **{ k: my_wallet.get(k, type(v)()) for k, v in cached_wallet.items() },
        **my_wallet,
    )
Although that may be called out as more expensive/redundant than necessary, you may want to combine the .keys() from both dictionaries, cast them as a set to dedupe then iterate through the actual wallet on those and fuck all, you're tossing the dirty tissue he stuffed into your wallet in the garbage anyway ... but then actually you decide to keep it, there may be DNA evidence here ... you have no idea how you're going to actually parse and apply this evidence and it's kinda disgusting and, ugh, get a hold of yourself, toss that out and go wash your hands and then focus on making sure the credit cards that are missing are canceled and hope to God that updating your card number on your Fubo TV streaming account doesn't invalidate your legacy subscription that lets you watch the Barcelona game each weekend for $10/mo because there's no way in hell you're going to start paying them $40/mo, that's bullshit.


I mean, that’s fine, my only point it is not obvious. And you are turning + into a partial function, where it is not usually


If a+b != b+a that breaks the commutativity property of addition.


Yes, but I still want this for combining dictionaries. Python already violates this property, I don't find it confusing.

    Python 3.7.2 (default, Dec 30 2018, 08:55:50) 
    [Clang 10.0.0 (clang-1000.11.45.5)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> 'foo' + 'bar' == 'bar' + 'foo'
    False
    >>> ['foo'] + ['bar'] == ['bar'] + ['foo']
    False


"a"+"b" != "b"+"a" already.


That property is not universal, and it is currently being defined for Python dictionaries. You can't break a property that isn't defined yet. ;-)


Is this what changed in 3.5?


Commutatitivity does not need to always hold. Multiplication is not commutative on matrices, but we still use it often and write it as a product nonetheless


In mathematics, the ‘multiplication’ operation is not assumed to be commutative, whereas the ‘addition’ operator is used specifically to indicate commutativity. So, for example, using the plus sign for string concatenation goes against this tradition.


Python actually supports a special operator @ for non-commutative matrix products.


To be fair, IEEE fp addition is noncommutative in corner cases.


No. That's the absolutely worse way of solving the problem.

1) I either want them to be overridden or not. The current .update can do both, you just put the dict you don't want to override as its argument

2) finding common keys is a different problem, which can be solved in a different way

So no, throwing an exception is just completely useless and won't make any of the common cases easier


The even more sensible thing is to take an optional argument: a function that is passed the conflicting keys and their values, and must resolve the conflict. If `None` is passed, then a documented default is taken.


It’s an operator, there are no optional arguments. Though it’s OK to also have a function like you propose.


Recursively calling the operator is an option.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: