Python objects qualified for function overloading? - python

I'm learning Python lately (Dec.'20) and found this concept is very convoluted. Let's say I have a function. How can I tell if a particular objects type can be applied to this kind of function overloading?
def compute(a, b, c):
return (a+b) * c
It works fine with some of these object type - number, string, but not others.
>>> a, b, c=1,2,3
>>> a, b, c = 'hi', 'U', 3 # okay
>>> a, b, c = [1], [2,3], 3 # okay
>>> a, b, c = {1}, {2}, 3 # it's set()

Taking the + as an example, a data type must implement the __add__() (magic/special/"dunder") method to meaningfully use the + operator. In your examples, it is defined for integers, strings and lists, but not for sets.
The Python Data Model is the reference document if you want to learn more about this topic, but as a starting point you may find this article useful.

If you want to know whether a particular object, say x has an operator overloaded, or what we call magic method in Python, you can check:
hasattr(x, '__add__') # for + and similarly for any other.
Of course, you can define one such method for a class you want, if it does not exist.

Related

What's an elegant way to create a list of objects a list of other objects in Python?

Imagine I have two dataclasses:
class A:
x: int
y: int
class B:
x:int
y:int
And I have a list of class a:
a_list = [A(x=1, y=2), A(x=3,y=4), ...]
What would be an elegant way of creating a list of class B from this list (copying the properties over in each case)?
Give B a class method that produces an instance of B given an instance of A:
class B
x: int
y: int
#classmethod
def from_A(cls, a):
return cls(a.x, a.y)
Then you can write
bs = [B.from_A(a) for a in a_list]
There's a bit of an asymmetry here: B knows details about A, but A knows nothing about B. You could reverse this and let instances of A produce instances of B:
class A:
x: int
y: int
def to_B(self):
return B(self.x, self.y)
bs = [a.to_B() for a in a_list]
Or, you could decide that neither A nor B should be "privileged" to know details about the other, instead having a third "omniscient" entity that knows how to go in one direction or the other. (This is at least symmetrical.)
def b_from_a(a):
return B(a.x, a.y)
bs = [b_from_a(a) for a in a_list]
How you decide to encapsulate the creation of a B from an A is up to you; other factors in your code may help you decide which of the three options is most reasonable.
Assuming both are dataclasses and all of the names match up, you can pass A as keyword arguments to B's constructor using asdict.
my_b_object = B(**asdict(my_a_object))
and, of course, to apply it over a list, use a list comprehension.
b_list = [B(**asdict(a)) for a in a_list]
Access the attributes directly and feed them into B:
b_list = [B(a.x, a.y) for a in a_list]

Use a dict as input to a function but only take arguments that the function needs

I'm working with data science and I have a pretty big script consisting of a lot of helper functions to run analysis on pandas dataframes and then one main function that utilizes my functions to get some results.
The issue is that most or the function inputs are just names of the columns in the dataframe (because they are taken as input from users) and this leads to a lot of functions have partially the same input.
What I wonder is if it possible to have a dict containing all my parameters and simply pass the dict to every function without modifying the dict? That is, each function would have to ignore the keywords in the dict that are not an input to that particular function.
To give an example say I have this function.
def func1(a, b, c):
return a + b * c
And I have this dict containing the inputs to all my functions.
input_dict = {'a': 1,
'b': 2,
'c': 3,
'd': 4}
If I call this function with this dict as input like this:
value = func1(**input_dict)
I will get an error because of the unexpected argument d.
Any workaround here that doesnt involve altering the dict? My code works but I would love to get away from having so many repeated input arguments everywhere.
Thanks in advance :)
What I wonder is if it possible to have a dict containing all my parameters and simply pass the dict to every function without modifying the dict?
That's a pretty good summary of the use case for a class.
from operator import attrgetter
class Foo:
def __init__(self, a, b, c, d):
self.a = a
self.b = b
self.c = c
self.d = d
def func1(self):
a, b, c = attrgetter('a', 'b', 'c')(self)
return a + b * c
f = Foo(1, 2, 3, 4)
value = f.func1()
The instance of your class replaces input_dict. Each helper
function becomes an instance method, with its definition using
only those instance attributes it needs.
My use of attrgetter is just a suggestion for helping migrate your existing functions into methods, as it doesn't require scattering a bunch
of references to self throughout. You can define func1 simply as
def func1(self):
return self.a + self.b * self.c

How to store many inputs from a user in a set [duplicate]

This question already has answers here:
How do I call a function twice or more times consecutively?
(9 answers)
Closed 3 years ago.
My aim is to have many inputs from a user stored in a disordered manner. So, I decided to use a set. The code I have so far is:
a = input()
b = input()
c = input()
d = input()
all = a, b, c, d
print(set(all))
However, I do not want to repeat input() several times like above. Is there a way to achieve this?
If all you want is a set you do not need a, b, c, d.
all = set() #or all = {*(),}
for _ in range(4):
all.add(input())
print(all)
Or,
all = {input() for _ in range(4)}
This is considering you take the inputs in new line. Otherwise, if the input are comma-separated for example:
all = set(input().split(','))
print(all)
or
all = {*input().split(',')}
print(all)
In case you need both a, b, c, d and all the inputs, you could do:
>>> all = a, b, c, d = {*input().split(',')}
# example
>>> all = a, b, c, d = {1, 2, 3, 4}
>>> all
{1, 2, 3, 4}
>>> a
1
>>> b
2
As pointed out by #Tomerikoo all(iterable) is a built-in function avoid naming your variables same as python builints or keywords.
Another point, if in case you have already done so, in order to get the default behavior of all, you could do:
>>> import builtins
>>> all = builtins.all
# Or, more conveniently, as pointed out by #Martijn Pieters
>>> del all
* is used for iterable unpacking
_ is used for don't care or throwaway or anonymous variable,
as we do not need the variable in the loop. More on this
here.
{*()} is just a fancy way of creating empty sets as python does not have empty set literals. It is recommended to use set()
You could put your calls to input() in a comprehension:
set(input() for i in range(4))
You could use a for loop:
all = set()
for _ in range(4):
all.add(int(input())
print(all)
Don't forget that input gives you a string, so you should convert it to int or any type if needed excplicitly.
"_" in the for loop means that this variable is not important. But you can put whatever name you like.

"ES6-like" Python dict spread [duplicate]

This question already has answers here:
Destructuring-bind dictionary contents
(17 answers)
Closed 4 years ago.
I'm trying to write a function spread in Python 3.6 (I cannot use any newer release), and, so far, I've got something that looks like this:
d = {"a": 1, "b": 2, "c": 3}
a, b, c = spread(d, ['a', 'b', 'c'])
a
>> 1
b
>> 2
c
>> 3
The problem is: there is kind of duplication since the position of the left side must match the keys list on the function's 2nd argument for it to make sense. So, change the order of the keys list, and variable a will hold a different value than d['a']. I need to keep consistency by either
a, b, c = spread(d) # how?
Or spread(d, ???). I'm not considering initializing a, b, c with None and then pass them as a list.
Any thoughts or leads on how to approach this? Is it even possible?
Thanks!
No this isn't really possible. You can't have
a, b, c = spread(d)
and
a, c, b = spread(d)
give the same value to b. This is because the right side of an assignment statement is evaluated first. So spread executes and returns its values before your code knows which order you put them in on the left.
Some googling leads be to believe that by "spread-like syntax for dicts", you're looking for the **dict syntax. See What does ** (double star/asterisk) and * (star/asterisk) do for parameters?
not very pretty, but you can sort of get there doing:
def f1(a, b, c, **_):
print(a)
print(b)
print(c)
d = {"a": 1, "b": 2, "c": 3}
f1(**d)
very different semantics, but posted in the hope it'll inspire something!
as per #phhu's comment, ** in the definition of f1 is a catch-all keyword argument specifier telling Python that all unmatched parameters should be put into a dictionary of the given name, _ in my case. calling as f1(**d) says to unpack the specified dictionary into the function's parameters.
hence if it was used like:
e = {"a": 1, "b": 2, "c": 3, "extra": 42}
f1(**e)
then inside f1 the _ variable would be set to {"extra": 42}. I'm using _ because this identifier is used across a few languages to indicate a throwaway/placeholder variable name, i.e. something that is not expected to be used later.
globals().update(d) does what you ask, but...
It works in the global scope only, locals() is not guaranteed to return a writable dictionary.
It impairs debuggability of your code. If one of the variables set this way ends up with an unexpected value, no search will show you that this is the place the variable is being set.
You could assign the variables to the result of a values() call:
>>> d = {"a": 1, "b": 2, "c": 3}
>>> a,b,c = d.values()
>>> a
1
>>> b
2
>>> c
3
I don't recommend doing this for versions of Python where dict ordering is not guaranteed, but luckily this should work in 3.6 and above.

Howto do reference to ints by name in Python

I want to have a a reference that reads as "whatever variable of name 'x' is pointing to" with ints so that it behaves as:
>>> a = 1
>>> b = 2
>>> c = (a, b)
>>> c
(1, 2)
>>> a = 3
>>> c
(3, 2)
I know I could do something similar with lists by doing:
>>> a = [1]
>>> b = [2]
>>> c = (a, b)
>>> c
([1], [2])
>>> a[0] = 3
>>> c
([3], [2])
but this can be easily lost if one assigns a or b to something instead of their elements.
Is there a simple way to do this?
No, there isn't a direct way to do this in Python. The reason is that both scalar values (numbers) and tuples are immutable. Once you have established a binding from a name to an immutable value (such as the name c with the tuple (1, 2)), nothing you do except reassigning c can change the value it's bound to.
Note that in your second example, although the tuple is itself immutable, it contains references to mutable values. So it appears as though the tuple changes, but the identity of the tuple remains constant and only the mutable parts are changing.
Whatever possible solution you come up with the second last line will always destroy it:
a = 3
This will assign a completely new content to the variable. Unless a stands for a property of an object or something (or a key in a list, as you did in your own example), you won't be able to have a relation between the first and last a.
If you just need the current values to be placed in a tuple on the fly you could use a lambda. You'll have to call c, not just return it or use it, but that may be acceptable in your situation. Something like this:
>>> a = 1
>>> b = 2
>>> c = lambda: (a, b)
>>> c()
(1, 2)
>>> a = 3
>>> c()
(3, 2)
There isn't a way in Python, not only because numbers are immutable, but also because you don't have pointers. Wrapping the value in a list simulates that you have pointers, so that's the best you can do.
class ByRefValue(object):
def __init__(self, value):
self.value = value
Pass it around wherever you like, remembering that you need to access the value member rather than the entire object.
Alternatively, globals().get('a', 0) will return the value of a if it is in the global namespace (or zero if it isn't).
Finally:
import threading
tls = threading.local()
tls.a = 1
If you import tls into every module where you need it, you will access the same value for a on each thread. Depending on how your program is set up, this may be acceptable, ideal or useless.
You can try creating your own pointer class and your own pointer storage object to emulate the system's internal stack.

Categories