开发者

Why does python dict behave this way?

开发者 https://www.devze.com 2023-02-19 21:58 出处:网络
I have some code written like so: class Invite(models.Model): STATE_UNKNOWN = 0 STATE_WILL_PLAY = 1 STATE_WONT_PLAY = 2

I have some code written like so:

class Invite(models.Model):
    STATE_UNKNOWN = 0
    STATE_WILL_PLAY = 1
    STATE_WONT_PLAY = 2
    STATE_READY = 3
    STATE_CHOICES = ((STATE_UNKNOWN, _("Unknown")),
                     (STATE_WILL_PLAY, _("Yes, I'll play")), 
                     (STATE_WONT_PLAY, _("Sorry, can't play")),
                     (STATE_READY, _("I'm ready to play now")))
    ...


    def change_state(self, state):
        assert(state in dict(Invite.STATE_CHOICES))

This code works like I want it to, but I'm curious as to why it works this way. It is admittedly very convenient that it does work this way, but it seems like maybe I'm missing some underlying philosophy as to why that is.

If I try something like:

dict((1,2,3), (2,2,3), (3,2,3))
ValueError: dictionary update sequence element #0 has length 3; 2 is required

it doesn't create a dict that looks like

{1: (2,3), 2: (2,3), 3: (2,3)}

So the general patter开发者_如何学Gon is not to take the first part of the tuple as the key and the rest as the value. Is there some fundamental underpinning that causes this behavior, or it is just, well, it would be convenient if it did....


I think it's somewhat obvious. In your example, (1,2,3) is a single object. So the idea behind a dictionary is to map a key to a value (i.e. object).

So consider the output:

>>> dict(((1,(2,3)), (2,(2,3)))).items()
[(1, (2, 3)), (2, (2, 3))]

But you can also do something like this:

>>> dict((((1,2),3), ((2,2),3)))
[((1, 2), 3), ((2, 2), 3)]

Where the key is actually an object too! In this case a tuple also.

So in your example:

dict((1,2,3), (2,2,3), (3,2,3))

how do you know which part of each tuple is the key and which is the value?

If you find this annoying, it's a simple fix to write your own constructor:

def special_dict(*args):
    return dict((arg[0], arg[1:]) for arg in args)

Also, to Rafe's comment, you should define the dictionary right away:

class Invite(models.Model):
    STATE_UNKNOWN = 0
    STATE_WILL_PLAY = 1
    STATE_WONT_PLAY = 2
    STATE_READY = 3
    STATE_CHOICES = dict(((STATE_UNKNOWN, _("Unknown")),
                     (STATE_WILL_PLAY, _("Yes, I'll play")), 
                     (STATE_WONT_PLAY, _("Sorry, can't play")),
                     (STATE_READY, _("I'm ready to play now"))))
    ...


    def change_state(self, state):
        assert(state in Invite.STATE_CHOICES)

If you ever want to iterate over the states, all you have to do is:

for state, description = Invite.STATE_CHOICES.iteritems():
    print "{0} == {1}".format(state, description)

The construction of the dictionary in your change_state function is unnecessarily costly.

When you define the Django field, just do:

models.IntegerField(sorted(choices=Invite.STATE_CHOICES.iteritems()))


The constructor of dict accepts (among other things) a sequence of (key, value) tuples. Your second examples passes a list of tuples of length 3 instead of 2, and hence fails.

dict([(1, (2, 3)), (2, (2, 3)), (3, (2, 3))])

however will create the dictionary

{1: (2, 3), 2: (2, 3), 3: (2, 3)}


The general pattern is just this: you can create a dict from a list (in general: iterable) of pairs, treated as (key, value). Anything longer would be arbitrary: why (1,2,3)->{1:(2,3)} and not (1,2,3)-> {(1,2):3}?

Moreover, the pairs<->dict conversion is obviously two-way. With triples it couldn't be (see the above example).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号