Passing same argument names for a function in Python - python

In python, I have a class with functions printing certain outputs, of which each has default parameters but those can be changed, like
def func1(self,a='a', b='b'):
return something
def func2(self,c='c', d='d'):
return something
Lots of other functions of a similar kind too.
I created another function that can take those functions with parameters and can do something with them, ie
def foo(self, default,*args,**kwargs):
df=self.df
if default == True:
args = self.list1
else:
for fname in args:
getattr(self, fname)(*kwargs)
df['appending_array'] = df.apply(lambda...
In the effect I'd like to be able to call something like
object.foo(False, func2,func11,d='z')
Unfortunately in the loop, when I change d to 'z', it changes the first argument of each function that is iterated, instead of the actual parameter d from the function I want.
Is there a possibility to either rearrange it so I can pass the original parameters of each passed function, or configure **kwarg so it can refer to the original parameters' names of each function?
Many thanks for any help/advice

So, if I understand correctly, you want to write it such that if, for example, d is present in the foo call, it is solely passed to all functions in args that have d as an input argument (in this case, func2)?
If so, then you want to determine all input arguments that each function can take.
Luckily, Python has a function that allows you to do just that: inspect.signature.
So, you could rewrite it like this (I am assuming you wanted to pass the names of the functions as args, not the actual functions themselves):
from inspect import signature
def foo(self, default, *args, **kwargs):
df=self.df
if default:
args = self.list1
else:
for fname in args:
# Obtain function
func = getattr(self, fname)
# Obtain signature of this function
f_sig = signature(func)
# Create dict with values for this function, updating with values from kwargs
# Make sure to skip 'self'
f_kwargs = {argname: kwargs.get(argname, argpar.default)
for argname, argpar in f_sig.parameters.items()
if (argname != 'self')}
# Call this function
f_out = func(**f_kwargs)
# Perform some operations on the output of this function
df['appending_array'] = df.apply(lambda...
Keep in mind that this only works if every function that is ever passed as args solely takes optional arguments.
If one argument is mandatory, it must be passed to kwargs.
PS: I am however unsure what the use of the args = self.list1 for if default is.
Because that part is incompatible with everything else.

You need to unpack the kwargs with dictionary unpacking using two stars:
getattr(self, fname)(**kwargs)
And I think you also need to provide self as the first argument since these are unbound methods:
getattr(self, fname)(self,**kwargs)

Related

passing keyword argument to inner function when outer function has keyword argument with the same name

I have two functions defined roughly like this:
def func_inner(bar=0):
print('inner bar:', bar)
def func_outer(bar=-1, *args, **kwargs):
print('outer bar:', bar)
func_inner(*args, **kwargs)
Is there a way to call func_outer and provide it with two values of bar - one for func_outer, and the other to be passed over to func_inner? Calling func_outer(bar=1,bar=2) clearly does not work.
It's possible to overcome the issue by specifying bar values as positional arguments, like func_outer(1,2), but I'd like to specify them as keyword arguments for both functions.
Is there a solution entirely at the caller's side (ie. without altering the functions)?
No, there is none
You cannot pass two arguments with the same name to a function. Thus, you will not be able to have the key "bar" in kwargs.
Thus you cannot pass this argument without modifying the two functions.
There may be a more adapted way to what you’re doing
This is the kind of code that pops up in a decorator. In this kind of case, you may want to make the outer function currified.

Call Python function passed as parameter without knowing if it takes any parameters

I have a project in which I need to do some things, call one of a few functions, and then do some other things:
def doCommand(function, parameter_for_function):
# do a thing
function(parameter_for_function)
# do a thing
My problem is that I don't know if the function I will be passing will require a parameter of its own!
How can I allow my function to call both functions that have no parameters, and functions that have one?
The preferred method of handling this is probably along these lines:
def doCommand(function, *args, **kwargs):
# do a thing
function(*args, **kwargs)
# do a thing
*args and **kwargs allow arbitrary arguments to be passed along to a method. As the name implies, these allow you to call doCommand with an arbitrary number of arguments and keyword arguments as well that get passed onto function.
I suggest explicitly saying that the function you take is one that's called with no parameters:
from typing import Callable
def doCommand(function: Callable[[], None]) -> None:
# do a thing
function()
# do a thing
If the function needs parameters, it's then explicitly the responsibility of the doCommand caller to provide them, e.g. within a lambda expression:
def foo(param) -> None:
print("foo: ", param)
doCommand(lambda: foo("bar"))
or a named wrapper function:
def foobar() -> None:
foo("bar")
doCommand(foobar)

Using the = sign in variables for method calls

When doing a method call in Python, in which cases do you use the = sign when entering arguments?
I noticed sometimes it's:
object.method(argument1 = ... , argument2 = ...)
and other times it's
object.method(... , ...)
Thanks in advance!
That types of arguments are called keyword arguments, probably you've seen that as (kwargs).
In a function you can pass 2 types of args, positional arguments and keyword-arguments.
def function(arg):
So you have to pass inside this function a positional argument, this means that you have to pass only the value for that arg like
function(1) or maybe a string function("hello") or maybe a list and so on; This type of arg is called position arg.
When you have to specify the name of the variable inside the brackets of a function call like function(name = "John"), you are using the keyword args.
If you want to understand more on this topic, i suggest you to see the unpack operator in Python (*) and then *args and **kwargs.
2 words to say what *args and **kwargs do in Python, they allows you to specify an unlimited number of arguments inside a function call.
def function(*args, **kwargs):
pass
function(1,2,3,"hello",[1,2,3],name="John",age=5)

How to get variable names of function call

I am going to to write a decorator which evaluates the actual names (not their value) of the variables that are passed to the function call.
Below, you find a skeleton of the code which makes it a bit clearer what I want to do.
import functools
def check_func(func):
# how to get variable names of function call
# s.t. a call like func(arg1, arg2, arg3)
# returns a dictionary {'a':'arg1', 'b':'arg2', 'c':'arg3'} ?
pass
def my_decorator(func):
#functools.wraps(func)
def call_func(*args, **kwargs):
check_func(func)
return func(*args, **kwargs)
return call_func
#my_decorator
def my_function(a, b, c):
pass
arg1='foo'
arg2=1
arg3=[1,2,3]
my_function(arg1,arg2,arg3)
You can't really have what you are asking for.
There are many ways of calling a function, where you won't even get variable names for individual values. For example, what would the names when you use literal values in the call, so:
my_function('foo', 10 - 9, [1] + [2, 3])
or when you use a list with values for argument expansion with *:
args = ['foo', 1, [1, 2, 3]]
my_function(*args)
Or when you use a functools.partial() object to bind some argument values to a callable object:
from functools import partial
func_partial = partial(my_function, arg1, arg2)
func_partial(arg3)
Functions are passed objects (values), not variables. Expressions consisting of just names may have been used to produce the objects, but those objects are independent of the variables.
Python objects can have many different references, so just because the call used arg1, doesn't mean that there won't be other references to the object elsewhere that would be more interesting to your code.
You could try to analyse the code that called the function (the inspect module can give you access to the call stack), but then that presumes that the source code is available. The calling code could be using a C extension, or interpreter only has access to .pyc bytecode files, not the original source code. You still would have to trace back and analyse the call expression (not always that straightforward, functions are objects too and can be stored in containers and retrieved later to be called dynamically) and from there find the variables involved if there are any at all.
For the trivial case, where only direct positional argument names were used for the call and the whole call was limited to a single line of source code, you could use a combination of inspect.stack() and the ast module to parse the source into something useful enough to analyse:
import inspect, ast
class CallArgumentNameFinder(ast.NodeVisitor):
def __init__(self, functionname):
self.name = functionname
self.params = []
self.kwargs = {}
def visit_Call(self, node):
if not isinstance(node.func, ast.Name):
return # not a name(...) call
if node.func.id != self.name:
return # different name being called
self.params = [n.id for n in node.args if isinstance(n, ast.Name)]
self.kwargs = {
kw.arg: kw.value.id for kw in node.keywords
if isinstance(kw.value, ast.Name)
}
def check_func(func):
caller = inspect.stack()[2] # caller of our caller
try:
tree = ast.parse(caller.code_context[0])
except SyntaxError:
# not a complete Python statement
return None
visitor = CallArgumentNameFinder(func.__name__)
visitor.visit(tree)
return inspect.signature(func).bind_partial(
*visitor.params, **visitor.kwargs)
Again, for emphasis: this only works with the most basic of calls, where the call consists of a single line only, and the called name matches the function name. It can be expanded upon but this takes a lot of work.
For your specific example, this produces <BoundArguments (a='arg1', b='arg2', c='arg3')>, so an inspect.BoundArguments instance. Use .arguments to get an OrderedDict mapping with the name-value pairs, or dict(....arguments) to turn that into a regular dictionary.
You'll have to think about your specific problem differently instead. Decorators are not meant to be acting upon the code calling, they act upon the decorated object. There are many other powerful features in the language that can help you deal with the calling context, decorators are not it.

What is the "**params" syntax in a Python method definition?

So I am trying out the new python code for the google app engine search library and I came across a weird syntax. This was:
cls_createDocument(**params)
where params was a dictionary.
The function this refers to is:
#classmethod
def _createDocument(
cls, pid=None, category=None, name=None, description=None,
category_name=None, price=None, **params)
My questions is, what does the **params signify and what does that do to the object?
Thanks!
Jon
Consider a function with default arguments:
def func(foo=3):
print(foo)
The structure of the arguments is (in principle) very similar to a dictionary. The function foo has (essentially) a dictionary of default arguments (in this case {'foo':3}). Now, lets say that you don't want to use the keyword in the function call, but you want to use a dictionary instead -- then you can call foo as:
d = {"foo":8}
func(**d)
This allows you to dynamically change what arguments you are passing to the function func.
This become a little more interesting if you try the following:
d = {"foo":8, "bar":12}
func(**d)
This doesn't work (it is equivalent to foo(foo=8, bar=12), but since bar isn't a valid argument, it fails).
You can get around that problem by giving those extra arguments a place to go inside the definition of foo.
def func( foo=3, **kwargs ):
print(foo,kwargs)
Now, try:
d = {"foo":8, "bar":12}
func(**d) #prints (8, {'bar':12})
All the extra keyword arguments go into the kwargs dictionary inside the function.
This can also be called as:
func(foo=8, bar=12)
with the same result.
This is often useful if funcA calls funcB and you want funcA to accept all of the keywords of funcB (plus a few extra) which is a very common thing when dealing with classes and inheritance:
def funcA(newkey=None,**kwargs):
funcB(**kwargs)
Finally, here is a link to the documentation
The **params parameter represents all the keyword arguments passed to the function as a dictionary.

Categories