How to unpack a tuple while calling an external method in Python? - python

I call a method of an external library multiple times in my class like this:
class MyClass:
const_a = "a"
const_b = True
const_c = 1
def push(self, pushee):
with ExternalLibrary.open(self.const_a, self.const_b, self.const_c) as el:
el.push(pushee)
def pop(self):
with ExternalLibrary.open(self.const_a, self.const_b, self.const_c) as el:
return el.pop()
The lines containing the with statement are bugging me, because they require passing the the constants as arguments every time. I would like to store the arguments in a predefined data structure like a tuple and pass that to the external library.

You can do this:
args = (const_a, const_b, const_c)
ExternalLibrary.open(*args)
The * syntax unpacks an iterable (tuple, list, etc.) into individual arguments in a function call. There is also a ** syntax for unpacking a dictionary into keyword arguments:
kwargs = {'foo': 1, 'bar': 2}
func(**kwargs) # same as func(foo=1, bar=2)
You can also use both in the same call, like func(*args, **kwargs).

Related

How to specify arg position for functool partial()

As per manual, functools partial() is 'used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.'
What's the best way to specify the positions of the arguments that one wishes to evaluate?
EDIT
Note as per comments, the function to be partially evaluated may contain named and unnamed arguments (these functions should be completely arbitrary and may be preexisting)
END EDIT
For example, consider:
def f(x,y,z):
return x + 2*y + 3*z
Then, using
from functools import partial
both
partial(f,4)(5,6)
and
partial(f,4,5)(6)
give 32.
But what if one wants to evaluate, say the third argument z or the first and third arguments x, and z?
Is there a convenient way to pass the position information to partial, using a decorator or a dict whose keys are the desired arg positions and the respective values are the arg values? eg to pass the x and z positions something like like this:
partial_dict(f,{0:4,2:6})(5)
No, partial is not designed to freeze positional arguments at non-sequential positions.
To achieve the desired behavior outlined in your question, you would have to come up with a wrapper function of your own like this:
def partial_positionals(func, positionals, **keywords):
def wrapper(*args, **kwargs):
arg = iter(args)
return func(*(positionals[i] if i in positionals else next(arg)
for i in range(len(args) + len(positionals))), **{**keywords, **kwargs})
return wrapper
so that:
def f(x, y, z):
return x + 2 * y + 3 * z
print(partial_positionals(f, {0: 4, 2: 6})(5))
outputs:
32
Simply use keyword arguments. Using your definition of f above,
>>> g = partial(f, z=10)
>>> g(2, 4)
40
>>> h = partial(f, y=4, z=10)
>>> h(2)
40
Note that once you use a keyword argument for a given parameter, you must use keyword arguments for all remaining arguments. For example, the following would not be valid:
>>> j = partial(f, x=2, z=10)
>>> j(4)
TypeError: f() got multiple values for argument 'x'
But continuing to use keyword arguments is:
>>> j = partial(f, x=2, z=10)
>>> j(y=4)
40
When you use functools.partial, you store the values of *args and **kwargs for later interpolation. When you later call the "partially applied" function, the implementation of functools.partial effectively adds the previously provided *args and **kwargs to the argument list at the front and end, respectively, as though you had inserted these argument-unpackings yourself. I.e., calling
h = partial(1, z=10)
f(4)
is roughly equivalent to writing
args = [1]
kwargs = {'z': 10}
f(*args, 4, **kwargs)
As such, the semantics of how you provide arguments to functools.partial is the same as how you would need to store arguments in the args and kwargs variables above such that the final call to f is sensible. For more information, take a look at the pseduo-implementation of functools.partial given in the functools module documentation
For easier usage, you can create a new object specifically to specify a positional argument that is to be skipped when sequentially listing values for positional arguments to be frozen with partial:
SKIP = object()
def partial_positionals(func, *positionals, **keywords):
def wrapper(*args, **kwargs):
arg = iter(args)
return func(*(*(next(arg) if i is SKIP else i for i in positionals), *arg),
**{**keywords, **kwargs})
return wrapper
so that:
def f(x, y, z):
return x + 2 * y + 3 * z
print(partial_positionals(f, 4, SKIP, 6)(5))
outputs:
32

How to use *args and self in Python constructor

I need a Python method to have access to self for instance variables and also be able to take any number of arguments. I basically want a method foo that can be called via
foo(a, b, c)
or
foo()
In the class, I think the constructor would be
def foo(self, *args):
Is this correct? Also, fyi, I am new to Python (if you can't tell).
You just have to add it after the self parameter:
class YourClass:
def foo(self, *args):
print(args)
def bar(self, *args, **kwargs):
print(args)
print(kwargs)
def baz(self, **kwargs):
print(kwargs)
I have also added a method in which you also add **kwargs, and the case in which you add both *args and **kwargs.
Examples
>>> o = YourClass()
>>> o.foo()
()
>>> o.foo(1)
(1,)
>>> o.foo(1, 2)
(1, 2)
def foo(self, *args):
Yes, that is correct.
You declared the method correctly. You can also use double asterisks to accept keyword arguments.
Reference: Expressions
A double asterisk ** denotes dictionary unpacking. Its operand must be a mapping. Each mapping item is added to the new dictionary. Later values replace values already set by earlier key/datum pairs and earlier dictionary unpackings.
....
An asterisk * denotes iterable unpacking. Its operand must be an iterable. The iterable is expanded into a sequence of items, which are included in the new tuple, list, or set, at the site of the unpacking.
Args will be a tuple. To access the values you will have to iterate or use positional arguments, ie: args[0]

Get function arguments value by pointer to this function?

I'd like to get value of function arguments by pointer to that function.
def cons(a, b):
def pair(f):
return f(a, b)
return pair
def car(cons):
# local_a = cons.a
# return local_a
pass
if __name__ == '__main__':
assert car(cons(3, 4)) == 3
You're on the wrong track. Looking at the code in the new version of your question, you're trying to extract the first element of a Church pair.
cons(3, 4) evaluates to a function that, when passed another function f, returns f(3, 4). To extract the 3, you should pass it a function that takes two arguments and returns its first argument:
def car(pair):
def firstarg(x, y):
return x
return pair(firstarg)
Then car(cons(3, 4)) calls cons(3, 4)(firstarg), which calls firstarg(3, 4), which returns 3.
Creating a Signature for the function is easy via the signature function:
from inspect import signature
def someMethod(self, arg1, kwarg1=None):
pass
sig = signature(someMethod)
Now, you can either view its parameters quickly by string it:
str(sig) # returns: '(self, arg1, kwarg1=None)'
or you can also get a mapping of attribute names to parameter objects via sig.parameters.
params = sig.parameters
print(params['kwarg1']) # prints: kwarg1=20
Additionally, you can call len on sig.parameters to also see the number of arguments this function requires:
print(len(params)) # 3
Each entry in the params mapping is actually a Parameter object that has further attributes making your life easier. For example, grabbing a parameter and viewing its default value is now easily performed with:
kwarg1 = params['kwarg1']
kwarg1.default # returns: None
similarly for the rest of the objects contained in parameters.

Python methods and "switches"

I'm trying to use a dictionary as a switch statement as in
def add(first, second):
return first + second
def sub():
...
return something
operations = {
"Add": add,
"Sub": sub
}
ret_val = operations[operation]
Now how can I pass the arguments to add and sub and get their response? Currently, I don't pass anything to the methods, and testing the ret_val. What I see is the operation getting called, but the return doesn't come back. What I get is the pointer to the operation method.
Thanks!
To call a function, put the arguments in parentheses after it, just like when you call a function directly by its name.
ret_val = operations[operation](1, 2)
Note that for this to work properly, all the functions in operations need to take the same number of arguments. So it won't work if add() takes two arguments but sub() takes none, as you've shown.
If the functions can take different numbers of arguments, you could put the arguments in a list and use the unpacking operator.
args = (1, 2)
ret_val = operations[operation](*args)
Then you just have to ensure that args contains the appropriate number of arguments for the particular operation.
The dictionary contains callable functions. To call them, just add the arguments in parentheses.
operations[operation](arg1, ...)
So, the main thing you're missing is executing the function call. The code as provided grabs the function reference properly, but you need parens to execute it.
Once you execute it, you need some way to pass arguments. Because the number of args varies by function, the best way is to pass both a variable number of args list (*args) and a dictionary of keyword arguments (**kwargs).
I've filled in your pseudocode slightly so these run:
def add(first, second):
return first + second
def sub(first, second):
return first - second
operations = {
"Add": add,
"Sub": sub,
}
Call add with args:
op = 'Add'
op_args = [1, 2]
op_kwargs = {}
ret_val = operations[operation](*op_args, **op_kwargs)
print(ret_val)
3
Call add with kwargs:
op = 'Add'
op_args = []
op_kwargs = {'first': 3, 'second': 4}
ret_val = operations[operation](*op_args, **op_kwargs)
print(ret_val)
7
If you try to pass both args and kwargs in a conflicting way, it will fail:
# WON'T WORK
op = 'Add'
op_args = [1, 2]
op_kwargs = {'first': 3, 'second': 4}
ret_val = operations[operation](*op_args, **op_kwargs)
print(ret_val)
TypeError: add() got multiple values for argument 'first'
But you can use both in a complementary way:
op = 'Add'
op_args = [1]
op_kwargs = {'second': 4}
ret_val = operations[operation](*op_args, **op_kwargs)
print(ret_val)
5
One technical note is that the naming args and kwargs is purely convention in Python. You could call them whatever you want. An answer that discusses the two more is available here: https://stackoverflow.com/a/36908/149428.
Note that I did not do any input validation, etc for the purpose of a simple, focused answer. If you're getting input from a user, that's an important step to remember.

Preserving argument default values while method chaining

If I have to wrap an existing method, let us say wrapee() from a new method, say wrapper(), and the wrapee() provides default values for some arguments, how do I preserve its semantics without introducing unnecessary dependencies and maintenance? Let us say, the goal is to be able to use wrapper() in place of wrapee() without having to change the client code. E.g., if wrapee() is defined as:
def wrapee(param1, param2="Some Value"):
# Do something
Then, one way to define wrapper() is:
def wrapper(param1, param2="Some Value"):
# Do something
wrapee(param1, param2)
# Do something else.
However, wrapper() has to make assumptions on the default value for param2 which I don't like. If I have the control on wrapee(), I would define it like this:
def wrapee(param1, param2=None):
param2 = param2 or "Some Value"
# Do something
Then, wrapper() would change to:
def wrapper(param1, param2=None):
# Do something
wrapee(param1, param2)
# Do something else.
If I don't have control on how wrapee() is defined, how best to define wrapper()? One option that comes into mind is to use to create a dict with non-None arguments and pass it as dictionary arguments, but it seems unnecessarily tedious.
Update:
The solution is to use both the list and dictionary arguments like this:
def wrapper(param1, *args, **argv):
# Do something
wrapee(param1, *args, **argv)
# Do something else.
All the following calls are then valid:
wrapper('test1')
wrapper('test1', 'test2')
wrapper('test1', param2='test2')
wrapper(param2='test2', param1='test1')
Check out argument lists in the Python docs.
>>> def wrapper(param1, *stuff, **kargs):
... print(param1)
... print(stuff)
... print(args)
...
>>> wrapper(3, 4, 5, foo=2)
3
(4, 5)
{'foo': 2}
Then to pass the args along:
wrapee(param1, *stuff, **kargs)
The *stuff is a variable number of non-named arguments, and the **kargs is a variable number of named arguments.
I'd hardly say that it isn't tedious, but the only approach that I can think of is to introspect the function that you are wrapping to determine if any of its parameters have default values. You can get the list of parameters and then determine which one is the first that has default values:
from inspect import getargspec
method_signature = getargspec(method)
param_names = method_signature[0]
default_values = method_signature[3]
params = []
# If any of method's parameters has default values, we need
# to know the index of the first one that does.
param_with_default_loc = -1
if default_values is not None and len(default_values) > 0:
param_slice_index = len(default_values) * -1
param_with_default = param_names[param_slice_index:][0]
param_with_default_loc = param_names.index(param_with_default)
At that point, you can iterate over param_names, copying into the dict that is passed to wrappee. Once your index >= param_with_default_loc, you can obtain the default values by looking in the default_values list with an index of your index - param_with_default_loc.
Does that make any sesne?
Of course, to make this generic, you would to define it as a wrapper function, adding yet another layer of wrapping.
def wrapper(param1, param2=None):
if param2:
wrapee(param1, param2)
else:
wrapee(param1)
is this what you want?
#!/usr/bin/python
from functools import wraps
def my_decorator(f):
#wraps(f)
def wrapper(*args, **kwds):
print 'Calling decorated function'
return f(*args, **kwds)
return wrapper
def f1(x, y):
print x, y
def f2(x, y="ok"):
print x, y
my_decorator(f1)(1,2)
my_decorator(f2)(1,2)
my_decorator(f2)(1)
adapted from http://koala/doc/python2.6-doc/html/library/functools.html#module-functools

Categories

Resources