python named arguments auto-naming - python

So if I have a function which takes loads of named arguments:
def foo(a = 1, b = 2, c = 3, d = 4, e = 5) # etc...
pass
and I'm calling it with all the arguments having exactly the same names as in the definition:
a = 0
b = 0
c = 0
d = 0
e = 0
is there a way to avoid doing this?
foo(e = e, b = b, d = d, a = a, c = c)
and just do this:
foo(e, b, d, a, c)
?
I guess I can do this:
foo(a, b, c, d, e)
but what if the arguments have complicated names and I can't remember the order of them by heart?

Well, you could do something like:
def foo(a, b, c, d):
print a, b, c, d
d = 4
b = 2
c = 3
a = 1
import inspect
foo(*[locals().get(arg, None) for arg in inspect.getargspec(foo).args])
but I'm not sure I can recommend this... In practice I'd use a dictionary of arguments:
foo_params = {
'd' : 4,
'b' : 2,
'c' : 3,
'a' : 1
}
foo(**foo_params)
of write a wrapper for foo which uses less arguments.

Python's argument passing mechanisms are extremely flexible. If they're not flexible enough, this seems like a design smell to me ...
possible smell: too many arguments to a function. Solutions: split into multiple functions, pass some args together in a dictionary or object.
possible smell: bad variable names. Solution: give variables more descriptive names.
Or just bite the bullet, figure out the correct order, and keep it simple.

If changing the function is not an option for you but you have the liberty to change the methodology in which you are assigning value to the parameters passed, here is a example code that might be helpful to you. This used orderdict to preserv the
Given
>>> def foo(a = 1, b = 2, c = 3, d = 4, e = 5):
print "a={0},b={1},c={2},d={3},e={4}".format(a,b,c,d,e)
the you can do
>>> var=dict()
>>> var['c']=12
>>> var['a']=10
>>> var['b']=11
>>> var['e']=14
>>> foo(**var)
a=10,b=11,c=12,d=4,e=14
Note, this answer is similar to what was proposed by #thg435 but you are
Not using inspect to hack the arguments a function expects.
Not looking through the local/global dictionary.
Supports missing arguments which defaults to what is the default argument.
And off-course you do not have to remember the order.
And you don;t even have to pass the variables as parameters. Just pass the dictionary.

You can do the following:
def func(a=1, b=2, c=3, **kw):
print a,b,c
a = 11
b = 22
c = 33
func(**locals())

Calling a 5-argument function with a completely different set of arguments each time is pretty rare. If, in practice, you're using the same a, c, and e args most of the time and calling with different b and d args (for example), you can create a class to help you with this:
class FooWrapper(object):
def __init__( self, commonA, commonC, commonE ):
self.a = commonA
self.c = commonC
self.e = commonE
def invokeFoo( self, _b, _d ):
foo( a=self.a, b = _b, c = self.c, d = _d, e = self.e )
w = FooWrapper( 1, 2, 3 )
w.invokeFoo( 4, 5 ) # calls foo( 1, 4, 2, 5, 3 )
w.invokeFoo( 6, 7 ) # calls foo( 1, 6, 2, 7, 3 )

Related

Flexibly unpack returned tuple in python

In Python, I can return multiple values from a function like this:
def myfunc():
return 1, 2, 3
When I call this function, I need to unpack the values like this:
a, b, c = myfunc()
# a = 1, b = 2, c = 3
If I try this, I'll get the whole tuple:
a = myfunc()
# a = (1, 2, 3)
Is there anyway I can make the code above make a = 1 without changing the way the function is called? I realize I can do something like a, _, _ = myfunc(), but I don't want to change the way the function is called. I can only change the function definition.
In other words I want something like this:
a, b, c = myfunc()
# a = 1, b = 2, c = 3
a, b = myfunc()
# a = 1, b = 2
a = myfunc()
# a = 1
Thanks for your help!
You can use the star notation:
a, *_ = myfunc()
This assigns 1 to a and the rest of the tuple to the
"anonymous" variable _.

Changing Method called without new instance of Python Class

I'm new to Python OOP and for the purpose of this question I have simplified my problem to this:
class Foo:
def __init__(self, a, b):
self.a = a
self.b = b
def add(self):
# some arbitrary change
return self.a + self.b
def subtract(self):
# some arbitrary change
return self.a - self.b
a = Foo(a=1, b=2).add()
b = Foo(a=1, b=3).subtract()
So I have an object, which has 2 methods which do different things, in order for me to get some output, I have created 2 separate instances of Foo as the value b has changed.
Is there a way for me to just dynamically set b and the obj.method() without just listing them one after the other? I.E: some sort of generic class that I can use to dynamically set the attributes and the methods that are present in the object? or is there anything built in I can use...
Edit
Here is another example:
class Foo:
def __init__(self, a, b):
self.a = list(a)
self.b = list(b)
def method1(self):.
# some arbitrary change in data
return self.a * 2
def method2(self):
return self.b + [5, 6, 4]
a = Foo(a=[1, 2, 3], b=[]).method1()
b = Foo(b=[1, 2, 3], a=[]).method2()
print(a)
print(b)
So here, the input list changes based on the method called, is there a way for me to package this up so I could feed just one instance some data and then it 'knows' that list a is for method1(), list b is for method2() - I want to use the word reflection but I feel like that might not be accurate.
Again I'm new to OOP so any advice is appreciated
class Foo:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
fo = Foo()
a = fo.add(1,2)
b = fo.subtract(1,3)
you don't need 2 instances of Foo to achieve this.
Just do something like this:
foo = Foo(a = 1, b = 2)
# Perform addition (now 'a' is 1 and 'b' is 2)
a = foo.add()
# Change 'b'
foo.b = 3
# Now perform subtraction (now 'a' is 1 and 'b' is 3)
b = foo.subtract()

How to pass kwargs in ruby function? Based on python example

Env:
$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
Question:
There is example on python:
def any_func(a, b=2, c = 0, d=None):
return c
print(any_func(25, c=30)) # will print 30
How can I do the same on ruby?
How "kwargs" is called on ruby? I would like to know it for further searching.
I tried following code on ruby:
#!/usr/bin/env ruby
def any_func(a, b = 2, c = 0, d = nil)
c
end
puts any_func(25, c = 30) # will print 0, 30 is expected
In Ruby you need to use hash-like syntax
def any_func(a, b: 2, c: 0, d: nil)
c
end
any_func(25, c: 30)
def fun(a=1) only sets a default value. And if you have any_func(25, c = 30), it means "call any_func with two arguments, 25 and result of expression c = 30 (that is 30)"
Read more
Ruby has both positional arguments and keyword arguments. Differing from Python, you can't mix them but have to declare them with their respective syntax and provide them accordingly in method calls.
def any_func(a, b: 2, c: 0, d: nil)
c
end
puts any_func(25, c: 30)
You can refer to the official syntax documentation to learn more about how to declare and call methods with arguments.
As for your own example of calling your any_func method with any_func(25, c = 30), what happened here is that you have created the local variable c in the calling context with the value 30. You have also passed this value to the second (optional) argument b of your method.

Make function change variables in main code

I want to make a function that does the following:
def func(*args):
for arg in args:
arg+=1
a = 5
b = 6
c = 7
func(a,b,c)
print("%i,%i,%i"%(a,b,c))
I want it to return:
6,7,8
How would I do this?
You can’t! Ta-da.
Python does not support pass-by-reference in any form. Return values instead:
def func(*args):
return [arg + 1 for arg in args]
a = 5
b = 6
c = 7
a, b, c = func(a, b, c)
print("%i,%i,%i" % (a, b, c))
You can't do this, because when you pass in a variable, it takes it in as its value, not as its variable.
Instead, return the value:
def func(*args):
args = list(args)
for i in range(len(args)):
args[i]+=1
return args
a = 5
b = 6
c = 7
a, b, c = func(a,b,c)
print("%i,%i,%i"%(a,b,c))
Which outputs:
>>> print("%i,%i,%i"%(a,b,c))
6,7,8
>>>
You can't, at least not with integer values. Integers are immutable, so you can't change their values, and a function doesn't have access to the namespace of its caller, so you can't rebind the variables (i.e., assign a new value to the variable a outside the function). See this question and various others about what you can and cannot do to affect variables in functions.
If your variables are mutable types like lists, you can achieve a similar effect by mutating the list's value:
def func(*args):
for arg in args:
arg[0] += 1
a = [5]
b = [6]
c = [7]
func(a,b,c)
print("%i,%i,%i"%(a,b,c))
However, you should think about why you want to do this. It may be better to simply return the values and assign them outside the function.
3 posts to tell "You can't" But "Impossible n'est pas français".
Python is the lingua franca of programming languages.
So it's possible:
#!/usr/bin/env python
def func(args):
for i in range(len(args)):
args[i] += 1
abc = [5, 6, 7]
func(abc)
print("%i,%i,%i" % tuple(abc))
actually prints
6,7,8
You can't do it easily because Python doesn't pass immutable objects such as integers by reference. However if you pass the function the names of objects in the current scope, you can achieve your goal like this:
import sys
def func(*args):
namespace = sys._getframe(1).f_globals # caller's globals
for arg in args:
namespace[arg] += 1
a = 5
b = 6
c = 7
func('a','b','c') # note variable *names* passed to function
print("%i,%i,%i" % (a,b,c)) # -> 6,7,8

What is the most pythonic way to call a method with optional parameters?

Let's say I have a method with a few optional parameters.
def foo(a, b=1, c=2, d=3)
How do I go about calling it so that if my variables are None or empty strings the defaults are used?
Conditionals like the following seems like a horrible solution:
if b and not c and d:
foo(myA, b = myB, d = myD)
elif b and not c and not d:
...
In Java I'd jump for a factory, but it seems like that's what defaults are supposed to avoid in this case.
I would change foo so it replaces empty values with default ones.
def foo(a, b=None, c=None, d=None):
if not b: b = 1
if not c: c = 2
if not d: d = 3
Note that this will treat all "false-y" values as defaults, meaning not only None and '' but also 0, False, [], etc. Personally I would tighten the interface up and use None and only None as a default value.
def foo(a, b=None, c=None, d=None):
if b is None: b = 1
if c is None: c = 2
if d is None: d = 3
Though I agree that changing the method is a better idea, here's an alternative that changes the calling part by using a dict of arguments, which is filtered and then unpacked:
d = {'b': myB, 'd': myD}
foo(myA, **{k: d[k] for k in d if d[k]})
Of course if d[k] can be replaced by if d[k] not in {None, ''} for example, which has a slightly different meaning (as pointed out by others).
If you want to catch ONLY None and '':
def foo(a, b, c, d):
blacklist = set([None, ''])
if b in blacklist:
b = 1
if c in blacklist:
c = 2
if d in blacklist:
d = 3
If you want to catch all values v such that bool(v) is False, then:
def foo(a, b, c, d):
b = b or 1
c = c or 2
d = d or 3
Or you could decorate the function with another function that does the assertions for you (which may or may not be overkill, based on your use case)
You could call a function that filters out the variables you don't want passed down
def arg_filter(**kw):
return dict((k,v) for k,v in kw.items() if v not in (None, ''))
foo(**arg_filter(a=1,b=None,c=''))
Not fully tested, but should act as a base:
import inspect
from functools import wraps
def force_default(f):
#wraps(f)
def func(*args, **kwargs):
ca = inspect.getcallargs(f, *args, **kwargs)
da = inspect.getargspec(f)
dv = dict(zip(reversed(da.args), reversed(da.defaults)))
for k, v in ca.iteritems():
if v is None or v == '':
ca[k] = dv[k]
return f(**ca)
return func
#force_default
def foo(a, b=1, c=2, d=3):
print a, b, c, d
foo(6, '', None, 'rabbit!')
# 6 1 2 rabbit!

Categories

Resources