chosing a function without if statement in python - python-3.x

Imagine having 200 functions, which represent 200 ways to solve a problem or calculate something like
def A():
...
def B():
...
.
.
.
and the method will be chosen as an input argument, meaning the user decides which method to use, giving this as an argument while running the program like "A" for function/method A.
how to chose that function without if-checking the name of every single function in python.

You can use a dictionary to access directly the function that you need in O(1) complexity. For example:
def A(x):
pass
def B(x):
pass
func_map = {"A": A, "B": B}
Say that you store the user input in a variable chosen_func, then to select and run the right function, do the following:
func_map[chosen_func](x)
Example:
In [1]: def A(x):
...: return x + x
In [2]: def B(x):
...: return x * x
In [3]: func_map = {"A": A, "B": B}
In [4]: func_map["A"](10)
Out[4]: 20
In [5]: func_map["B"](10)
Out[5]: 100

Related

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)

Python defining iterators for dictionary comprehension for a class

I want to check with someone if I defined the iteration function properly. To explain, please consider the following example:
x=[{'n':'foo', 'a': [1,2,3], 'b':[2,3,5]}, {'n':'baz','a':[4,5,6], 'b':[7,8,9]},
{'n':'foo', 'a': [4,3,4], 'b':[1,5,6]}, {'n':'bar','a':[1,2,2], 'b':[2,5,6]}]
quick_dict = {key['n']: [sample['a'] for sample in x if sample['n']==key['n']] for key in x}
This works as expected and outputs:
{'foo': [[1, 2, 3], [4, 3, 4]], 'baz': [[4, 5, 6]], 'bar': [[1, 2, 2]]}
I am trying to do something similar for a class I defined using the __next__ and __iter__ methods. The class instance has many functions and attributes but for the purpose of this question, only the attribute samples is important because it is a list of dictionaries exactly as in the above example. I defined the methods as follows:
def __next__(self):
if self.itercounter < len(self.samples)-1:
self.itercounter +=1
return self.samples[self.itercounter]
else:
raise StopIteration
def __iter__(self):
self.itercounter = -1
return self
This seems to work for list comprehensions, but it fails for dictionary comprehensions.
If I do:
quick_dict = {key['Name']: [sample['CM'] for sample in data if sample['Name'] == key['Name']]
for key in data.samples}
then it works because it is directly accessing the list of dictionaries and it knows what to do. On the other hand if I do
quick_dict = {key['Name']: [sample['CM'] for sample in data if sample['Name'] == key['Name']]
for key in data}
then it is going through my functions, and it doesn't work. It just returns a dictionary with a single key. Here 'CM' is just a key like 'a' in the example.
What am I doing wrong in my definition of __iter__ and __next__?
Your second definition of quick_dict iterates over data with for sample in data while already iterating over it with for key in data. However, your __iter__ and __next__ implementation uses a single instance attribute to control iteration, meaning that nested iteration over data won't work because the second (nested) call to __iter__ resets the counter. To support nested iteration, eliminate __next__ and have __iter__ return a generator instead:
def __iter__(self):
i = -1
while i < len(self.samples)-1:
i += 1
yield self.samples[i]

Split/partition list based on invariant/hash?

I have a list [a1,21,...] and would like to split it based on the value of a function f(a).
For example if the input is the list [0,1,2,3,4] and the function def f(x): return x % 3,
I would like to return a list [0,3], [1,4], [2], since the first group all takes values 0 under f, the 2nd group take value 1, etc...
Something like this works:
return [[x for x in lst if f(x) == val] for val in set(map(f,lst))],
But it does not seem optimal (nor pythonic) since the inner loop unnecessarily scans the entire list and computes same f values of the elements several times.
I'm looking for a solution that would compute the value of f ideally once for every element...
If you're not irrationally ;-) set on a one-liner, it's straightforward:
from collections import defaultdict
lst = [0,1,2,3,4]
f = lambda x: x % 3
d = defaultdict(list)
for x in lst:
d[f(x)].append(x)
print(list(d.values()))
displays what you want. f() is executed len(lst) times, which can't be beat
EDIT: or, if you must:
from itertools import groupby
print([[pair[1] for pair in grp]
for ignore, grp in
groupby(sorted((f(x), x) for x in lst),
key=lambda pair: pair[0])])
That doesn't require that f() produce values usable as dict keys, but incurs the extra expense of a sort, and is close to incomprehensible. Clarity is much more Pythonic than striving for one-liners.
#Tim Peters is right, and here is a mentioned setdefault and another itertool.groupby option.
Given
import itertools as it
iterable = range(5)
keyfunc = lambda x: x % 3
Code
setdefault
d = {}
for x in iterable:
d.setdefault(keyfunc(x), []).append(x)
list(d.values())
groupby
[list(g) for _, g in it.groupby(sorted(iterable, key=keyfunc), key=keyfunc)]
See also more on itertools.groupby

Assign a pandas dataframe to an object as a static class variable - memory use (Python)

I have an Python object called DNA. I want to create 100 instances of DNA. Each of the instances contains a pandas dataframe that is identical for all instances. To avoid duplication, I want to incorporate this dataframe as a static/class attribute.
import pandas as pd
some_df = pd.DataFrame()
class DNA(object):
df = some_variable # Do i declare here?
def __init__(self,df = pd.DataFrame(), name='1'):
self.name = name
self.instance_df = instance_df # I want to avoid this
DNA.some_df = df # Does this duplicate the data for every instance?
What is the correct way to do this?
Can I use the init function to create the class variable? Or will it create a separate class variable for every instance of the class?
Do I need to declare the class variable between the 'class..' and 'def init(...)'?
Some other way?
I want to be able to change the dataframe that I use as a class variable but once the class is loaded, it needs to reference the same value (i.e. the same memory) in all instances.
I've answered your question in the comments:
import pandas as pd
some_df = pd.DataFrame()
class DNA(object):
df = some_variable # You assign here. I would use `some_df`
def __init__(self,df = pd.DataFrame(), name='1'):
self.name = name
self.instance_df = instance_df # Yes, avoid this
DNA.some_df = df # This does not duplicate, assignment **never copies in Python** However, I advise against this
So, using
DNA.some_df = df
inside __init__ does work. Since default arguments are evaluated only once at function definition time, that df is always the same df, unless you explicitly pass a new df to __init__, but that smacks of bad design to me. Rather, you probably want something like:
class DNA(object):
def __init__(self,df = pd.DataFrame(), name='1'):
self.name = name
<some work to construct a dataframe>
df = final_processing_function()
DNA.df = df
Suppose, then you want to change it, at any point you can use:
DNA.df = new_df
Note:
In [5]: class A:
...: pass
...:
In [6]: a1 = A()
In [7]: a2 = A()
In [8]: a3 = A()
In [9]: A.class_member = 42
In [10]: a1.class_member
Out[11]: 42
In [11]: a2.class_member
Out[11]: 42
In [12]: a3.class_member
Out[12]: 42
Be careful, though, when you assign to an instance Python takes you at your word:
In [14]: a2.class_member = 'foo' # this shadows the class variable with an instance variable in this instance...
In [15]: a1.class_member
Out[15]: 42
In [16]: a2.class_member # really an instance variable now!
Out[16]: 'foo'
And that is reflected by examining the namespace of the instances and the class object itself:
In [17]: a1.__dict__
Out[17]: {}
In [18]: a2.__dict__
Out[18]: {'class_member': 'foo'}
In [19]: A.__dict__
Out[19]:
mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'class_member': 42})

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))

Resources