Tuples in Dicts - python

Is it possible in python to add a tuple as a value in a dictionary?
And if it is,how can we add a new value, then? And how can we remove and change it?

>>> a = {'tuple': (23, 32)}
>>> a
{'tuple': (23, 32)}
>>> a['tuple'] = (42, 24)
>>> a
{'tuple': (42, 24)}
>>> del a['tuple']
>>> a
{}
if you meant to use tuples as keys you could do:
>>> b = {(23, 32): 'tuple as key'}
>>> b
{(23, 32): 'tuple as key'}
>>> b[23, 32] = 42
>>> b
{(23, 32): 42}
Generally speaking there is nothing specific about tuples being in dictionary, they keep behaving as tuples.

Since tuples are immutable, you cannot add a value to the tuple. What you can do, is construct a new tuple from the current tuple and an extra value. The += operator does this for you, provided the left argument is a variable (or in this case a dictionary value):
>>> t = {'k': (1, 2)}
>>> t['k'] += (3,)
>>> t
{'k': (1, 2, 3)}
Regardless, if you plan on altering the tuple value, perhaps it's better to store lists? Those are mutable.
Edit: Since you updated your question†, observe the following:
>>> d = {42: ('name', 'date')}
>>> d[42][0] = 'name2'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
This happens because, as stated before, tuples are immutable. You cannot change them. If you want to change them, then in fact you'll have to create a new one. Thus:
>>> d[42] = ('name2', d[42][2])
>>> d
{42: ('name2', 'date')}
As a side note, you may want to use namedtuples. They work just like regular tuples, but allow you to refer to elements within the tuple by name:
>>> from collections import namedtuple
>>> Person = namedtuple('Person', 'name date')
>>> t = {42: Person('name', 'date')}
>>> t[42] = Person('name2', t[42].date)
>>> t
{42: Person(name='name2', date='date')}
†: Next time please edit your actual question. Do not post an answer containing only further questions. This is not a forum.

You can't change a tuple itself. You have to replace it by a different tuple.
When you use a list, you could also add values to it (changing the list itself) without need to replace it:
>> a = {'list': (23, 32)}
>> a
{'list': [23, 32]}
>> a['list'].append(99)
>> a
{'list': [23, 32, 99]}
In most cases, lists can be used as replacement for tuples (since as much I know they support all tuple functions -- this is duck typing, man!)

t1=('name','date')
t2=('x','y')
# "Number" is a String key!
d1={"Number":t1}
# Update the value of "Number"
d1["Number"] = t2
# Use a tuple as key, and another tuple as value
d1[t1] = t2
# Obtain values (getters)
# Can throw a KeyError if "Number" not a key
foo = d1["Number"]
# Does not throw a key error, t1 is the value if "Number" is not in the dict
d1.get("Number", t1)
# t3 now is the same as t1
t3 = d1[ ('name', 'date') ]
You updated your question again. Please take a look at Python dict docs. Python documentation is one of it's strong points! And play with the interpreter (python)on the command line! But let's continue.
initially key 0
d[0] = ('name', datetime.now())
id known
d1 = d[0]
del d[0]
name changed
tmp = d1
d1 = ( newname, tmp1 )
And please consider using a
class Person(object):
personIdCounter = 1
def __init__(self):
self.id = Person.personIdCounter
Person.personIdCounter += 1
self.name
self.date
then
persons = {}
person = Person()
persons[person.id] = person
person.name = "something"
persons[1].name = "something else"
That looks better than a tuple and models your data better.

Related

Some difficulties in Python on Begginer level

Sorry if I'm asking something dumb. I'm begginer btw.
Can I somehow place multiple variables in one variable, like:
login = "user"
enter = input(login + ">")
commandLogin = "login"
commandRegister = "register"
commandExit = "exit"
commandList = commandLogin, commandRegister, commandExit
while enter != commandList:
print("incorrect command!")
enter = input(login + ">")
Well, in the example you're already doing that.
The main correction is that you probably want while enter not in commandList
You can use an 'iterable', such as a tuple or a list. Check here: Python docs
So for your code you can use:
login = "user"
commandLogin = "login"
commandRegister = "register"
commandExit = "exit"
commandList = [commandLogin, commandRegister, commandExit]
enter = input(login + ">")
while enter not in commandList:
print("Incorrect command!")
enter = input(login + ">")
Sure. You can use a dictionary to put multiple variables in one variable:
>>> a={}
>>> a['first']='this is the first'
>>> a['second']='the second'
>>> a
{'first': 'this is the first', 'second': 'the second'}
>>> a['first']
'this is the first'
>>>
You can also create a class, an object of the class, and access attributes:
>>> class Empty():
... pass
...
>>> a = Empty()
>>> a.first = 'the first value'
>>> a.second = 'the second value'
>>> a
<__main__.Empty object at 0x7fceb005dbd0>
>>> a.first
'the first value'
>>> a.second
'the second value'
>>> a.third
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Empty' object has no attribute 'third'
>>>
In your example, you probably want to use a list:
>>> a = []
>>> a.append("first")
>>> a.append("second")
>>> a.append("third")
>>> a
['first', 'second', 'third']
>>>
But it's not really clear what you are trying to do in your example.
When you separate multiple variables by comma without any surrounding parenthesis, python creates a tuple for you:
a = 1
b = 2
c = 3
# implicit tuple
x = a, b, c
# equivalent, explicit tuple
x = (a, b, c)
Likewise, you can expand a tuple (or any iterable) into multiple variables:
# Expanding a tuple
x = (1, 2, 3)
a, b, c = x # a=1, b=2, c=3
# Expanding a list
x = [1, 2, 3]
a, b, c = x # a=1, b=2, c=3
# Expanding a dict (the keys are expanded into the new variables)
x = {'a': 1, 'b': 2}
a, b = x # a = 'a', b = 'b'
# Expanding an iterator
a, b = range(2) # a=0, b=1
The result of this is a tuple. A tuple is similar to a list but once a tuple is created, the values cannot be changed. The technique in the code is called tuple packing and essentially just creates a tuple.
So you can do it by many ways, i'll give you two of them!
You can always try to get to know some other ;)
You can do a list(changeable list) like this : colors = ["white", "blue", "red"].
Or you can do a tuple ( non-changeable list ( you can change the variable, but not the list itself )) like this : colors = ("white", "blue", "red")
Yes You can Place multiple Variable
but avoid it
https://www.geeksforgeeks.org/assigning-multiple-variables-in-one-line-in-python/

How to find the name of a min (or max) attribute?

Given three or more variables, I want to find the name of the variable with the min value.
I can get the min value from the list, and I can get the index within the list of the min value. But I want the variable name.
I feel like there's another way to go about this that I'm just not thinking of.
a = 12
b = 9
c = 42
cab = [c,a,b]
# yields 9 (the min value)
min(cab)
# yields 2 (the index of the min value)
cab.index(min(cab))
What code would yield 'b'?
The magic of vars prevents you from having to make a dictionary up front if you want to have things in instance variables:
class Foo():
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def min_name(self, names = None):
d = vars(self)
if not names:
names = d.keys()
key_min = min(names, key = (lambda k: d[k]))
return key_min
In action
>>> x = Foo(1,2,3)
>>> x.min_name()
'a'
>>> x.min_name(['b','c'])
'b'
>>> x = Foo(5,1,10)
>>> x.min_name()
'b'
Right now it'll crash if you pass an invalid variable name in the parameter list for min_name, but that's resolvable.
You can also update the dictionary and it's reflected in the source
def increment_min(self):
key = self.min_name()
vars(self)[key] += 1
Example:
>>> x = Foo(2,3,4)
>>> x.increment_min()
>>> x.a
3
You cannot get the name of the variable with the minimum/maximum value like this*, since as #jasonharper commented: cab is nothing more than a list containing three integers; there is absolutely no connection to the variables that those integers originally came from.
A simple workaround is to user pairs, like this:
>>> pairs = [("a", 12), ("b", 9), ("c", 42)]
>>> min(pairs)
('b', 9)
>>> min(pairs)[0]
'b'
See Green Cloak Guy's answer, but if you want to go for readability, I suggest following a similar approach to mine.
You'd have to get very creative for this to work, and the only solution I can think of is rather inefficient.
You can get the memory address of the data b refers to fairly easily:
>>> hex(id(b))
'0xaadd60'
>>> hex(id(cab[2]))
'0xaadd60'
To actually correspond that with a variable name, though, the only way to do that would be to look through the variables and find the one that points to the right place.
You can do this by using the globals() function:
# get a list of all the variable names in the current namespace that reference your desired value
referent_vars = [k for k,v in globals().items() if id(v) == id(cab[2])]
var_name = referent_vars[0]
There are two big problems with this solution:
Namespaces - you can't put this code in a function, because if you do that and then call it from another function, then it won't work.
Time - this requires searching through the entire global namespace.
The first problem could be alleviated by additionally passing the current namespace in as a variable:
def get_referent_vars(val, globals):
return [k for k,v in globals.items() if id(v) == id(val)]
def main():
a = 12
b = 9
c = 42
cab = [a, b, c]
var_name = get_referent_vars(
cab[cab.index(min(cab))],
globals()
)[0]
print(var_name)
# should print 'b'

Extending python dictionary and changing key values

Assume I have a python dictionary with 2 keys.
dic = {0:'Hi!', 1:'Hello!'}
What I want to do is to extend this dictionary by duplicating itself, but change the key value.
For example, if I have a code
dic = {0:'Hi!', 1:'Hello'}
multiplier = 3
def DictionaryExtend(number_of_multiplier, dictionary):
"Function code"
then the result should look like
>>> DictionaryExtend(multiplier, dic)
>>> dic
>>> dic = {0:'Hi!', 1:'Hello', 2:'Hi!', 3:'Hello', 4:'Hi!', 5:'Hello'}
In this case, I changed the key values by adding the multipler at each duplication step. What's the efficient way of doing this?
Plus, I'm also planning to do the same job for list variable. I mean, extend a list by duplicating itself and change some values like above exmple. Any suggestion for this would be helpful, too!
You can try itertools to repeat the values and OrderedDict to maintain input order.
import itertools as it
import collections as ct
def extend_dict(multiplier, dict_):
"""Return a dictionary of repeated values."""
return dict(enumerate(it.chain(*it.repeat(dict_.values(), multiplier))))
d = ct.OrderedDict({0:'Hi!', 1:'Hello!'})
multiplier = 3
extend_dict(multiplier, d)
# {0: 'Hi!', 1: 'Hello!', 2: 'Hi!', 3: 'Hello!', 4: 'Hi!', 5: 'Hello!'}
Regarding handling other collection types, it is not clear what output is desired, but the following modification reproduces the latter and works for lists as well:
def extend_collection(multiplier, iterable):
"""Return a collection of repeated values."""
repeat_values = lambda x: it.chain(*it.repeat(x, multiplier))
try:
iterable = iterable.values()
except AttributeError:
result = list(repeat_values(iterable))
else:
result = dict(enumerate(repeat_values(iterable)))
return result
lst = ['Hi!', 'Hello!']
multiplier = 3
extend_collection(multiplier, lst)
# ['Hi!', 'Hello!', 'Hi!', 'Hello!', 'Hi!', 'Hello!']
It's not immediately clear why you might want to do this. If the keys are always consecutive integers then you probably just want a list.
Anyway, here's a snippet:
def dictExtender(multiplier, d):
return dict(zip(range(multiplier * len(d)), list(d.values()) * multiplier))
I don't think you need to use inheritance to achieve that. It's also unclear what the keys should be in the resulting dictionary.
If the keys are always consecutive integers, then why not use a list?
origin = ['Hi', 'Hello']
extended = origin * 3
extended
>> ['Hi', 'Hello', 'Hi', 'Hello', 'Hi', 'Hello']
extended[4]
>> 'Hi'
If you want to perform a different operation with the keys, then simply:
mult_key = lambda key: [key,key+2,key+4] # just an example, this can be any custom implementation but beware of duplicate keys
dic = {0:'Hi', 1:'Hello'}
extended = { mkey:dic[key] for key in dic for mkey in mult_key(key) }
extended
>> {0:'Hi', 1:'Hello', 2:'Hi', 3:'Hello', 4:'Hi', 5:'Hello'}
You don't need to extend anything, you need to pick a better input format or a more appropriate type.
As others have mentioned, you need a list, not an extended dict or OrderedDict. Here's an example with lines.txt:
1:Hello!
0: Hi.
2: pylang
And here's a way to parse the lines in the correct order:
def extract_number_and_text(line):
number, text = line.split(':')
return (int(number), text.strip())
with open('lines.txt') as f:
lines = f.readlines()
data = [extract_number_and_text(line) for line in lines]
print(data)
# [(1, 'Hello!'), (0, 'Hi.'), (2, 'pylang')]
sorted_text = [text for i,text in sorted(data)]
print(sorted_text)
# ['Hi.', 'Hello!', 'pylang']
print(sorted_text * 2)
# ['Hi.', 'Hello!', 'pylang', 'Hi.', 'Hello!', 'pylang']
print(list(enumerate(sorted_text * 2)))
# [(0, 'Hi.'), (1, 'Hello!'), (2, 'pylang'), (3, 'Hi.'), (4, 'Hello!'), (5, 'pylang')]

Hashing bitarray ? Counting

It seems for some reason that a dict can not have a non-duplicate key which is bitarray()
ex.:
data = {}
for _ in xrange(10):
ba = ...generate repeatable bitarrays ...
data[ba] = 1
print ba
{bitarray('11011'): 1, bitarray('11011'): 1, bitarray('11011'): 1, bitarray('01111'): 1, bitarray('11110'): 1, bitarray('11110'): 1, bitarray('01111'): 1, bitarray('01111'): 1, bitarray('11110'): 1, bitarray('11110'): 1}
You can clearly see that duplicate are stored as different keys (f.e. first two elements) !! which is weird. What could be the reason.
My goal is simply to count the number of times a bit pattern shows up, and of course Dict's are perfect for this, but it seems that bitarray() for some reason is opaque to the hashing algorithm.
btw.. i have to use bitarray(), cause i do 10000 bits+ patterns.
Any other idea of efficient way of counting occurrence of bit pattens ..
This answer addresses your first confusion regarding duplicate dictionary keys and I assume you're referring to bitarray() from bitarray module, *I've not used this module myself.
In your example above, you're not actually getting duplicate dictionary keys, you might see them that way, but they're duplicates to the naked eye only, for instance:
>>> class X:
... def __repr__(self):
... return '"X obj"'
...
>>> x1 = X()
>>> x2 = X()
>>> d = {x1:1, x2:2}
>>> d
{"X obj": 2, "X obj": 1}
But x1 isn't exactly equals to to x2 and hence they're not duplicates, they're distinct objects of class X:
>>> x1 == x2
False
>>> #same as
... id(x1) == id(x2)
False
>>> #same as
...x1 is x2
False
Moreover, because X class defines __repr__ which returns the string representation for its objects, you would think dictionary d has duplicate keys, again there are no duplicated keys nor are the keys of type str; key of value 1 is X object and key of value 2 is another object of X -- literally two different objects with a single string representation returned by their class's __repr__ method:
>>> # keys are instance of X not strings
... d
{"X obj": 2, "X obj": 1}
>>> d["X obj"]
KeyError: 'X obj'
>>>[x1]
1
>>>[x2]
2
Till BitArray 0.8.1 (or later) I believe it does not satisfy the hash invariant property.
To work around it, you should convert the bit array to byte format as follows.
>>> from bitarray import bitarray
>>> l = [bitarray('11111'), bitarray('11111'), bitarray('11010'), bitarray('11110'), bitarray('11111'), bitarray('11010')]
>>> for x in l: ht[x.tobytes()] = 0
...
>>> for x in l: ht[x.tobytes()] += 1
...
>>> ht
{'\xf8': 3, '\xf0': 1, '\xd0': 2}
Remember you can get back the bitarray from the byte format by using the command frombytes(byte). Though, in this case you will have to keep track of the size of bitarray explicitly as it will return bitarray of size multiple of 8.
If you want to keep the bitarray in the dictionary also:
>>> from bitarray import bitarray
>>> l = [bitarray('11111'), bitarray('11111'), bitarray('11010'), bitarray('11110'), bitarray('11111'), bitarray('11010')]
>>> ht = {}
>>> for x in l: ht[x.tobytes()] = (0, x)
...
>>> for x in l:
... old_count = ht[x.tobytes()][0]
... ht[x.tobytes()] = (old_count+1, x)
...
>>> ht
{'\xf8': (3, bitarray('11111')), '\xf0': (1, bitarray('11110')), '\xd0': (2, bitarray('11010'))}
>>> for x,y in ht.iteritems(): print(y)
...
(3, bitarray('11111'))
(1, bitarray('11110'))
(2, bitarray('11010'))
I solved it :
desc = bitarray(res).to01()
if desc in data : data[desc] += 1
else : data[desc] = 1
gosh I miss perl no-nonsense autovivification :)

Python: Sort a list of objects based on their attributes

Just to preface this, I've already checked out posts pertaining to this question and they haven't fully answered mine.
So I would just like to know how to sort a list of objects based on their attributes in two ways:
if the attribute is a string (to alphabetize)
if the attribute is a integer (to do by numerical order)
This is my list of classes:
mainList = [
hero( name='SirGoose', classes='Fighter', level=150 ),
hero( name='Conan', classes='Barbarian', level=160 ),
hero( name='KingArthur', classes='Knight', level=170 )
]
So what I'm really looking for is a way to sort this list to that the hero's names are sorted in alphabetical order, then another method for level. Thank you!
sorted, list.sort accept optional key parameter. Pass a key function. The return value of the function is used for comparison instead of the original value:
>>> from collections import namedtuple
>>> hero = namedtuple('hero', ['name', 'classes', 'level'])
>>>
>>> mainList = [
... hero(name='SirGoose', classes='Fighter', level=150 ),
... hero(name='Conan', classes='Barbarian', level=160 ),
... hero( name='KingArthur', classes='Knight', level=170 )
... ]
>>> sorted(mainList, key=lambda h: (h.name, h.level))
[hero(name='Conan', classes='Barbarian', level=160),
hero(name='KingArthur', classes='Knight', level=170),
hero(name='SirGoose', classes='Fighter', level=150)]
NOTE: the key function used here (lambda) returns a tuple. tuples are compared item by item. If the first items are same, the next items are compared, ...
>>> ('SirGoose', 12) < ('Barbarian', 160)
False
>>> ('SirGoose', 12) < ('SirGoose', 160)
True
Alternative using operator.attrgetter:
>>> import operator
>>> sorted(mainList, key=operator.attrgetter('name', 'level'))
[hero(name='Conan', classes='Barbarian', level=160),
hero(name='KingArthur', classes='Knight', level=170),
hero(name='SirGoose', classes='Fighter', level=150)]

Categories