Meaning of "..." in Python function signature - python

In the language reference, I've seen "..." used in multiple places for function parameters without an explicit specification for how it should be interpreted.
map(function, iterable, ...)
for maps
class C:
#classmethod
def f(cls, arg1, arg2, ...): ...
for classmethod
class C:
#staticmethod
def f(arg1, arg2, ...):...
for staticmethod
I feel like the second and third are merely informally saying that the signature of the function is unspecified when using "..." but I am confused with the usage in maps. I know *args specifies variable number of positional arguments, what does the ellipsis mean in this case?

Related

Method parameter auto completion for a class instance

I am trying to get auto completion of method parameter names for class instances but can't quite figure it out. Take this example class:
class Abc:
def meth(param):
pass
If I type Abc.meth(p and press Tab I see the expected completion of param=:
However, if I try to do the same with an instance of the class Abc().meth(p, I don't see param in the completion results:
I see the same behavior in both JupyterLab and VS Code, and the fact that this works for instances of other packages such as scikit learn, I think there is something I am missing.
How can I get method parameter completion for an instance of my Abc() class similar to the LinearRegression class above?
Based on experimentation on JupyterLab Version 3.2.9, this seems to be because the autocomplete tries to account for implicit parameters like self and cls. Including a self parameter should mostly fix your issues.
class Abc:
def meth(arg1, arg2, arg3):
pass
These are the completion options I was presented for the class above:
Decorator
Abc.meth
Abc().meth
None
arg1, arg2, arg3
arg2, arg3
#staticmethod
arg1, arg2, arg3
arg2, arg3
#classmethod
arg2, arg3
arg3
The standard behavior is fine, but the results for Abc().meth are wrong with both decorators. As mentioned in the docs for staticmethod and classmethod:
method can be called either on the class (such as C.f()) or on an instance (such as C().f())
So both columns should have been the same when decorators are used, but it always omits one parameter from Abc().meth, presumably for self. And even though cls is correctly handled for Abc.meth when using #classmethod, it ends up omitting two parameters in Abc().meth instead.
Testing with Visual Studio Code 1.67.1 gave the correct autocomplete options for all cases. So the missing suggestion you experienced there is expected behavior since param takes the place of self and there are no other parameters.

Callback protocol for functions with different kwargs

I have a function that takes a callback function as a parameter:
def function(arg1: int, callback):
...
I am trying to add a type hint for the callback function. Now, the callback functions that are passed to this function have the same positional args but the kwargs can be completely different (both in type and name).
def function1(arg1: int, arg2: str, **kwargs1):
...
def function2(arg1: int, arg2: str, **kwargs2):
...
I am trying to write a Callback Protocol that fits these 2 functions, but so far I don't see a way to make this work when the kwargs are different. Is there a way to create a unified Callback protocol in this case?
You can create this Callback protocol:
from typing_extensions import Protocol
class Callback(Protocol):
def __call__(self, arg1: int, arg2: str, **kwargs): ...
and type hint callback as Callback:
def function(arg1: int, callback: Callback): ...
Now, among these three function calls:
def function1(arg1: int, arg2: str, **kwargs1): ...
def function2(arg1: int, arg2: str, **kwargs2): ...
def function3(arg1: int): ...
function(0, function1) # Success.
function(0, function2) # Success.
function(0, function3) # Error.
mypy will report success for the first two, but report an error for the third one because of the missing arg2.
The key thing to realize here is that the name of the keyword-arguments parameter does not actually matter. If I take function2 and rename arg1 to arg_1, mypy would complain about that. This is because the public interface of function2 has changed in a backwards-incompatible way. For example, I would have to modify arg1=0 at any call site to arg_1=0.
But if I take the same function and rename kwargs2 to kwargs_2, the public interface does not actually change! This is because it was impossible to ever explicitly refer to the kwargs / kwargs1 / kwargs2 parameter at a call site in the first place. Take this example:
function2(arg1=0, arg2="", kwargs2={})
If I were to dump kwargs2 from inside the function, it would actually have the key "kwargs2" mapped to {}, instead of being an empty dictionary itself. This shows that it is impossible to rely on the name of the keyword-arguments parameter. mypy can therefore allow different names when checking if the public interface of function2 matches that of a Callback.
As for the type, kwargs, kwargs1 and kwargs2 all have a type of Dict[str, Any] left annotated. Since you're consistent across all 3, mypy reports success here, while also enabling you to take advantage of Any for keyword arguments specific to function1 or function2.

Use **kwargs both in function calling and definition

Suppose I have a function get_data which takes some number of keyword arguments. Is there some way I can do this
def get_data(arg1, **kwargs):
print arg1, arg2, arg3, arg4
arg1 = 1
data['arg2'] = 2
data['arg3'] = 3
data['arg4'] = 4
get_data(arg1, **data)
So the idea is to avoid typing the argument names in both function calling and function definition. I call the function with a dictionary as argument and the keys of the dictionary become local variables of function and their values are the dictionary values
I tried the above and got error saying global name 'arg2' is not defined. I understand I can change the locals() in the definition of get_data to get the desired behavior.
So my code would look like this
def get_data(arg1, kwargs):
locals().update(kwargs)
print arg1, arg2, arg3, arg4
arg1 = 1
data['arg2'] = 2
data['arg3'] = 3
data['arg4'] = 4
get_data(arg1, data)
and it wouldn't work too. Also cant I achieve the behavior without using locals()?
**kwargs is a plain dictionary. Try this:
def get_data(arg1, **kwargs):
print arg1, kwargs['arg2'], kwargs['arg3'], kwargs['arg4']
Also, check documentation on keyword arguments.
If we examine your example:
def get_data(arg1, **kwargs):
print arg1, arg2, arg3, arg4
In your get_data functions's namespace, there is a variable named arg1, but there is no variable named arg2. so you can not reach a function or a variable that is not in your namespace.
In fact, in your namespace; there is a variable arg1 and a dictionary object called kwargs. using ** notation before the kwargs (name kwargs is not important in here, it can be something else too.) tells your python compiler that kwargs is a dictionary and all values in that dictionary will be evaluated as named parameters in your function definition.
Lets take a look at this example:
def myFunc(**kwargs):
do something
myFunc(key1='mykey', key2=2)
when you call myFunc with named parameters key1 and key2, your function definition acts like
def myFunc(key1=None, key2=None):
but with an exception! since myFunc have no named parameters, compiler do not know how to handle them directly since you can pass any named parameter to your function.
So your function accept those named parameters within a dictionary like you call your function:
myFunc({key1:'mykey', key2:2})
so, your function definition get those parameters within a dictionary. **kwargs defines your dictionary in that point, which also tell compiler that any named parameters will be accepted (by the help of ** signs before the kwargs)
def myFunc(**kwargs):
print kwargs
will print
{key1:'mykey', key2:2}
So in your example, you can use kwargs like a dictionary;
def myFunc(**kwargs):
kwargs['arg2']
kwargs.get('arg3')
I hope it is not complicated (:

what constitutes a member of a class? What constitutes a method?

It seems that an acceptable answer to the question
What is a method?
is
A method is a function that's a member of a class.
I disagree with this.
class Foo(object):
pass
def func():
pass
Foo.func = func
f = Foo()
print "fine so far"
try:
f.func()
except TypeError:
print "whoops! func must not be a method after all"
Is func a member of Foo?
Is func a method of Foo?
I am well aware that this would work if func had a self argument. That's obvious. I'm interested in if it's a member of foo and in if it's a method as presented.
You're just testing it wrong:
>>> class Foo(object): pass
...
>>> def func(self): pass
...
>>> Foo.func = func
>>> f = Foo()
>>> f.func()
>>>
You error of forgetting to have in the def the self argument has absolutely nothing to do with f.func "not being a method", of course. The peculiar conceit of having the def outside the class instead of inside it (perfectly legal Python of course, but, as I say, peculiar) has nothing to do with the case either: if you forget to have a first argument (conventionally named self) in your def statements for methods, you'll get errors in calling them, of course (a TypeError is what you get, among other cases, whenever the actual arguments specified in the call can't match the formal arguments accepted by the def, of course).
The type error wouldn't be thrown if func had a self argument, like any other instance method.
That's because when you evaluate f.func, you're actually binding f to the first argument of the function -- it then becomes a partial application which you can provide further arguments to.
If you want it to be a static method, then you need the staticmethod decorator which just throws away the first parameter and passes the rest into the original function.
So 2 ways of making it work:
def func(self): pass
-- or --
Foo.func = staticmethod(func)
depending on what you're aiming for.
As written, func is a member of Foo and a method of Foo instances such as your f. However, it can't be successfully called because it doesn't accept at least one argument.
func is in f's namespace and has a _call_() method. In other words, it is enough of a method that Python tries to call it like one when you invoke it like one. If it quacks like a duck, in other words...
That this call doesn't succeed is neither here nor there, in my opinion.
But perhaps a proper response to "But Doctor! When I don't accept any arguments in a function defined in a class, I get confused about whether it's really a method or not!" is simply "Don't do that." :-)

Superfluous python parameters

I've noticed a discrepancy in the way that python parameters are called. In every other language I've dealt with, you either have
foo()
meaning either no parameters, or as many parameters as you like, or
foo(arg1, arg2,...,argn)
where you pass in the same number of parameters to define the function and call it. In python however, I've noticed that the function definitions, and when the function is called, can have two different parameters sets, this usually consists of:
class foo(object):
def bar(self, arg1, arg2):
pass
However, when I want to call the function, all I have to do is:
zoo = foo()
zoo.bar(arg1, arg2)
Where did the self parameter go?
Thank you.
Where did the self parameter go?
It's in front of the dot when you call the function, i.e. in your case it's zoo.
Note that you can also call the function as foo.bar(zoo, arg1, arg2). Basically in python object.method(arguments) is a shortcut for objects_class.method(object, arguments).
zoo is the self parameter.
In C++, for example, you get the object passed implicitly as the this pointer. In Python, this parameter is explicit.
zoo is implicitly passed as the first parameter in your example.
As I remember, "zoo.bar" gives you just an attribute "bar" of object "zoo" that can be called. All magic is done at construction where all methods of class is binded to that object while dictionary of attributes is populated.
Consider next example:
zoo = foo()
xbar = zoo.bar
xbar(arg1, arg2)

Categories