Python: function as argument - python

I'm diving into the concept of passing functions as arguments to other functions. I have a question related to this:
when you pass a function as argument: can you also set a fixed value for some of the parameters(apart from their default value)?, something like this:
def my_cross(y,cross=2):
y=y*cross;
return y;
def my_print(func,x):
print(func(x))
my_print(my_cross(cross=3),3)
won't execute because some arguments have not been given to my_cross.
Tried also giving a default value to the first argument:
def my_cross(y: int = 1,cross=2):
y=y*cross;
return y;
def my_print(func,x):
print(func(x))
my_print(my_cross(cross=3),3)
but resulted in:
TypeError: 'int' object is not callable

Mistake in your my_print(my_cross(cross=3),3) line
my_cross(cross=3) already called and returned an int object, and my_print tries to call an int object, which is not callable.
For your situation, you can do this:
def my_print(func, **kwargs):
print(func(**kwargs))
my_print(my_cross, cross=3)
In this case, you put a callable object my_cross and cross=3 as kwargs, and function will implement this as print(func(cross=3))

Related

IntelliJ-IDEA's Python type checker - incorrect expected type?

In the following code:
from typing import List
def test() -> List[str]:
ret = ["Hi", "Hi"]
return ret
I'm receiving the following warning on the function definition line:
Expected type 'Integral", got 'str' instead
I can't see any reason why IntelliJ-IDEA's python module would interpret this simple function as returning an list of Integral rather than str. Is this a type checker bug?
Pycharm only have some preliminary support for type hints.
Note that typing.List is a simple wrapper class that extends builtin list class. It helps in type hinting and doesn't allow creation of new list using itself.
Here is the current code for typing.List:
class List(list, MutableSequence[T], extra=list):
__slots__ = ()
def __new__(cls, *args, **kwds):
if _geqv(cls, List):
raise TypeError("Type List cannot be instantiated; "
"use list() instead")
return _generic_new(list, cls, *args, **kwds)
By doing typing.List[str], you are trying to call the method __getitem__ on an object typing.List (which is a type). Also note that
This translates to following:
typing.List.__getitem__(str)
# or
list.__getitem__(str)
Now if you see, what does list indexing method expect, it expects an integer like a[0], a[1]. In this case instead of an int like 0, 1, it received a type str
Hope this gives you a more holistic view of typing.List.

How to acces to a class variable with a string?

I have the following code:
import Parameters
def ReadOptionFromParametersFile(self,variable_name):
if (hasattr(Parameters, str(variable_name))):
return Parameters.variable_name
else:
return 0
I want to call this function for different variables. In case the variable is defined on that Parameter file i want to read the value doing Parameters.variable_name. The problem is obviously that the file has no variable called variable_name.
I also tried:
Parameters.str(variable_name)
or
Parameters.eval(variable_name)
You can use getattr to access an attribute by its string name:
return getattr(Parameters, variable_name)
In fact, your entire method can be refactored to just:
def ReadOptionFromParametersFile(self, variable_name):
return getattr(Parameters, variable_name, 0)
The third argument to getattr is an optional default value to return if the attribute is not found.

Python function loses identity after being decorated

(Python 3)
First of all, I feel my title isn't quite what it should be, so if you stick through the question and come up with a better title, please feel free to edit it.
I have recently learned about Python Decorators and Python Annotations, and so I wrote two little functions to test what I have recently learned.
One of them, called wraps is supposed to mimic the behaviour of the functools wraps, while the other, called ensure_types is supposed to check, for a given function and through its annotations, if the arguments passed to some function are the correct ones.
This is the code I have for those functions:
def wraps(original_func):
"""Update the decorated function with some important attributes from the
one that was decorated so as not to lose good information"""
def update_attrs(new_func):
# Update the __annotations__
for key, value in original_func.__annotations__.items():
new_func.__annotations__[key] = value
# Update the __dict__
for key, value in original_func.__dict__.items():
new_func.__dict__[key] = value
# Copy the __name__
new_func.__name__ = original_func.__name__
# Copy the docstring (__doc__)
new_func.__doc__ = original_func.__doc__
return new_func
return update_attrs # return the decorator
def ensure_types(f):
"""Uses f.__annotations__ to check the expected types for the function's
arguments. Raises a TypeError if there is no match.
If an argument has no annotation, object is returned and so, regardless of
the argument passed, isinstance(arg, object) evaluates to True"""
#wraps(f) # say that test_types is wrapping f
def test_types(*args, **kwargs):
# Loop through the positional args, get their name and check the type
for i in range(len(args)):
# function.__code__.co_varnames is a tuple with the names of the
##arguments in the order they are in the function def statement
var_name = f.__code__.co_varnames[i]
if not(isinstance(args[i], f.__annotations__.get(var_name, object))):
raise TypeError("Bad type for function argument named '{}'".format(var_name))
# Loop through the named args, get their value and check the type
for key in kwargs.keys():
if not(isinstance(kwargs[key], f.__annotations__.get(key, object))):
raise TypeError("Bad type for function argument named '{}'".format(key))
return f(*args, **kwargs)
return test_types
Supposedly, everything is alright until now. Both the wraps and the ensure_types are supposed to be used as decorators. The problem comes when I defined a third decorator, debug_dec that is supposed to print to the console when a function is called and its arguments. The function:
def debug_dec(f):
"""Does some annoying printing for debugging purposes"""
#wraps(f)
def profiler(*args, **kwargs):
print("{} function called:".format(f.__name__))
print("\tArgs: {}".format(args))
print("\tKwargs: {}".format(kwargs))
return f(*args, **kwargs)
return profiler
That also works cooly. The problem comes when I try to use debug_dec and ensure_types at the same time.
#ensure_types
#debug_dec
def testing(x: str, y: str = "lol"):
print(x)
print(y)
testing("hahaha", 3) # raises no TypeError as expected
But if I change the order with which the decorators are called, it works just fine.
Can someone please help me understand what is going wrong, and if is there any way of solving the problem besides swapping those two lines?
EDIT
If I add the lines:
print(testing.__annotations__)
print(testing.__code__.co_varnames)
The output is as follows:
#{'y': <class 'str'>, 'x': <class 'str'>}
#('args', 'kwargs', 'i', 'var_name', 'key')
Although wraps maintains the annotations, it doesn't maintain the function signature. You see this when you print out the co_varnames. Since ensure_types does its checking by comparing the names of the arguments with the names in the annotation dict, it fails to match them up, because the wrapped function has no arguments named x and y (it just accepts generic *args and **kwargs).
You could try using the decorator module, which lets you write decorators that act like functools.wrap but also preserve the function signature (including annotations).
There is probably also a way to make it work "manually", but it would be a bit of a pain. Basically what you would have to do is have wraps store the original functions argspec (the names of its arguments), then have ensure_dict use this stored argspec instead of the wrapper's argspec in checking the types. Essentially your decorators would pass the argspec in parallel with the wrapped functions. However, using decorator is probably easier.

Is there a standard function code like `lambda x, y: x.custom_method(y)`?

I know that I can call magic methods using functions from operator module, for example:
operator.add(a, b)
is equal to
a.__add__(b)
Is there a standard function for calling a custom method (like operator.methodcaller but also accepts method arguments when called)?
Currently I have code like this:
def methodapply(name):
"""Apply a custom method.
Usage:
methodapply('some')(a, *args, **kwargs) => a.some(*args, **kwargs)
"""
def func(instance, *args, **kwargs):
return getattr(instance, name)(*args, **kwargs)
func.__doc__ = """Call {!r} instance method""".format(name)
return func
Yes, it is still called operator.methodcaller():
Return a callable object that calls the method name on its operand. If additional arguments and/or keyword arguments are given, they will be given to the method as well. For example:
After f = methodcaller('name'), the call f(b) returns b.name().
After f = methodcaller('name', 'foo', bar=1), the call f(b) returns b.name('foo', bar=1).
This inverts the chain a little from what you want; you need to tell methodcaller() up-front what arguments to pass in.
There is no standard-library callable that'll build a method caller that accepts both the instance and the arguments.
However, if your sequence of objects you are going to apply this to is homogenous, you can instead just use the unbound method; unbound methods take self as the first argument. So this works:
from itertools import
map(str.split, list_of_strings, [','] * len(list_of_strings), range(len(list_of_strings)))
which will split strings on commas with a growing limit; each time map() calls str.split() (an unbound method), it'll pass in a str object from the list_of_strings list, a ',' string, and an integer argument ranging from 0 to len(list_of_strings) - 1.

Calling a function from a string in Python throws error

I am trying to call a function from a string in Python as explained in
Calling a function of a module from a string with the function's name in Python.
Unfortunately, this doesn't work, and the Python interpreter throws an error:
TypeError: 'str' object is not callable
def current(self, t):
if self.iMode == None:
return self.i
else:
return getattr(self, 'iMode')(t)
The error refers to the last line. iMode has been set to sinx(t), that has been declared in the class.
Can anyone help me please?
From the error message it is obvious that your attribute was set to 'sinx(t)' (the string literal).
You should set it the function reference sinx instead, which is a callable.
However, as zhangyangu already said, in you example using getattr() is not needed. Maybe you really want to use a parameter (string reference) instead of the literal 'iMode'?
From the error, your iMode is a string. The iMode is not a method. There must be something wrong with your declaration. And in the class you can use self.iMode, no need to use getattr.
I think you may look for the function like eval.

Categories

Resources