Python: array as an argument for a function - python

I wanted to know how to work with an array as a functional argument in Python. I will show a short example:
def polynom(x, coeff_arr):
return coeff_arr[0]+ coeff_arr[1]+x +coeff_arr[2]*x**2
I obviously get the error that 2 positional arguments are needed but 4 were given when I try to run it, can anybody tell me how to do this accept just using (coeff_arr[i]) in the argument of the function?
Cheers

Your question is missing the code you use to call the function, but from the error I infer that you are calling it as polynom(x, coefficient1, coefficient2, coefficient3). Instead you need to either pass the coefficients as a list:
polynom(x, [coefficient1, coefficient2, coefficient3])
Or use the unpacking operator * to define the function as follows, which will take all positional arguments after x and put them into coeff_arr as a list:
def polynom(x, *coeff_arr):
(The unpacking operator can also be used in a function call, which will do the opposite of taking a list and passing its elements as positional arguments:
polynom(x, *[coefficient1, coefficient2, coefficient3])
is equivalent to
polynom(x, coefficient1, coefficient2, coefficient3)
)

Related

How to set a ** parameter in Python

I'm newbie in Python.
I'm using Python 3.7.7 and Tensorflow 2.1.0.
This is my code:
import tensorflow as tf
import tensorflow_datasets as tfds
d = {"name": "omniglot:3.0.0", "data_dir": "d:\\tmp"}
omniglot_builder = tfds.builder("omniglot:3.0.0", builder_init_kwargs=d)
omniglot_builder.download_and_prepare(download_dir="d:\\tmp")
But I get this error:
got an unexpected keyword argument 'builder_init_kwargs'
I want to set data_dir, but I don't know how to do it. I have tried to set download_dir in omniglot_builder.download_and_prepare(download_dir="d:\\tmp") but it stills download it to ~/tensorflow_datasets.
From Tensorflow documentation for tdfs.builder:
**builder_init_kwargs: dict of keyword arguments passed to the DatasetBuilder. These will override keyword arguments passed in name,
if any.
How can I set builder_init_kwargs parameter value?
Based on the docs, which say the tfds.builder method has type:
tfds.builder(
name, **builder_init_kwargs
)
You want to do this:
dict = {"name":"omniglot:3.0.0", "data_dir": "d:\\tmp"}
tfds.builder(**dict)
The ** syntax passes a variable as the kwargs, making the above code equivalent to:
tfds.builder(name="omniglot:3.0.0", data_dir="d:\\tmp")
To set a kwargs argument in python, you have to simply add the ** before the argument itself.
So, this would be your code:
import tensorflow as tf
import tensorflow_datasets as tfds
dict = {"name": "omniglot:3.0.0", "data_dir": "d:\\tmp"}
omniglot_builder = tfds.builder("omniglot:3.0.0", builder_init_kwargs=**dict)
omniglot_builder.download_and_prepare(download_dir="d:\\tmp")
Of course, I am just guessing, because I know what a kwargs argument is, but I am not familiar with tensorflow.
Hope this helps!
It seems you need a little help with argument packing and unpacking.
In the definition of a function or method, you specify the sequence of arguments that will be passed. If you want to have a variable number of input arguments, the mechanism is to "pack" them together into a list or directory. For example say you want to get the sum of all arguments given:
def get_sum(a, b): #only useful for two numbers
return a + b
def get_sum(a,b,c): #only useful for three numbers
return a + b
You would have to have a different definition for every possible number of input arguments. The solution to this is to use the packing operator to pack all arguments given into a list that can be iterated over
def get_sum(*list_of_inputs): # * will pack all subsequent positional arguments into a list
x = 0
for item in list_of_inputs:
x += item
return x
get_sum(1,2,3,4,5,6,7) #returns 28
get_sum() #returns 0
The same can be done for keyword arguments which get packed into a dictionary:
def foo(**keyword_args):
for k in keyword_args:
print(f'{k}: {keyword_args[k]}')
Now when you are using (calling) a function, sometimes you need to be able to "unpack" a list or a dictionary into the function call. The same operator is used to pack and unpack, so it looks very similar:
def foo(a,b,c):
print(f'{a} + {b} = {c}')
arguments = ['spam', 'eggs', 'delicious']
foo(*arguments) #unpack the list of arguments into their required positions
Now finally on to your specific case: the function you are trying to use defines **kwargs in its definition. This means that it will take any subsequent keyword arguments and pack them all up into a dictionary to be used inside the function definition. The practical meaning of this is that you can provide keyword arguments to the function that aren't specifically defined in the function signature (this is particularly common when the function is calling another function and passing along the arguments). If you have already packed up your arguments prior to calling the function, it is easy to unpack them using the same process as shown by Oli: tfds.builder(**dict)

Returning 2 values in a function

In a nutshell, I'm trying to implement the following:
def function_one(value):
...
return a, b
def function_two(a, b):
...
And when I try
function_two(function_one(value))
I get an error message:
"function_two() missing 1 required positional argument: 'b'"
Is there a way to make this work as intended?
Thanks!
You have to unpack the tuple you return into separate arguments:
function_two(*function_one(value))
Another option would be changing function_two to accept a single argument and then unpack it inside the function or use it as-is. Whether this is a good idea or not depends on the context.

Recursion does not work with positional arguments

I am trying to do multiplication recursion (multiplying all the values of a container) in Python. The function receives the elements of the list as positional argument(*n). On execution I receive the error saying "Maximum recursion depth reached". However, the code works fine if I simply use n instead of *n and send the elements in a list.
Code not working:
def multiply(*n):
if n:
return n[0]*multiply(n[1:])
else:
return 1
multiply(5,1,4,9)
Working code:
def multiply(n):
if n:
return n[0]*multiply(n[1:])
else:
return 1
multiply([5,1,4,9])
In the first piece of code, the expression
multiply(n[1:])
is calling multiply with only one argument. Namely, the rest of the list. In order to call it with arguments equal to the contents of the list n[1:], you use the splat operator again, like so:
multiply(*n[1:])
When you tell a function to expect an arbitrary number of positional arguments with *n, you need to accommodate it in that format: with multiple arguments, not with a single iterable that contains all the arguments. If you have a single iterable whose elements should be used as arguments, you have to unpack it with * when you call it. The second function works because it's expecting a single, iterable argument, and you send it a single, iterable argument.
Replace n[1:] with *n[1:].

Call PyObject_CallObject with more than one function argument

If I have a function in Python like this:
def multiply(a, b)
return a * b
How can I call PyObject_CallObject when it will give me an error if I have more than two arguments? There may be a much better way of calling a function from C++ but I am very new to the Python/C API
From the documentation:
PyObject* PyObject_CallObject(PyObject *callable_object, PyObject *args)
Return value: New reference.
Call a callable Python object callable_object, with arguments given
by the tuple args. If no arguments are needed, then args may be NULL.
Returns the result of the call on success, or NULL on failure. This is
the equivalent of the Python expression callable_object(*args).
In other words:
You can pass more than one argument to the function by passing a single tuple containing the arguments. So you'd have to build a tuple containing x and y (i.e. (x, y)) and then pass the tuple as single parameter to PyObject_CallObject(multiply, the_tuple) this will be equivalent to multiply(x, y).
It does not represent the most general call. The most generic call is PyObject_Call which takes two arguments: a tuple of positional arguments and a dictionary of keyword arguments.
There are also the PyObject_CallFunction* functions that are similar to PyObject_CallObject but they avoid having to create the tuple and allow multiple parameters.

Problems passing argument to python function with decorator

I have the following code, which results in this error:
TypeError('smallTask() takes exactly 1 argument (2 given)',)
#task
def master():
count = 0
obj = { 'var1':'val1', 'var2':'val2' }
while count < 10:
subtask('smallTask',obj).apply_async()
count += 1
#task(name='smallTask')
def smallTask(obj):
print obj
Passing a dictionary to a function, I imagine I need to use **kwargs but if I do that, I get the error that the function takes no arguments yet 2 have been supplied.
I assume the issue here is with either the decorator (have a basic understanding of this but not enough to solve the problem) or the subtask function in Celery.
I don't have enough python knowledge to really proceed..could anyone give me an idea of what's happening and how I can pass the smallTask function a dictionary?
You need to pass arguments for a subtask in the args keyword argument, which must be a tuple according to the celery.subtask() documentation:
subtask('smallTask', args=(obj,)).apply_async()
or use the Task.subtask() method on your smallTask task, but again pass the arguments as a tuple:
smallTask.subtask((obj,)).apply_async()
Alternatively, use star arguments with the Task.s() method:
smallTask.s(obj).apply_async()
The subtasks documentation you yourself linked to use a tuple in the examples; arguments and keyword arguments are two pieces of data that Celery has to store for you until it can run that task, then it'll apply those arguments and keyword arguments for you.
But the celery.subtask() function takes more than just the arguments and keyword arguments for your task; it also takes additional options. In order to work with arbitrary arguments (positional or keyword) to your task, and support other arguments that are not passed to your task, the function signature has no choice but to accept positional arguments as an explicit tuple, and keyword arguments as an explicit dictionary.
The Task.s() method does not accept any arguments other than what the task itself would accept, so it does support passing arguments as if you called the task directly. Internally, this uses catch-all arguments: Task.s(*args, **kwarg), and just passes the captured arguments as a tuple and dictionary on to Task.subtask().

Categories