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 pattern 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).
Related
P.S: Thank you everybody ,esp Matthias Fripp . Just reviewed the question You are right I made mistake : String is value not the key
num=[1,2,3,4,5,6]
pow=[1,4,9,16,25,36]
s= ":subtraction"
dic={1:1 ,0:s , 2:4,2:s, 3:9,6:s, 4:16,12:s.......}
There is easy way to convert two list to dictionary :
newdic=dict(zip(list1,list2))
but for this problem no clue even with comprehension:
print({num[i]:pow[i] for i in range(len(num))})
As others have said, dict cannot contain duplicate keys. You can make key duplicate with a little bit of tweaking. I used OrderedDict to keep order of inserted keys:
from pprint import pprint
from collections import OrderedDict
num=[1,2,3,4,5,6]
pow=[1,4,9,16,25,36]
pprint(OrderedDict(sum([[[a, b], ['substraction ({}-{}):'.format(a, b), a-b]] for a, b in zip(num, pow)], [])))
Prints:
OrderedDict([(1, 1),
('substraction (1-1):', 0),
(2, 4),
('substraction (2-4):', -2),
(3, 9),
('substraction (3-9):', -6),
(4, 16),
('substraction (4-16):', -12),
(5, 25),
('substraction (5-25):', -20),
(6, 36),
('substraction (6-36):', -30)])
In principle, this would do what you want:
nums = [(n, p) for (n, p) in zip(num, pow)]
diffs = [('subtraction', p-n) for (n, p) in zip(num, pow)]
items = nums + diffs
dic = dict(items)
However, a dictionary cannot have multiple items with the same key, so each of your "subtraction" items will be replaced by the next one added to the dictionary, and you'll only get the last one. So you might prefer to work with the items list directly.
If you need the items list sorted as you've shown, that will take a little more work. Maybe something like this:
items = []
for n, p in zip(num, pow):
items.append((n, p))
items.append(('subtraction', p-n))
# the next line will drop most 'subtraction' entries, but on
# Python 3.7+, it will at least preserve the order (not possible
# with earlier versions of Python)
dic = dict(items)
I'm a python student (by myself), and as an exercise I decided to try making a script to 'encrypt/decrypt' a message.
The 'encryption algo' that I'm using is very simple, I learned during military service and it was used for troops in the field to encrypt radio messages only.
I assume it's not a secure way to encrypt stuff. (If someone can comment on that, I would love to know more)
Anyway, I'm doing it as an exercise for programming logic, but I've been stuck for a while now.
Here it's how it works:
You get a keyword/phrase (More often used with 2 words (vertical and horizontal) but for now I'm coding the 1 keyword only).
Let's use 'PASSWORD' as key and the message: 'This is a sample message'. I would make a table with PASSWORD as colum index, and fill the table with the message:
P A S S W O R D
t h i s i s a s
a m p l e m e s
s a g e x y z x
[Since the message didn't complete all the columns we completed it with letters that won't cause issues]
Then, we determine the order for the scramble, deriving it alphabetically from the key:
4 1 6 7 8 3 5 2
P A S S W O R D
[a,d,o,p,r,s,s,w]
So line by line, letter by letter, we would take the letters from the message according to the key-order, and form the encrypted message:
'hsstaisi' for the first line, 'msmaeple' and 'axyszgex' for the second and third line.
So the message would be 'hsstaisimsmaepleaxyszgex' [Usually transmitted as "hssta isims maepl eaxys zgex" to make it easier for the radio operator]
Now the code:
I manage to make it work (kind of...), here is how:
I get the message and key, remove spaces, make both them into lists. I create a dictionary where every letter from the key(list) becomes a key in the dict, and the value is a number (from 0 to lenght of the key), like an iterator.
{ 'p':0, a':1, 's':2,... } #[Here is my problem]
After that we sort the key(list) alphabetically and use it as iterator to call for the key(dict) that will call for a number that will be a index from the message list. (My explanation is confusing, may be easier to understand by checking the code bellow).
Letter by letter the message is scrambled and appended in a new list, and then presented as 'encrypted'.
It works! Except if the keyphrase has repeated letters (like ours 'password'). In that situation the corresponding value of a repeated dictionary key gets overwritten, because dict keys are unique.
I've written several different versions for the same code, but I always get stuck in the dict problem, at some point or the other.
Here is the piece of code:
key = ['p','a','s','s','w','o','r','d']
msg = ['t','h','i','s','i','s','a','s','a','m','p','l','e','m','e','s','s','a','g','e']
def encrypt(key_list,msg_list):
while len(msg_list) % len(key_list) != 0:
rest = len(key_list) - (len(msg_list) % len(key_list))
for i in range(rest):
if msg_list[-1] == 'z':
msg_list.append('x')
else:
msg_list.append('z')
key_dict = {}
for i in range(len(key_list)):
key_dict[key_list[i]] = i
key_list.sort()
qnty_rows = len(msg_list) // len(key_list)
cloop = 0
scramble_list = []
while cloop < qnty_rows:
for i in range(len(key_list)):
scramble_list.append(msg_list[key_dict[key_list[i]]+(cloop*len(key_list))])
cloop +=1
encrypted_msg = "".join(scramble_list)
print(encrypted_msg)
Can someone help me find a solution to this, or point me at the right direction?
Considering that I'm still learning to code, any constructive criticism for the code in general is welcomed.
Your error lies in how you assign column numbers to each of the key characters, using a dictionary:
for i in range(len(key_list)):
key_dict[key_list[i]] = i
For repeated letters, only the last index remains; s from password maps first to 2, then to 3, so key_dict['s'] ends up being 3:
i = 0, key_list[i] == 'p', key_dict['p'] = 0
i = 1, key_list[i] == 'a', key_dict['a'] = 1
i = 2, key_list[i] == 's', key_dict['s'] = 2
i = 3, key_list[i] == 's', key_dict['s'] = 3 # replacing 2
i = 4, key_list[i] == 'w', key_dict['w'] = 4
# etc.
Don't use a dictionary; generate a list of paired index and character values, sort this by letter, then extract just the indices:
indices = [i for i, c in sorted(enumerate(key_list, key=lambda p: p[1]))]
I used the enumerate() function to generate the indices; it's achieves the same thing as your range(len(key_list)) loop in a more compact form.
Because enumerate() produces (index, value) pairs and we want to sort on the values (the characters), the above code uses a sort key extracting the values (p[1]).
Note that you don't need your key to be a list even, the above would work directly on a string too; strings are sequences just like lists are.
Here's how this works:
>>> keyphrase = 'password'
>>> list(enumerate(keyphrase)) # add indices
[(0, 'p'), (1, 'a'), (2, 's'), (3, 's'), (4, 'w'), (5, 'o'), (6, 'r'), (7, 'd')]
>>> sorted(enumerate(keyphrase), key=lambda p: p[1]) # sorted on the letter
[(1, 'a'), (7, 'd'), (5, 'o'), (0, 'p'), (6, 'r'), (2, 's'), (3, 's'), (4, 'w')]
>>> [i for i, c in sorted(enumerate(keyphrase), key=lambda p: p[1])] # just the indices
[1, 7, 5, 0, 6, 2, 3, 4]
Now you can use these indices to remap chunks of the plaintext input to encrypted output.
What you have is a called a columnar transposition cipher. For modern computers such a cipher is rather trivial to break. See https://crypto.stackexchange.com/questions/40119/how-to-solve-columnar-transposition-cipher-without-a-key for a discussion on how to approach cracking such a ciphertext.
So i'm making a game, and I have a dictionary of tuples containing the coordinates of the objects on the playing field that looks like this (for example):
location = {player : (1, 4), monster : (3, 2), escape : (4, 0)}
later in my code, I want to change the coordinates to easier to understand areas. The first defining part would be a corresponding letter, and then the second a number, looking like this: player would be in B4, monster in C2, and so on. the top right 'area' is represented by the tuple (4, 4), and the bottom left 'area' is represented by the tuple (0, 0). The only thing I have been able to think of that might work is something like this:
location = {player : (1, 4), monster : (3, 2), escape : (4, 0)}
letters = ["A", "B", "C", "D", "E"]
playerArea = "{}{}".format(letters[int(location[player[0]])+1], location[player[1]])
In short, it didn't work. I think the problem is in unpacking the tuples from the dictionary and using it as the number to get the letter from the list letters. Sorry is this was confusing, i'll try to answer all your questions.
The core of the question is how to convert numerical row/column coordinates to something more readable (battleship-style). Here is a simple and fast function to do just that:
>>> def rc_to_abc(x, y):
return 'ABCDEFGHIJKLOMOPQRSTUVWXYZ'[x] + str(y)
>>> rc_to_abc(1, 4)
'B4'
>>> rc_to_abc(3, 2)
'D2'
>>> rc_to_abc(4, 0)
'E0'
Use a dictionary comprehension using string formatting to build the new values. Unpacking the value-tuples is easily done:
location = {k: '{}{}'.format(letters[x-1], y) for k, (x, y) in location.items()}
print(location)
# {'player': 'A4', 'monster': 'C2', 'escape': 'D0'}
Also, you can use string.ascii_uppercase instead of defining a list of alphabets manually.
OTOH, since your board supposedly has a (0, 0) not sure what you intend to make of index 0 since A is already taken as 1.
You can use string.ascii_uppercase to get a full list of the alphabet to use for each coordinate:
from string import ascii_uppercase as alphabet
location = {"player":(1, 4), "monster":(3, 2), "escape":(4, 0)}
new_location = {a:alphabet[b[0]-1]+str(b[-1]) for a, b in location.items()}
print(new_location)
Output:
{'player': 'A4', 'monster': 'C2', 'escape': 'D0'}
I'm working with a large set of records and need to sum a given field for each customer account to reach an overall account balance. While I can probably put the data in any reasonable form, I figured the easiest would be a list of tuples (cust_id,balance_contribution) as I process through each record. After the round of processing, I'd like to add up the second item for each cust_id, and I am trying to do it without looping though the data thousands of time.
As an example, the input data could look like:[(1,125.50),(2,30.00),(1,24.50),(1,-25.00),(2,20.00)]
And I want the output to be something like this:
[(1,125.00),(2,50.00)]
I've read other questions where people have just wanted to add the values of the second element of the tuple using the form of sum(i for i, j in a), but that does separate them by the first element.
This discussion, python sum tuple list based on tuple first value, which puts the values as a list assigned to each key (cust_id) in a dictionary. I suppose then I could figure out how to add each of the values in a list?
Any thoughts on a better approach to this?
Thank you in advance.
import collections
def total(records):
dct = collections.defaultdict(int)
for cust_id, contrib in records:
dct[cust_id] += contrib
return dct.items()
Would the following code be useful?
in_list = [(1,125.50),(2,30.00),(1,24.50),(1,-25.00),(3,20.00)]
totals = {}
for uid, x in in_list :
if uid not in totals :
totals[uid] = x
else :
totals[uid] += x
print(totals)
output :
{1: 125.0, 2: 30.0, 3: 20.0}
People usually like one-liners in python:
[(uk,sum([vv for kk,vv in data if kk==uk])) for uk in set([k for k,v in data])]
When
data=[(1,125.50),(2,30.00),(1,24.50),(1,-25.00),(3,20.00)]
The output is
[(1, 125.0), (2, 30.0), (3, 20.0)]
Here's an itertools solution:
from itertools import groupby
>>> x
[(1, 125.5), (2, 30.0), (1, 24.5), (1, -25.0), (2, 20.0)]
>>> sorted(x)
[(1, -25.0), (1, 24.5), (1, 125.5), (2, 20.0), (2, 30.0)]
>>> for a,b in groupby(sorted(x), key=lambda item: item[0]):
print a, sum([item[1] for item in list(b)])
1 125.0
2 50.0
I have this python function that takes 2 arguments (string , dictionary) and returns a float. The function is designed to take the average of the integers within a dicionary of scores and strings.
def happiness_score(string, dic):
keys = string.lower().split()
v = sum(dic[key] for key in keys)
return float(v)/len(keys)
I have this test case which works:
print happiness_score("a b" , {"a":(1.2) , "b":(3.4)})
>>> 2.3
I also have a test case with tuples:
print happiness_score("a b" , {"a":(1,2) , "b":(3,4)})
How can I change my code so that I can convert any given tuple to integer so that I can still run my program?
Attempting to use my ninja mind reading skills to guess how you want to convert a tuple to a float, Perhaps you want:
def tup2float(tup):
return float('.'.join(str(x) for x in tup))
This will only work with 2-tuples...
Some results:
>>> tup2float((1,2))
1.2
>>> tup2float((2,3))
2.3
>>> tup2float((2,30))
2.3
>>> tup2float((2,32))
2.32
I'm going to assume that you want the answer in this case to be (2, 3). In other words, you want to treat each element in the tuple as the source of a separate mean.
def happiness_score(dic):
scores = map(sum, zip(*d.values()))
return [float(v)/len(dic) for v in scores]
The first step is to create a set of values for each tuple element:
d = {'a': (1, 2), 'b': (3, 4)}
zip(*d.values())
[(1, 3), (2, 4)]
Then apply sum to each tuple using map.
assuming you read the file into a list called lines
hrs, miles = zip(*[(float(line.split()[-4]), float(line.split()[-2])) for line in lines if 'miles' and 'hrs' in line])
total_hrs = sum(hrs)
total_miles = sum(miles)