python set union operation is not behaving well with named tuples - python

I want to create a set of namedtuple in python, with the ability to add elements dynamically using the union operation.
The following code snippet creates a set of namedtuple, which is behaving nicely.
from collections import namedtuple
B = namedtuple('B', 'name x')
b1 = B('b1',90)
b2 = B('b2',92)
s = set([b1,b2])
print(s)
which prints
{B(name='b1', x=90), B(name='b2', x=92)}
Now if I create another namedtuple and add it to my set with the union operations it is not behaving as expected.
b3 = B('b3',93)
s = s.union(b3)
print(s)
The code snippet prints the following output.
{93, B(name='b1', x=90), B(name='b2', x=92), 'b3'}
The expected output should be:
{B(name='b1', x=90), B(name='b2', x=92), B(name='b3', x=93)}
Am I mis-understanding the API?
Both python2 and 3 are showing the same behaviour.

A namedtuple instance is an iterable of items. set.union simply merges the current set with the items in the namedtuple.
However, what you want is put the namedtuple in another container/iterable, so the merge is done with the item (the namedtuple) contained in the new parent iterable:
s.union((b3,))
It becomes more obvious if you actually think of the operator equivalent:
s = s | set(b3) # set(b3) -> {93, 'b3'}
As compared to what we actually want:
s = s | {b3}
The union is performed with the outer iterable.

union expects a set (or a list or another iterable), but you pass a named tuple, which is an iterable by itself, but it provides values, so you merge the set with the values. Try this:
s = s.union({b3})

Since b3 is iterable, union works on its elements rather than on the tuple itself. Replace that with:
s = s.union([b3])

The documentation on set.union actually explains this:
union(*others)
Return a new set with elements from the set and all others.
So it will create a new set including all elements from the others:
>>> set(b3) # these are the unique elements in your `b3`
{93, 'b3'}
>>> s.union(b3) # the union of the unique elements in "s" and "b3"
{B(name='b1', x=90), 93, 'b3', B(name='b2', x=92)}
In your case (because you assign it back to s) you can simply add the item thus avoiding creating a new set entirely:
>>> s.add(b3)
>>> s
{B(name='b1', x=90), B(name='b3', x=93), B(name='b2', x=92)}

Related

Lambda in deafaultdict in Python

I'm following the book 'Data Science from Scratch' and this is a piece of code in it:
dd_pair = defaultdict(lambda: [0, 0])
dd_pair[2][1] = 1 # now dd_pair contains {2: [0, 1]}
Can someone please help me understand why and how it works?
defaultdict takes a data-type as an initializer. Let's consider we have a dictionary called "users" with "ID" being key and a list as value.
We have to check if a "ID" exists in the dictionary, if yes we append something to the list, else we put an empty list in that place.
So with a regular dictionary, we do something like:
users = {}
if "id1" not in users:
users["id1"] = []
users["id1"].append("log")
Now with defaultdict, all we have to do is to set an initialiser as:
from collections import defaultdict
users = defaultdict(list) # Any key not existing in the dictionary will get assigned a `list()` object, which is an empty list
users["id1"].append("log")
So coming to your code,
dd_pair = defaultdict(lambda: [0, 0])
This says, any key which doesn't exist in dd_pair will get a list of two elements initialised to 0 as their initial value. So if you just do print(dd_pair["somerandomkey"]) it should print [0,0].
Therefore, dd_pair[2][1] translates roughly to look like this:
dd_pair[2] = [0,0] # dd_pair looks like: {2:[0,0]}
dd_pair[2][1] = 1 # dd_pair looks like: {2:[0,1]}
Why the need for lambda, why not just use [0,0] ?
The defaultdict constructor expects a callable (The constructor actually expects a default_factory, check out Python docs). In extremely simple terms, if we do defaultdict(somevar), somevar() should be valid.
So, if you just pass [0,0] to defaultdict it'll be wrong since [0,0]() is not valid at all.
So what you need is a function which returns [0,0], which can be simply implemented using lambda:[0,0]. (To verify, just do (lambda:[0,0])() , it will return [0,0]).
One more way is to create a class for your specific type, which is better explained in this answer: https://stackoverflow.com/a/36320098/

pyhamcrest - Compare two list

I've just started learning python. Currently writing a unit test to assert if the elements in the expected list is present in the actual list
def test_compare_list_of_items():
actual_list_of_items = ['a','b']
expected_list_of_items = ['a']
assert_that(actual_list_of_items, has_item(has_items(expected_list_of_items)))
but i'm getting errors like
E Expected: a sequence containing (a sequence containing <['a']>)
E but: was <['a', 'b']>
How and what sequence matcher should i use in order to assert if item 'a' in the expected list is present in the actual list?
You are using has_item when you should only be using has_items. According to the docs this takes multiple matchers which is what you want. Your function then becomes
def test_compare_list_of_items():
actual_list_of_items = ['a','b']
expected_list_of_items = ['a']
assert_that(actual_list_of_items, has_items(*expected_list_of_items))
We use iterable unpacking for the list to feed as the arguments and now when you run it, it shouldn't error out.
I don't know about has_items function, but can you just use something like this?
assertTrue(all(item in expected_list_of_items for item in actual_list_of_items))

why does sqlalchemy return tuples rather than just the result? [duplicate]

I'm querying for the ids of a model, and get a list of (int,) tuples back instead of a list of ids. Is there a way to query for the attribute directly?
result = session.query(MyModel.id).all()
I realize it's possible to do
results = [r for (r,) in results]
Is it possible for the query to return that form directly, instead of having to process it myself?
When passing in ORM-instrumented descriptors such as a column, each result is a named tuple, even for just one column. You could use the column name in a list comprehension to 'flatten' the list (you can drop the .all() call, iteration retrieves the objects too):
result = [r.id for r in session.query(MyModel.id)]
or use the fact that it's a tuple when looping a for loop and unpack it to a single-element tuple of targets:
result = session.query(MyModel.id)
for id, in result:
# do something with the id
The latter could also be used in a list comprehension:
[id for id, in session.query(MyModel.id)]
You don't really have any options to force the row results to be just the single id value.
It's strange that SQLalchemy is not providing a proper solution. In sqlalchemy if select a member variable such as a column then each result is a named tuple as #Martijn said. I came to a solution for this using zip function of python
ZIP Method
Zip official docuementation
zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
Return a list of tuples, where each tuple contains the i-th element
from each of the argument sequences. The returned list is truncated
in length to the length of the shortest argument sequence.
Coming to your example
result = session.query(MyModel.id).all()
result = zip(*result)[0]
Output:
[id1, id2, id3...]
How it will work it will flatten list of tuples given as argument if you pass the list like
[(key11, key21), (key12,key22)]
Zip will convert this list of tuples into
[(key11, key12), (key21, key22)]
In your case you want every Initial value of tupe that is of the MyModel so you can take the 0th tuple from the list.
Chain Method
from itertools import chain
result = session.query(MyModel.id).all() # result [(id1,), (id2,), (id3,)]
result = list(chain(*result))
Output
[id1, id2, id3]
For loop
result = session.query(MyModel.id).all() # result [(id1,), (id2,), (id3,)]
result = [id for id, in result]
Output
[id1, id2, id3]
With Result.scalars() you can create a ScalarResult to receive single objects instead of Rows. With the Core-style querying syntax your example could look like this:
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import sessionmaker
>>> session = sessionmaker(...)
>>> result = session.execute(select(MyModel.id))
>>> result.scalars().all()
[1, 2, 3, ...]
🐉🔥 Careful: If you select multiple columns .scalars().all() returns the first column only. So select(MyModel.id, MyModel.something) has the same ScalarResult as select(MyModel.id). However, to select all columns using select(MyModel), the ScalarResult is helpful again, as it returns a list of MyModel-objects.
q_stringlist = [q[0] for q in session.query((distinct(Table.column))).all()]
for Flask SQLAlchemy
q_stringlist = [q[0] for q in db.session.query((distinct(Table.column))).all()]

Query for list of attribute instead of tuples in SQLAlchemy

I'm querying for the ids of a model, and get a list of (int,) tuples back instead of a list of ids. Is there a way to query for the attribute directly?
result = session.query(MyModel.id).all()
I realize it's possible to do
results = [r for (r,) in results]
Is it possible for the query to return that form directly, instead of having to process it myself?
When passing in ORM-instrumented descriptors such as a column, each result is a named tuple, even for just one column. You could use the column name in a list comprehension to 'flatten' the list (you can drop the .all() call, iteration retrieves the objects too):
result = [r.id for r in session.query(MyModel.id)]
or use the fact that it's a tuple when looping a for loop and unpack it to a single-element tuple of targets:
result = session.query(MyModel.id)
for id, in result:
# do something with the id
The latter could also be used in a list comprehension:
[id for id, in session.query(MyModel.id)]
You don't really have any options to force the row results to be just the single id value.
It's strange that SQLalchemy is not providing a proper solution. In sqlalchemy if select a member variable such as a column then each result is a named tuple as #Martijn said. I came to a solution for this using zip function of python
ZIP Method
Zip official docuementation
zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
Return a list of tuples, where each tuple contains the i-th element
from each of the argument sequences. The returned list is truncated
in length to the length of the shortest argument sequence.
Coming to your example
result = session.query(MyModel.id).all()
result = zip(*result)[0]
Output:
[id1, id2, id3...]
How it will work it will flatten list of tuples given as argument if you pass the list like
[(key11, key21), (key12,key22)]
Zip will convert this list of tuples into
[(key11, key12), (key21, key22)]
In your case you want every Initial value of tupe that is of the MyModel so you can take the 0th tuple from the list.
Chain Method
from itertools import chain
result = session.query(MyModel.id).all() # result [(id1,), (id2,), (id3,)]
result = list(chain(*result))
Output
[id1, id2, id3]
For loop
result = session.query(MyModel.id).all() # result [(id1,), (id2,), (id3,)]
result = [id for id, in result]
Output
[id1, id2, id3]
With Result.scalars() you can create a ScalarResult to receive single objects instead of Rows. With the Core-style querying syntax your example could look like this:
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import sessionmaker
>>> session = sessionmaker(...)
>>> result = session.execute(select(MyModel.id))
>>> result.scalars().all()
[1, 2, 3, ...]
🐉🔥 Careful: If you select multiple columns .scalars().all() returns the first column only. So select(MyModel.id, MyModel.something) has the same ScalarResult as select(MyModel.id). However, to select all columns using select(MyModel), the ScalarResult is helpful again, as it returns a list of MyModel-objects.
q_stringlist = [q[0] for q in session.query((distinct(Table.column))).all()]
for Flask SQLAlchemy
q_stringlist = [q[0] for q in db.session.query((distinct(Table.column))).all()]

How to convert a list of tuples containing two lists into dictionary of key value pairs?

I have a list like this-
send_recv_pairs = [(['produce_send'], ['consume_recv']), (['Send'], ['Recv']), (['sender2'], ['receiver2'])]
I want something like
[ {['produce_send']:['consume_recv']},{['Send']:['Recv']},{['sender2']:['receiver2']}
How to do this?
You can not use list as the key of dictionary.
This Article explain the concept,
https://wiki.python.org/moin/DictionaryKeys
To be used as a dictionary key, an object must support the hash function (e.g. through hash), equality comparison (e.g. through eq or cmp), and must satisfy the correctness condition above.
And
lists do not provide a valid hash method.
>>> d = {['a']: 1}
TypeError: unhashable type: 'list'
If you want to specifically differentiate the key values you can use tuple as they hash able
{ (i[0][0], ): (i[1][0], ) for i in send_recv_pairs}
{('Send',): ('Recv',),
('produce_send',): ('consume_recv',),
('sender2',): ('receiver2',)}
You can't have lists as keys, only hashable types - strings, numbers, None and such.
If you still want to use a dictionary knowing that, then:
d={}
for tup in send_recv_pairs:
d[tup[0][0]]=tup[1]
If you want the value to be string as well, use tup[1][0] instead of tup[1]
As a one liner:
d={tup[0][0]]:tup[1] for tup in list} #tup[1][0] if you want values as strings
You can check it over here, in the second way of creating distionary.
https://developmentality.wordpress.com/2012/03/30/three-ways-of-creating-dictionaries-in-python/
A Simple way of doing it,
First of all, your tuple is tuple of lists, so better change it to tuple of strings (It makes more sense I guess)
Anyway simple way of working with your current tuple list can be like :
mydict = {}
for i in send_recv_pairs:
print i
mydict[i[0][0]]= i[1][0]
As others pointed out, you cannot use list as key to dictionary. So the term i[0][0] first takes the first element from the tuple - which is a list- and then the first element of list, which is the only element anyway for you.
Do you mean like this?
send_recv_pairs = [(['produce_send'], ['consume_recv']),
(['Send'], ['Recv']),
(['sender2'], ['receiver2'])]
send_recv_dict = {e[0][0]: e[1][0] for e in send_recv_pairs}
Resulting in...
>>> {'produce_send': 'consume_recv', 'Send': 'Recv', 'sender2': 'receiver2'}
As mentioned in other answers, you cannot use a list as a dictionary key as it is not hashable (see links in other answers).
You can therefore just use the values in your lists (assuming they stay as simple as in your example) to create the following two possibilities:
send_recv_pairs = [(['produce_send'], ['consume_recv']), (['Send'], ['Recv']), (['sender2'], ['receiver2'])]
result1 = {}
for t in send_recv_pairs:
result1[t[0][0]] = t[1]
# without any lists
result2 = {}
for t in send_recv_pairs:
result2[t[0][0]] = t[1][0]
Which respectively gives:
>>> result1
{'produce_send': ['consume_recv'], 'Send': ['Recv'], 'sender2': ['receiver2']}
>>> result2
{'produce_send': 'consume_recv', 'Send': 'Recv', 'sender2': 'receiver2'}
Try like this:
res = { x[0]: x[1] for x in pairs } # or x[0][0]: x[1][0] if you wanna store inner values without list-wrapper
It's for Python 3 and when keys are unique. If you need collect list of values per key, instead of single value, than you may use something like itertools.groupby or map+reduce. Wrote about this in comments and I'll provide example.
And yes, list cannot store key-values, only dict's, but maybe it's just typo in question.
You can not use list as the dictionary key, but instead you may type-cast it as tuple to create the dict object.
Below is the sample example using a dictionary comprehension:
>>> send_recv_pairs = [(['produce_send'], ['consume_recv']), (['Send'], ['Recv']), (['sender2'], ['receiver2'])]
>>> {tuple(k): v for k, v in send_recv_pairs}
{('sender2',): ['receiver2'], ('produce_send',): ['consume_recv'], ('Send',): ['Recv']}
For details, take a look at: Why can't I use a list as a dict key in python?
However if your nested tuple pairs were not list, but any other hashable object pairs, you may have type-casted it to dict for getting the desired result. For example:
>>> my_list = [('key1', 'value1'), ('key2', 'value2')]
>>> dict(my_list)
{'key1': 'value1', 'key2': 'value2'}

Categories

Resources