nargin functionality (from Matlab) in Python - python

I am attempting to translate these lines of code:
error(nargchk(3, 4, nargin))
if nargout > 2, error('too many output parameters'); end
I want to translate it into Python. I have yet to find an equivalent for error (although I could perhaps just throw an exception here?), nargchk and nargin. Can anybody provide any input as to how this line of code can be translated?

There is nothing equivalent to nargin and nargchk because Python doesn't need it. You can define optional arguments in Python directly in the function definition. So for example:
def myfunc(a=1, b=3):
In this case it lets you set a and b to whatever you want, but if you don't define them it uses the default values you specified. So if you call myfunc(), it sets a to 1 and b to 3. If you call myfunc(5) it sets a to 5 and b to 3. You can also call by name, such as myfunc(b=10), which sets a to 1 and b to 10.
If you want argument to be required, just don't assign anything to them in the function definition. So in the following case, a is required while b is optional:
def myfunc(a, b=3):
If you want a user to be able to specify an arbitrary number of arguments, you can use *args (where args can be whatever variable you want. So this:
def myfunc(a=1, b=3, *args):
Will assign the first argument to a, the second argument to b, and any remaining arguments to args. You can also do this with arguments defined by name using **kwargs (again, kwargs can be any variable name). So this:
def myfunc(a=1, b=3, *args, **kwargs):
Does the same thing as the previous one, but any arguments provided by name other than a and b will go in kwargs (which is a dictionary).
So you don't need nargin and nargchk, generally you can just define the arguments you need and set default values for the optional ones. Then Python will automatically handle checking to make sure the required options are specified and there aren't too many arguments provided. If you need to capture an arbitrary number of additional arguments, just use *args and **kwargs and you can check that the len is correct yourself.
For nargout, there is no python equivalent because python requires all outputs be handled. So if you do return a, b, in myfun, a, and b must both be handled. You can simply ignore some, but you must explicitly do this. There is no easy way to check if those arguments are being ignored. So say we have this in myfunc:
return a, b, c, d
And you only want a, you can just do:
a, *_ = myfunc()
This puts the first returned value in a and dumps the remainder in the throw-away variable _ (just like *args captures all remaining arguments, *_ captures all remaining outputs). For, say, a and c, you can do:
a, _, c, _ = myfunc()
You can also index directly from a function call, something MATLAB doesn't allow. So this is also possible. So to get the first argument, you can do:
a = myfunc()[0]
So since it is easy in Python to get the returned values you want, and since implicit variable numbers of output values is an extreme pain to handle properly, Python doesn't support it. If you really want to change the number of output argument you can set an optional named argument in the function definition to do so. Since optional arguments are easy in Python, this isn't a huge issue. But it isn't usually needed.
As for errors, yes you just raise the appropriate exception. Python has different types of errors for different situations. raise TypeError('blah') is the correct approach for the wrong number of arguments, where blah is the message you want.

Related

Python Multiple Dispatch on only some arguments

What I would really like to do is make a function with a default first argument
def do_thing(self, category:str='default', sub_category:str, argA:int|float, argB:int|float, argC:int|float)
...
obj.do_thing('cat1', 'sub1', 1, 2, 3)
obj.do_thing('sub2', 1, 2, 3) #In thise case 'default' is used for the category
But that isn't possible since all arguments with default values must come after non-default arguments.
I could technically move the category argument last and the default work work fine, but the category and sub_category arguments are closely related so I would like to keep them grouped together at the beginning of the line then followed by argA/B/C since it follows the format [selected thing, arguments to use on selected thing]
Using *args would also technically work and could check number of arguments and whether there are one or two strings at the beginning of the list.
But this seems much less clear what arguments are expected to be given and doesn't have type hinting or the arguments which are useful for IDE auto-suggestions.
def do_thing(self, *args)
Multiple Dispatch seemed like it did sort of what I wanted since it allows different versions of the function to be called based on the arguments that are used.
So I tried doing this
from multipledispatch import dispatch
#dispatch(str, str, object, object, object) #Use object to allow more than one type since float and int are both valid
def do_thing(self, category:str='default', sub_category:str, argA:int|float, argB:int|float, argC:int|float)
...
#dispatch(str, object, object, object)
def do_thing(self, sub_category:str, argA:int|float, argB:int|float, argC:int|float)
do_thing('default', sub_category, argA, argB, argC)
This mostly works if all the arguments are entered as positional arguments, however the users of this library regularly (but not always) call the function with argA-argC as keyword arguments, which doesn't work with multiple dispatch.
Is there any way to check if there are at least 2 positional argument and then only if the first two arguments of the function are strings run one function, else for any other combination, call the other version?
functools.singledispatchmethod almost does what I want to do, but in both cases, the first argument will always be a string, so I don't think that will work unless there's a way to tell it to look at the second argument instead of the first
Alternatively, is there a better way to go about doing what I was trying to do to begin with? Basically create a default value for the first argument?

How to map function on all argument values, as a list? but have explicit argument names in the function definition

I want to define a function using explicit argument names ff(a,b,c) in the function definition, but I also want to map a function over all arguments to get a list:
ff(a,b,c):
return list(map(myfunc,[a,b,c]))
However, I don't want to explicitly write parameter names inside function as a,b,c. I want to do it like
ff(a,b,c):
return list(map(myfunc,getArgValueList()))
getArgValueList() will retrieve the argument values in order and form a list. How to do this? Is there a built-in function like getArgValueList()?
What you're trying to do is impossible without ugly hacks. You either take *args and get a sequence of parameter values that you can use as args:
def ff(*args):
return list(map(myfunc, args))
… or you take three explicit parameters and use them by name:
def ff(a, b, c):
return list(map(myfunc, (a, b, c)))
… but it's one or the other, not both.
Of course you can put those values in a sequence yourself if you want:
def ff(a, b, c):
args = a, b, c
return list(map(myfunc, args))
… but I'm not sure what that buys you.
If you really want to know how to write a getArgValueList function anyway, I'll explain how to do it. However, if you're looking to make your code more readable, more efficient, more idiomatic, easier to understand, more concise, or almost anything else, it will have the exact opposite effect. The only reason I could imagine doing something like this is if you had to generate functions dynamically or something—and even then, I can't think of a reason you couldn't just use *args. But, if you insist:
def getArgValueList():
frame = inspect.currentframe().f_back
code = frame.f_code
vars = code.co_varnames[:code.co_argcount]
return [frame.f_locals[var] for var in vars]
If you want to know how it works, most of it's in the inspect module docs:
currentframe() gets the current frame—the frame of getArgValueList.
f_back gets the parent frame—the frame of whoever called getArgValueList.
f_code gets the code object compiled from the function body of whoever called getArgValueList.
co_varnames is a list of all local variables in that body, starting with the parameters.
co_argcount is a count of explicit positional-or-keyword parameters.
f_locals is a dict with a copy of the locals() environment of the frame.
This of course only works for a function that takes no *args, keyword-only args, or **kwargs, but you can extend it to work for them as well with a bit of work. (See co_kwonlyargcount, co_flags, CO_VARARGS, and CO_VARKEYWORDS for details.)
Also, this only works for CPython, not most other interpreters. and it could break in some future version, because it's pretty blatantly relying on implementation details of the interpreter.
The *args construction will give you the arguments as a list:
>>> def f(*args): return list(map(lambda x:x+1, args))
>>> f(1,2,3)
[2, 3, 4]
If you are bound with the signature of f, you'll have to use the inspect module:
import inspect
def f(a, b,c):
f_locals = locals()
values = [f_locals[name] for name in inspect.signature(f).parameters]
return list(map(lambda x:x+1, values))
inspect.signature(f).parameters gives you the list of arguments in the correct order. The values are in locals().

Caching in python using *args and lambda functions

I recently attempted Googles foo.bar challenge. After my time was up I decided to try find a solution to the problem I couldn't do and found a solution here (includes the problem statement if you're interested). I'd previously been making a dictionary for every function I wanted to cache but it looks like in this solution any function/input can be cached using the same syntax.
Firstly I'm confused on how the code is even working, the *args variable isn't inputted as an argument (and prints to nothing). Heres an modified minimal example to illustrate my confusion:
mem = {}
def memoize(key, func, *args):
"""
Helper to memoize the output of a function
"""
print(args)
if key not in mem:
# store the output of the function in memory
mem[key] = func(*args)
return mem[key]
def example(n):
return memoize(
n,
lambda: longrun(n),
)
def example2(n):
return memoize(
n,
longrun(n),
)
def longrun(n):
for i in range(10000):
for j in range(100000):
2**10
return n
Here I use the same memoize function but with a print. The function example returns memoize(n, a lambda function,). The function longrun is just an identity function with lots of useless computation so it's easy to see if the cache is working (example(2) will take ~5 seconds the first time and be almost instant after).
Here are my confusions:
Why is the third argument of memoize empty? When args is printed in memoize it prints (). Yet somehow mem[key] stores func(*args) as func(key)?
Why does this behavior only work when using the lambda function (example will cache but example2 won't)? I thought lambda: longrun(n) is just a short way of giving as input a function which returns longrun(n).
As a bonus, does anyone know how you could memoize functions using a decorator?
Also I couldn't think of a more descriptive title, edits welcome. Thanks.
The notation *args stands for a variable number of positional arguments. For example, print can be used as print(1), print(1, 2), print(1, 2, 3) and so on. Similarly, **kwargs stands for a variable number of keyword arguments.
Note that the names args and kwargs are just a convention - it's the * and ** symbols that make them variadic.
Anyways, memoize uses this to accept basically any input to func. If the result of func isn't cached, it's called with the arguments. In a function call, *args is basically the reverse of *args in a function definition. For example, the following are equivalent:
# provide *args explicitly
print(1, 2, 3)
# unpack iterable to *args
arguments = 1, 2, 3
print(*arguments)
If args is empty, then calling print(*args) is the same as calling print() - no arguments are passed to it.
Functions and lambda functions are the same in python. It's simply a different notation for creating a function object.
The problem is that in example2, you are not passing a function. You call a function, then pass on its result. Instead, you have to pass on the function and its argument separately.
def example2(n):
return memoize(
n,
longrun, # no () means no call, just the function object
# all following parameters are put into *args
n
)
Now, some implementation details: why is args empty and why is there a separate key?
The empty args comes from your definition of the lambda. Let's write that as a function for clarity:
def example3(n):
def nonlambda():
return longrun(n)
return memoize(n, nonlambda)
Note how nonlambda takes no arguments. The parameter n is bound from the containing scope as a closure, bound from the containing scope. As such, you don't have to pass it to memoize - it is already bound inside the nonlambda. Thus, args is empty in memoize, even though longrun does receive a parameter, because the two don't interact directly.
Now, why is it mem[key] = f(*args), not mem[key] = f(key)? That's actually slightly the wrong question; the right question is "why isn't it mem[f, args] = f(*args)?".
Memoization works because the same input to the same function leads to the same output. That is, f, args identifies your output. Ideally, your key would be f, args as that's the only relevant information.
The problem is you need a way to look up f and args inside mem. If you ever tried putting a list inside a dict, you know there are some types which don't work in mappings (or any other suitable lookup structure, for that matter). So if you define key = f, args, you cannot memoize functions taking mutable/unhashable types. Python's functools.lru_cache actually has this limitation.
Defining an explicit key is one way of solving this problem. It has the advantage that the caller can select an appropriate key, for example taking n without any modifications. This offers the best optimization potential. However, it breaks easily - using just n misses out the actual function called. Memoizing a second function with the same input would break your cache.
There are alternative approaches, each with pros and cons. Common is the explicit conversion of types: list to tuple, set to frozenset, and so on. This is slow, but the most precise. Another approach is to just call str or repr as in key = repr((f, args, sorted(kwargs.items()))), but it relies on every value having a proper repr.

in python, how do you denote required parameters and optional parameters in code?

especially when there are so many parameters (10+ 20+).
What are good ways of enforcing required/optional parameters to a function?
What are some good books that deal with this kind of questions for python?
(like effective c++ for c++)
** EDIT **
I think it's very unpractical to list def foo(self, arg1, arg2, arg3, .. arg20, .....): when there are so many required parameters.
Parameters can be required or optional depending on how they appear in the function definition:
def myfunction(p1, p2, p3=False, p4=5)
In this definition, parameters p1 and p2 are required. p3 is optional and will acquire the value False if not provided by the caller. p4 is also optional and will acquire the value 5 if not provided.
If you really need to pass ten or more parameters into a function, it might be better to pass them as a dictionary:
args = {'a': something, 'b': something_else, 'c': 5, 'd': 99.99, ... }
myfunc(args)
If a function parameter is not required, you can set it equal to None.
def hello(first_name, last_name=None):
print "Hello " + first_name
Or as John Gordon said, you can set a parameter with a default value the same way.
Note that optional parameters need to be defined after the required parameters.
One way is to have your required parameters be named like func(a, b, c=1) and this would denote required because the code will error out at runtime if missing any. Then for the optional parameters you would then use Python's args and kwargs.
Of course anytime you use Python's args and kwargs means additional code to pull the parameter from the args and kwargs.
Additionally for each combination of optional parameters you then would need to code a bunch of conditional control flow.
In addition you don't want too many optional assignments because it makes the code's API to complex to describe... and the control flow have to many lines of code because the number of possible combinations grows very quickly for each additional optional parameter.
AND your test code grows EVEN faster...
You can denote required keyword argument as follows:
def myfunc(positional_arg, *, required_kwarg, optional_kwarg=None, **other_kwargs):
pass

mapping functions with *args using lambda

Okay this one is confusing. My old piece of code has something like
map(lambda x:x.func1(arg1), other_args_to_be_mapped)
now I would like to make arg1 -> *args
while other_args_to_be_mapped stays unchanged.
in func1, the length of arguments will be checked different operations. My questions are
1) which length will be checked? arg1 or other_args_to_be_mapped
2) in func1, how should I set up the default? It was like
def func1(arg1=something)
but now with potential multiple arguments, I don't know what to do with the initialization. I want to be able to do something like
def func1(args*=something, something_else)
Is that even possible?
If I understand your question correctly, you're looking for variable arguments. These can be mixed with fixed arguments, provided you obey a logical ordering (fixed arguments first, then keyword arguments or variable arguments).
For example, the following shows how map to a function that takes in one constant argument and one variable argument. If you would like different behaviour, please provide a concrete example of what you are trying to accomplish
import random
class Foo:
def get_variable_parameters(self):
return [1] if random.random() > .5 else [1,2]
def foo( self, arg, *args ):
print("Doing stuff with constant arg", arg)
if len(args) == 1:
print("Good",args)
else:
print("Bad",args)
list(map( lambda x : x.foo( 'Static Argument', *x.get_variable_parameters()), [Foo(),Foo(),Foo()] ))
We don't know how many arguments are going to be passed to foo (in this trivial case, it's one or two), but the "*" notation accepts any number of objects to be passed
Note I've encapsulated map in list so that it gets evaluated, as in python3 it is a generator. List comprehension may be more idiomatic in python. Also don't forget you can always use a simple for loop - an obfuscated or complex map call is far less pythonic than a clear (but several line) for-loop, imo.
If, rather, you're trying to combine multiple arguments in a map call, I would recommend using the same variable argument strategy with the zip function, e.g.,
def foo(a,*b): ...
map(lambda x : foo(x[0],*x[1]), zip(['a','b'],[ [1], [1,2] ]))
In this case, foo will get called first as foo('a',1), and then as foo('b',2,3)

Categories