class params:
a = 1
b = 2
c = 3
d = 4
I know it is not possible to pass all four elements of the class to a function, which takes a total of four parameters by just doing
function(class)
Is this the only way
function(a,b,c,d)
or is there an other solution?
I think 4 arguments is acceptable. But if you do want, define a function such as func(a, *args, **kargs) is a choice. Any of the three parameters is optional. I do think there is no need to create such a class to do something that Python has already done, and do much better. For more about function arguments, check here.
You could do something like this:
class params:
a = 1
b = 2
c = 3
d = 4
def __call__(self):
r = dict()
for attr in dir(self):
if not attr.startswith('_'):
r[attr] = getattr(self, attr)
return r
p = params()
function(**p())
However, this is quite hacky and probably not very maintainable: if for some reason the function call for function changes, you need to modify the class also.
(Perhaps using a tuple() instead of dict() for r might be a bit more flexible, but still not a solution I would highly recommend...)
Related
I have a class & method, each with several arguments: my_class(a,b,c).my_method(d,e,f) and I'd like to be able to only change a single argument, while holding the others constant.
Constantly copy-pasting the other constant arguments seems bad, so I'd like to create a new object wrapper_fct where I reference my_class but only provide the one argument I want to change, b, without always having to specify the remaining arguments. How would wrapper_fct() look like?
For example, wrapper_fct(my_class, b1) would return my_class(a,b1,c).my_method(d,e,f), wrapper_fct(my_class, b2) would return my_class(a,b2,c).my_method(d,e,f).
Here's an example in practice:
Loop through just the variable b and evaluate several classes/methods for each new instance of b, and append the results in a list.
I can currently do this in a for loop:
mylist1 = [] # init lists (append results here)
mylist2 = []
mylist2 = []
for b in [1,2,3,4,5]:
mylist1.append( my_class1(a,b,c).my_method(d,e,f) )
mylist2.append( my_class2(a,b,c).my_method(d,e,f) )
mylist3.append( my_class3(a,b,c).my_method(d,e,f) )
...
But it seems better to create a function loop_through_B() and use the wrapper_fct(my_class,b) as specified above. Not sure if it's the ideal solution, but maybe something like:
def loop_through_B(input_class, b_values = [1,2,3,4,5])
mylist = []
for b in b_values:
mylist.append( wrapper_fct(input_class,b) )
return mylist
loop_through_B(my_class1) # would I also have to specify the method here as well?
loop_through_B(my_class2)
loop_through_B(my_class3)
Extra Question: how would I add the ability to vary method arguments, or even multiple class & method arguments?
After #chepner pointed me in the right direction, I think the best solution is to use the lambda function:
wrapper_fct = lambda b: my_class1(a,b,c).my_method(d,e,f)
In this case, I can vary b as much as I want while holding the class arguments a,c, and method arguments d,e,f constant. Note that with lambda functions, I can also vary the method arguments and/or the class arguments. For example:
wrapper_fct_multiple = lambda b, e: my_class1(a,b,c).my_method(d,e,f)
It is also possible to do this with functools.partial, but it's not obvious to me how I would specify both class & method arguments with functools.
Anyway, here is the solution implementation using lambda:
# define the "wrapper function" outside the loop
wrapper_fct = lambda b: my_class1(a,b,c).my_method(d,e,f)
# define the function I want to use to loop through B:
def loop_through_B(class_wrapper, b_values)
mylist = []
for b in b_values:
mylist.append( class_wrapper(b) )
return mylist
# run:
loop_through_B(wrapper_fct, b_values=[1,2,3,4,5])
# Can make additional wrapper_fct2, wrapper_fct3, for my_class2, my_class3 ...
You can pass the method a dictionary of arguments, and change what the method sees by selectively updating it when calling the method.
Here's what I mean:
class MyClass:
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def my_method(self, kwargs):
return sum((kwargs[key] for key in kwargs.keys()))
def __repr__(self):
classname = type(self).__name__
args = ', '.join((f'{v!r}' for v in (self.a, self.b, self.c)))
return f'{classname}({args})'
instance = MyClass('a','b','c')
print(instance) # -> MyClass('a', 'b', 'c')
kwargs = dict(d=1, e=2, f=3)
print(instance.my_method(kwargs)) # -> 6
print(instance.my_method(dict(kwargs, e=38))) # -> 42
Firstly I have already seen a number of similar questions, although they were not exactly my question. I am already familiar with *args and **kwargs.
Question Explanation:
I usually use positional arguments when calling a function. However, I often find myself needing to pass a plethora of arguments into a function, so using positional arguments has gotten quite burdensome. I have also found myself needing to pass a varied number of variables to a function that can accept more or other variables if needed.
How do I pass many arguments into a function that is able to take a varied number of arguments ?
I have tried to create an example that is as basic as possible. The functions just perform some arithmetic operations on some variables, and then prints them out.
a = 10
b = 20
c = 30
def firstFunction(*args):
d = a *2
e = b /2
f = c +2
x = d -10
y = e -10
z = f -10
h = 1 #or 2
secondFunction(d,e,f,h)
thirdFunction(x,y,z,h)
def secondFunction(d,e,f,h):
if h == 1:
print d
print e
print f
def thirdFunction(x,y,z,h):
if h == 2:
print x
print y
print z
firstFunction(b,c,a)
And the results produced, as expected, for h=1 and h=2 respectively, are:
20
10
32
10
0
22
Now lets say I want to combine the second and third functions together, so I need to just call one function instead of two. The function would be, in this case:
def combinedFunction(d,e,f,h,x,y,z):
if h == 1:
print d
print e
print f
if h == 2:
print x
print y
print z
and would be called by: combinedFunction(d,e,f,h,x,y,z). As you can imagine, this can get extremely annoying for much more complicated functions. Also I am passing many different arguments that are not going to be used at all, and each of them has to be first declared. For example, in the example, if h = 1, x,y and z have to still be passed into the function, and maybe the value of one of them hasn't been determined as yet(in this simple case it is). I can't use 'combinedFunction(*args)' because not every argument is globally defined.
TLDR:
Basically I want the following:
def someFunction(accepts any number of arguments **and** in any order):
# does some stuff using those arguments it received at the time it was called
# it can accept many more parameters if needed
# it won't need to do stuff to a variable that hasn't been passed through
and this function is called by:
someFunction(sends any number of arguments **and** in any order)
# can call the function again using different arguments and a
# different number of arguments if needed
Can this be easily achieved?
Using global variables from inside a function is generally a bad approach. Instead of it you can use **kwargs this way:
def firstFunction(**kwargs):
d = kwargs.get('a') * 2
secondFunction(d=d, **kwargs)
thirdFunction(e=1, **kwargs)
def secondFunction(d, **kwargs):
print d
print kwargs.get('a')
def thirdFunction(**kwargs):
print kwargs.get('e')
firstFunction(a=1, b=3, q=42) # q will not be used
You could use a dict to pass the data to the functions, but it does makes it less intuitive when programming.
Each function could the convert the dict arguments as you please.
def func_a(input_dict):
a = input_dict["a"]
b = input_dict["b"]
print(a+b)
def func_b(input_dict):
c = input_dict["c"]
d = input_dict["d"]
print(c+d)
def combined_func(input_dict):
func_a(input_dict)
func_b(input_dict)
This is quite similar to kwargs, so it might not be what you are looking for.
If I understood correctly what you are looking for:
def something(*args):
for i in args:
print(i)
some_args = range(10)
something(*some_args)
print('-----------')
something(*some_args[::-1]) # reverse order
Output:
0
1
2
3
4
5
6
7
8
9
-----------
9
8
7
6
5
4
3
2
1
0
I am trying to create a set of functions in python that will all do a similar operation on a set of inputs. All of the functions have one input parameter fixed and half of them also need a second parameter. For the sake of simplicity, below is a toy example with only two functions.
Now, I want, in my script, to run the appropriate function, depending on what the user input as a number. Here, the user is the random function (so the minimum example works). What I want to do is something like this:
def function_1(*args):
return args[0]
def function_2(*args):
return args[0] * args[1]
x = 10
y = 20
i = random.randint(1,2)
f = function_1 if i==1 else function_2
return_value = f(x,y)
And it works, but it seems messy to me. I would rather have function_1 defined as
def function_1(x):
return x
Another way would be to define
def function_1(x,y):
return x
But that leaves me with a dangling y parameter.
but that will not work as easily. Is my way the "proper" way of solving my problem or does there exist a better way?
There are couple of approaches here, all of them adding more boiler-plate code.
There is also this PEP which may be interesting to you.
But 'pythonic' way of doing it is not as elegant as usual function overloading due to the fact that functions are just class attributes.
So you can either go with function like that:
def foo(*args):
and then count how many args you've got which will be very broad but very flexible as well.
another approach is the default arguments:
def foo(first, second=None, third=None)
less flexible but easier to predict, and then lastly you can also use:
def foo(anything)
and detect the type of anything in your function acting accordingly.
Your monkey-patching example can work too, but it becomes more complex if you use it with class methods, and does make introspection tricky.
EDIT: Also, for your case you may want to keep the functions separate and write single 'dispatcher' function that will call appropriate function for you depending on the arguments, which is probably best solution considering above.
EDIT2: base on your comments I believe that following approach may work for you
def weigh_dispatcher(*args, **kwargs):
#decide which function to call base on args
if 'somethingspecial' in kwargs:
return weight2(*args, **kwargs)
def weight_prep(arg):
#common part here
def weight1(arg1, arg2):
weitht_prep(arg1)
#rest of the func
def weight2(arg1, arg2, arg3):
weitht_prep(arg1)
#rest of the func
alternatively you can move the common part into the dispatcher
You may also have a function with optional second argument:
def function_1(x, y = None):
if y != None:
return x + y
else:
return x
Here's the sample run:
>>> function_1(3)
3
>>> function_1(3, 4)
7
Or even optional multiple arguments! Check this out:
def function_2(x, *args):
return x + sum(args)
And the sample run:
>>> function_2(3)
3
>>> function_2(3, 4)
7
>>> function_2(3, 4, 5, 6, 7)
25
You may here refer to args as to list:
def function_3(x, *args):
if len(args) < 1:
return x
else:
return x + sum(args)
And the sample run:
>>> function_3(1,2,3,4,5)
15
I want to do something like this:
f[frozenset((1,3,4))] = 5
f[frozenset((1,))] = 3
but it's just painful to type these all the time, is there anyway to have alias for this? I know in C++ it's possible to have a helper function which return a reference so you can just type:
F(1,3,4) = 5
F(1) = 3
with F as a helper function. Thanks very much!
I think that this can really only be achieved via a subclass:
class FrozenSetDict(dict):
def __setitem__(self,idx,value):
try:
dict.__setitem__(self,frozenset(idx),value)
except TypeError:
dict.__setitem__(self,frozenset((idx,)),value)
d = FrozenSetDict()
d[1,2,3] = 4
d[1] = 5
print d
yields:
{frozenset([1, 2, 3]): 4, frozenset([1]): 5}
This introduces an asymmetry between __getitem__ and __setitem__ which could easily be fixed by re-defining __getitem__ in the same way.
This might seem a little messy -- Indeed it is. Why require a subclass? That just makes it harder to put non-frozenset objects into your dictionary as keys. You could easily use this recipe though to create a proxy object which will do this with your dict:
#I don't like the name of this class -- I'm open to suggestions :)
class FrozenSetProxy(object):
def __init__(self,obj):
self.obj = obj
def __setitem__(self,idx,value):
try:
self.obj[frozenset(idx)] = value
except TypeError:
self.obj[frozenset((idx,))] = value
def __getitem__(self,idx):
try:
return self.obj[frozenset(idx)]
except TypeError:
return self.obj[frozenset((idx,))]
d = dict()
F = FrozenSetProxy(d)
F[1,2,3] = 4
F[1] = 5
print d
print F[1]
There's nothing like a C++ reference in Python, and the syntax you use is illegal to boot (in the words of the parser: can't assign to function call). You could emulate it with an object or subclass dict to customize its __getitem__. But there's a simpler and less intrusive way: Pass the value to the helper too, and let it handle the assignment:
def blah(f):
def F(*args, value):
f[frozenset(args)] = value
F(1, 3, 4, value=5)
F(1, value=3)
Note that this uses a Python 3 feature, keyword-only parameters. If you need it to work with Python 2, you can emulate the call syntax by accepting **kwdargs:
def F(*args, **kwds):
# optional: check that no other keyword arguments were passed
f[frozenset(args)] = kwds['value']
I understand that functions can have attributes. So I can do the following:
def myfunc():
myfunc.attribute += 1
print(myfunc.attribute)
myfunc.attribute = 1
Is it possible by any means to make such a function behave as if it were an instance? For example, I'd like to be able to do something like this:
x = clever_wrapper(myfunc)
y = clever_wrapper(myfunc)
x.attribute = 5
y.attribute = 9
x() # I want this to print 6 (from the 5 plus increment)
y() # I want this to print 10 (from the 9 plus increment)
As it stands, there is only one "instance" of the function, so attribute only exists once. Modifying it by either x or y changes the same value. I'd like each of them to have their own attribute. Is that possible to do at all? If so, can you provide a simple, functional example?
It is important that I be able to access attribute from inside of the function but have the value of attribute be different depending on which "instance" of the function is called. Essentially, I'd like to use attribute as if it were another parameter to the function (so that it could change the behavior of the function) but not pass it in. (Suppose that the signature of the function were fixed so that I cannot change the parameter list.) But I need to be able to set the different values for attribute and then call the functions in sequence. I hope that makes sense.
The main answers seem to be saying to do something like this:
class wrapper(object):
def __init__(self, target):
self.target = target
def __call__(self, *args, **kwargs):
return self.target(*args, **kwargs)
def test(a):
return a + test.attribute
x = wrapper(test)
y = wrapper(test)
x.attribute = 2
y.attribute = 3
print(x.attribute)
print(y.attribute)
print(x(3))
print(y(7))
But that doesn't work. Maybe I've done it incorrectly, but it says that test does not have attribute. (I'm assuming that it's because wrapper actually has the attribute.)
The reason I need this is because I have a library that expects a function with a particular signature. It's possible to put those functions into a pipeline of sorts so that they're called in order. I'd like to pass it multiple versions of the same function but change their behavior based on an attribute's value. So I'd like to be able to add x and y to the pipeline, as opposed to having to implement a test1 function and a test2 function that both do almost exactly the same thing (except for the value of the attribute).
You can make a class with a __call__ method which would achieve a similar thing.
Edit for clarity: Instead of making myfunc a function, make it a callable class. It walks like a function and it quacks like a function, but it can have members like a class.
A nicer way:
def funfactory( attribute ):
def func( *args, **kwargs ):
# stuff
print( attribute )
# more stuff
return func
x = funfactory( 1 )
y = funfactory( 2 )
x( ) # 1
y( ) # 2
This works because the functions are closures, so they will grab all local variables in their scope; this causes a copy of attribute to be passed around with the function.
class Callable(object):
def __init__(self, x):
self.x = x
def __call__(self):
self.x += 1
print self.x
>> c1 = Callable(5)
>> c2 = Callable(20)
>> c1()
6
>> c1()
7
>> c2()
21
>> c2()
22
A generator might be an alternate solution here:
def incgen(init):
while True:
init += 1
print init
yield
x = incgen(5)
y = incgen(9)
x.next() # prints 6
y.next() # prints 10
y.next() # prints 11
x.next() # prints 7
You can't dig back in to the generator and manipulate the data though.
#!/usr/bin/env python
# encoding: utf-8
class Callable(object):
attribute = 0
def __call__(self, *args, **kwargs):
return self.attribute
def main():
c = Callable()
c.attribute += 1
print c()
if __name__ == '__main__':
main()