Does Python set update() replace existing items? - python-3.x

If I use my_set.update(my_list) where some elements of my_list are == existing elements in my_set, does it replace the existing items or not?

It does not replace existing elements, as can be seen from the following example:
>>> s = {1}
>>> s.add(1.0)
>>> s
{1}
The same behavior holds for dictionary keys:
>>> d = {1: 'foo'}
>>> d.update({1.0: 'bar'})
>>> d
{1: 'bar'}

It doesn't:
from dataclasses import dataclass
#dataclass(frozen=True)
class MyType:
hashed: int # This will contribute to hash equality
not_hashed: str # This won't contribute to hash equality
# Override hashing/equality
def __eq__(self, other):
return hash(self) == hash(other)
def __hash__(self):
return hash((self.hashed, ))
x = { MyType(hashed=1, not_hashed="original") } # Add an item to a set
x.update([MyType(1, "new")]) # .update() with a different item with the same hash
print(x)
# { MyType(hashed=1, not_hashed='original') } # The original item is in the set

Related

creating a new object based on the type of the given object in python

I have a function that gets a sequence as a parameter and based on the type of that sequence, it creates a new object of that type.
def myFunc(seq):
new_object = # don't know how to instantiate it based on seq type.
For example, if 'seq' is a list, 'new_object' will be an empty list.
Or if 'seq' is a string, 'new_object' will be an empty string and so on.
How can I do that??
This works:
def make_empty(obj):
return type(obj)()
>>> make_empty([1, 2, 3])
[]
>>> make_empty('abc')
''
Works also for numbers not only sequences:
>>> make_empty(2)
0
or dictionaries:
>>> make_empty({'a': 100})
{}
Use type to get the type of the object and use () to create new, empty instance of it.
A version that allows to specified the allowed types:
def make_empty(obj, allowed_types=None):
if allowed_types is None:
return type(obj)()
for typ in allowed_types:
if isinstance(obj, typ):
return type(obj)()
raise TypeError(f'Type {type(obj)} not allowed')
Now.
It still works non-sequences:
>>> make_empty(3)
0
But the allowed types can be specified:
>>> make_empty(3, allowed_types=(str, list, tuple))
...
TypeError: Type <class 'int'> not allowed
The sequences work if allowed:
>>> make_empty('abc', allowed_types=(str, list, tuple))
''
>>> make_empty([1, 2, 3], allowed_types=(str, list, tuple))
[]
>>> make_empty((1, 2, 3), allowed_types=(str, list, tuple))
()
A dictionary does not if not allowsd:
>>> make_empty({'a': 100}, allowed_types=(str, list, tuple))
...
TypeError: Type <class 'dict'> not allowed
since your object is a sequence you can use a slice to get a new object of the same type but empty:
def make_empty(obj):
return obj[:0]
make_empty(['a', 'b', 'c'])
output:
[]
You could access its __class__() attribute.
def empty(obj):
return obj.__class__()
print(empty(2)) # -> 0
print(empty([1,2,3])) # -> []

Check for the existence of an dict and then only define it

python3
>>> a = dict()
>>> a['id1'] = dict()
>>> a['id1']['a'] = 5
>>> a['id1'] = dict()
>>> a['id1']['b'] = 10
>>> a
{'id1': {'b': 10}}
>>>
How can I check the existence of a['id1'] if dict or not and only if not then do a['id1'] = dict()
I need to print
{'id1': {'a': 5, 'b': 10}}
In PHP we don't need to define an associate array, we can assign it directly.
You can test for the presence of key k in a with:
if 'k' in a:
You can see if some object x is a dict with:
if isinstance(x, dict):
Use defaultdict:
from collections import defaultdict
a = defaultdict(dict)
a['id1']['a'] = 5
a['id1']['b'] = 10

python use multiple values in multiple function

How to do something like this in python
def func1():
x = 2
y = 3
return x, y
def funcx():
print(func1().x)
def funcy():
print(func1().y)
So basically return multiple values from a single function then use each returned value in a different function
Python functions can return only one value, but it is easy for that value to contain others. In your example, func1 returns a single tuple, which in turn contains two values.
>>> def func1():
... x = 2
... y = 3
... return x, y
...
>>> func1()
(2, 3)
You can index or unpack this return value just like any other tuple:
>>> func1()[0]
2
>>> func1()[1]
3
>>> a, b = func1()
>>> a
2
You can use indexing also in your desired functions:
def funcx():
print(func1()[0])
def funcy():
print(func1()[1])
If you desire named fields, you can use a dict or namedtuple:
# dict
def func1():
return {'x': 2, 'y': 3}
def funcx():
print(func1()['x'])
# namedtuple
from collections import namedtuple
Point2D = namedtuple('Point2D', ['x', 'y'])
def func1():
return Point2D(x=2, y=3)
def funcx():
print(func1().x)

How do I create a default dictionary of dictionaries

I am trying to write some code that involves creating a default dictionary of dictionaries. However, I have no idea how to initialise/create such a thing. My current attempt looks something like this:
from collections import defaultdict
inner_dict = {}
dict_of_dicts = defaultdict(inner_dict(int))
The use of this default dict of dictionaries is to for each pair of words that I produce from a file I open (e.g. [['M UH M', 'm oo m']] ), to set each segment of the first word delimited by empty space as a key in the outer dictionary, and then for each segment in the second word delimited by empty space count the frequency of that segment.
For example
[['M UH M', 'm oo m']]
(<class 'dict'>, {'M': {'m': 2}, 'UH': {'oo': 1}})
Having just run this now it doesn't seem to have output any errors, however I was just wondering if something like this will actually produce a default dictionary of dictionaries.
Apologies if this is a duplicate, however previous answers to these questions have been confusing and in a different context.
To initialise a defaultdict that creates dictionaries as its default value you would use:
d = defaultdict(dict)
For this particular problem, a collections.Counter would be more suitable
>>> from collections import defaultdict, Counter
>>> d = defaultdict(Counter)
>>> for a, b in zip(*[x.split() for x in ['M UH M', 'm oo m']]):
... d[a][b] += 1
>>> print(d)
defaultdict(collections.Counter,
{'M': Counter({'m': 2}), 'UH': Counter({'oo': 1})})
Edit
You expressed interest in a comment about the equivalent without a Counter. Here is the equivalent using a plain dict
>>> from collections import defaultdict
>>> d = defaultdict(dict)
>>> for a, b in zip(*[x.split() for x in ['M UH M', 'm oo m']]):
... d[a][b] = d[a].get(b, 0) + 1
>>> print(d)
defaultdict(dict, {'M': {'m': 2}, 'UH': {'oo': 1}})
You also could a use a normal dictionary and its setdefault method.
my_dict.setdefault(key, default) will look up my_dict[key] and ...
... if the key already exists, return its current value without modifying it, or ...
... assign the default value (my_dict[key] = default) and then return that.
So you can call my_dict.setdefault(key, {}) always when you want to get a value from your outer dictionary instead of the normal my_dict[key] to retrieve either the real value assigned with this key if it#s present, or to get a new empty dictionary as default value which gets automatically stored into your outer dictionary as well.
Example:
outer_dict = {"M": {"m": 2}}
inner_dict = d.setdefault("UH", {})
# outer_dict = {"M": {"m": 2}, "UH": {}}
# inner_dict = {}
inner_dict["oo"] = 1
# outer_dict = {"M": {"m": 2}, "UH": {"oo": 1}}
# inner_dict = {"oo": 1}
inner_dict = d.setdefault("UH", {})
# outer_dict = {"M": {"m": 2}, "UH": {"oo": 1}}
# inner_dict = {"oo": 1}
inner_dict["xy"] = 3
# outer_dict = {"M": {"m": 2}, "UH": {"oo": 1, "xy": 3}}
# inner_dict = {"oo": 1, "xy": 3}
This way you always get a valid inner_dict, either an empty default one or the one that's already present for the given key. As dictionaries are mutable data types, modifying the returned inner_dict will also modify the dictionary inside outer_dict.
The other answers propose alternative solutions or show you can make a default dictionary of dictionaries using d = defaultdict(dict)
but the question asked how to make a default dictionary of default dictionaries, my navie first attempt was this:
from collections import defaultdict
my_dict = defaultdict(defaultdict(list))
however this throw an error: *** TypeError: first argument must be callable or None
so my second attempt which works is to make a callable using the lambda key word to make an anonymous function:
from collections import defaultdict
my_dict = defaultdict(lambda: defaultdict(list))
which is more concise than the alternative method using a regular function:
from collections import defaultdict
def default_dict_maker():
return defaultdict(list)
my_dict = defaultdict(default_dict_maker)
you can check it works by assigning:
my_dict[2][3] = 5
my_dict[2][3]
>>> 5
or by trying to return a value:
my_dict[0][0]
>>> []
my_dict[5]
>>> defaultdict(<class 'list'>, {})
tl;dr
this is your oneline answer my_dict = defaultdict(lambda: defaultdict(list))

issue with equating dictionaries and global issue

Trying to figure best way to union of two dictionaries. Here is the code that I have. Counter is one of the options that I found.
def __add__(self,right):
mergedbag = Bag()
mergedbag.bag_value = copy.copy(self.bag_value)
for item in right.bag_value.keys():
mergedbag.bag_value[item] += right.bag_value[item]
return mergedbag
To test if two dictionaries have the same contents, simply use an equality test:
self.bag_items == bag_equal.bag_items
Python does this comparison test efficiently; keys and values have to match exactly and difference in length means the dictionaries are not equal:
>>> a = {'a': 'b'}
>>> b = {'a': 'b'}
>>> a == b
True
>>> b['b'] = 'c'
>>> a == b
False
>>> del b['b']
>>> b['a'] = 'c'
>>> a == b
False
>>> b['a'] = 'b'
>>> a == b
True
Note that rather than raise a TypeError, __eq__ should return the NotImplemented sentinel object to signal that equality testing is not supported:
def __eq__(self, other):
if not isinstance(other, Bag):
return NotImplemented
return self.bag_items == other.bag_items
As a side-note, the in membership operator already returns either True or False, there is no need to use a conditional expression in __contains__; the following is enough:
def __contains__(self, include):
return include in self.bag_items
Your code never actually does anything with the items passed in however, nor are you ever counting the items. Your count() method should just look up the element in self.bag_items and return the count once you properly track counts.

Resources