I am writing a function that will take a sequence of strings as a parameter, and then return a function (probably a lambda) where those strings are the names of the parameters. I can make it return a function, but I can't quite figure out how to set the parameters of the returned function.
i.e. if I gave called make_a_new_function(('a','b','c','d')) the returned function would have four parameters: a, b, c and d.
I know that the function.__code__.co_varnames is stored as a tuple of strings, but it's a read-only value so I can't edit it.
Is what I'm wanting possible? Or am I going mad over impossible dreams?
EDIT: Turns out there was a good alternative by constructing a string of python syntax, then using eval() to call the expression.
With the aid of some people in the comments, I determined that the best way to do get what I was aiming for would be to construct the expression as a string. This could then be evaluated using the eval() function, as long as the string was syntactically correct.
def logic_AND_expression(*parameters)
return ' and '.join(*parameters)
eval(logic_AND_expression('A','B','C','D'),{'A'=True,'B'=False,'C'=True,'D'=True})
False
Related
If there is a function defined in the pandas docs as: function([,parameter]) what does this represent and where can I find this information in the docs? I would like to know for the future if there is a conventional place for documentation to explain their notation choices.
Usually this would be phrased as
function(a[, b])
which simply means that the function can be called either with one argument as function(a) or with two arguments as function(a, b). [] is standard notation for "optional", while including the , in the brackets makes it clear that the comma would need to accompany that optional argument.
Suppose python supported function overloading, how would we define overloaded functions for adding two numbers and concatenating two strings?
I just want to know how do we assign a particular data type to a parameter in the function definition or will it depend on the arguments given in the function call.
Would it be like this:
def num(a=int,b=int):
return a+b
def strng(a=str,b=str):
return a+b
python is dynamically typed language. Hence, type checks are not strictly enforced. In defining a function, you can define an optional type for the parameters.
def function(a:int=0, b:int=0): return a + b
The type definition here really does nothing peculiar but probably helps with IDE autocompletion. You can pass in a string and you won't get any errors but it'll definitely throw an error when adding a number to a string.
Overview
At some point at run-time, I want to create a function that exactly takes a given number of arguments (known only at run-time). Exactly here means that this must not be a variadic function. Is there any way to do this without resorting to eval or similar ways to interpret strings as code?
My problem (slightly reduced)
I want to create a function that takes its arguments and passes them to another function as an iterable. Usually, I could do this as follows:
my_function = lambda *args: other_function(args)
Unfortunately, my_function will be called by some routine that cannot properly handle variadic functions¹. However, my_function will always be called with the same number of arguments n_args. So, if I knew n_args to be 3, I could use:
my_function = lambda a,b,c: other_function((a,b,c))
The problem is that I only get to know n_args at run-time, just before creating my_function. Thus, I need to generalise the above.
What I found so far
I achieve what I want using eval (or exec or similar):
arg_string = ",".join( "arg_%i"%i for i in range(n_args) )
my_function = eval(
"lambda %s: other_function((%s))" % (arg_string,arg_string),
{"other_function":other_function}
)
The downside to this is that I have to use eval, which is ugly and bad. If you so wish, my question is how to create something equivalent to the above without using eval or obtaining a variadic function.
SymPy’s lambdify allows me to dynamically create functions with a fixed number of arguments at run-time (that work in my context), but looking at the source code, it seems that it uses eval under the hood as well.
¹ It’s a compiled routine created with F2Py. I cannot change this with reasonable effort right now. Yes, it’s sad and on the long run, I will find try to fix this or get this fixed, but for now let’s accept this as given.
Create a function like the below. You can write a script to generate the code dynamically to make it as long as you want, but still keep the result statically in a normal python file to avoid using eval/exec.
def function_maker(n_args, other_function):
return [
lambda: other_function(),
lambda arg_0: other_function(arg_0),
lambda arg_0, arg_1: other_function(arg_0, arg_1),
][n_args]
Then use it as follows:
function_maker(2, other_function)(a, b)
I would like to write a function in Python which takes a slice as a parameter. Ideally a user would be to be able to call the function as follows:
foo(a:b:c)
Unfortunately, this syntax is not permitted by Python - the use of a:b:c is only allowed within [], not ().
I therefore see three possibilities for my function:
Require the user to use a slice "constructor" (where s_ acts like the version provided by numpy):
foo(slice(a, b, c))
foo(s_[a:b:c])
Put the logic of my function into a __getitem__ method:
foo[a:b:c]
Give up trying to take a slice and take start, stop and step individually:
foo(a, b, c)
Is there a way to get the original syntax to work? If not, which of the workaround syntaxes would be preferred? Or is there another, better option?
Don't surprise your users.
If you use the slicing syntax consistently with what a developer expects from a slicing syntax, that same developer will expect square brackets operation, i.e. a __getitem__() method.
If instead the returned object is not somehow a slice of the original object, people will be confused if you stick to a __getitem__() solution. Use a function call foo(a, b, c), don't mention slices at all, and optionally assign default values if that makes sense.
Slices make more sense when they're expressed as a slice of something. So, another alternative is to be more object-oriented: create a temporary object that represents your slice of something, and put your function as a method of it.
For example, if your function is really:
foo(bar, a:b:c)
or
bar.foo(a:b:c)
then you can replace this with:
bar[a:b:c].foo()
If bar[a:b:c] already has a different meaning, then come up with a another name baz and do:
bar.baz[a:b:c].foo()
It's hard to give convincing examples without a real context, because you're trying to name related things with names that make intuitive sense, let you write unambiguous code, and are relatively short.
If you're really just writing a function on its own operating on a slice, then either:
Your function modifies a slice, returning a different slice:
bar[foo(a:b:c)]
If this is the case, whatever valid syntax you choose is going to look a little confusing. You probably don't want to use slices if you're aiming for a broad audience of Python programmers.
Your function really operates on a slice of the integers, so you can make that explicit with a temporary object:
the_integers[a:b:c].foo()
The use of [a:b:c] is, as you note, a syntax thing. The interpreter raises a syntax error for (a:b:c) right away, before your code has any chance to do something with the values. There isn't a way around this syntax without rewriting the interpreter.
It's worth keeping in mind that the interpreter translates foo[a:b:c] to
foo.__getitem__(slice(a,b,c))
The slice object itself is not very complicated. It just has 3 attributes (start,step,stop) and a method indices. It's the getitem method that makes sense of those values.
np.s_ and other functions/classes in np.lib.index_tricks are good examples of how __getitem__ and slice can be used to extend (or simplify) indexing. For example, these are equivalent:
np.r_[3:4:10j]
np.linspace(3,4,10)
As to the foo(a,b,c) syntax, the very common np.arange() uses it. As does range and xrange. So you, and your users, should be quite familiar with it.
Since the alternatives all end up giving you the start/step/stop trio of values, they are functionally equivalent (in speed). So the choice comes down to user preferences and familiarity.
While your function can't take a:b:c notation directly, it can be written to handle a variety of inputs - a slice, 3 positional arguments, a tuple, a tuple of slices (as from s_), or keyword arguments. And following the basic numpy indexing you could distinguish between tuples and lists.
I'm looking for the best way to give a list of arguments to my function :
def myFunc(*args):
retVal=[]
for arg in args:
retVal.append(arg+1)
return "test",retVal
The problem is that it becomes very annoying when you have a long list of parameters to pass to your function because you have to write two times your whole list of parameters and When you have 10 parameters or more with complete names, it becomes really (really) heavy.
test,alpha,beta,gamma,delta,epsilon,zeta,eta,theta,iota=myFunc(alpha,beta,gamma,delta,epsilon,zeta,eta,theta,iota)
So I thought about something like this :
w=alpha,beta,gamma,delta,epsilon,zeta,eta,theta,iota
test,w=myFunc(w)
But then I sill have to do :
alpha,beta,gamma,delta,epsilon,zeta,eta,theta,iota=w
Is there any shorter way to give and get back a list of parameter from a function.
Or give a pointer to the function for it to modify directly the parameters ?
This is what I'm looking for :
w=alpha,beta,gamma,delta,epsilon,zeta,eta,theta,iota
test,w=myFunc(w)
# And directly get my parameters modified to be able to print them :
print alpha,[...],iota
Two options:
Try reducing the number of arguments by splitting the logic into multiple functions.
If 1.) is not possible, you can use a dictionary a single argument - encapsulating all your arguments. This would be a flexible (signature of function stays the same, even if you take away or add parameters) and mostly readable solution (meaningful keys in the dictionary).
Simply make the function return a dict. Then you can call it using myFunc(**yourdict) to use the dict items as arguments and if you return yourdict you get back the same dict (with probably modified values) - or you just modify the original dict and don't return one at all.