Method parameter auto completion for a class instance - python

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.

Related

How to specify type for function parameter (Python)

I want to restrict scope of functions that can be passed as parameter to another function. For example, to restrict functions to be only one from two specified, or from particular module, or by signature. I tried the code below but in it there is now restrictions: as parameter can be passed any function.
Is this possible in Python?
def func_as_param():
print("func_as_param called")
def other_func():
print("other_func called")
def func_with_func_arg(func: func_as_param): # this is not giving restrictions
# def func_with_func_arg(func: type(func_as_param)): # this is also not giving restrictions
print("func_with_func_arg called")
func()
def test_func_with_func_arg():
print("test_func_with_func_arg")
func_with_func_arg(func_as_param)
func_with_func_arg(other_func) # <- here IDE must complain that only func_as_param is expected
Frameworks expecting callback functions of specific signatures might be type hinted using Callable[[Arg1Type, Arg2Type], ReturnType]
You might want to use the Callable type. This might help https://docs.python.org/3/library/typing.html#callable
NOTE
Type annotations in Python are not make-or-break like in C. They’re optional chunks of syntax that we can add to make our code more explicit.
Erroneous type annotations will do nothing more than highlight the incorrect annotation in our code editor — no errors are ever raised due to annotations.
If thats necessary, you must do the checking by yourself.
normally this is done by creating your own type (class) ... then any other function can inherit from it and will be of the same "type".
class my_functions:
pass
class func_as_param_class(my_functions):
#staticmethod
def __call__():
print("func_as_param called")
func_as_param = func_as_param_class() # create the callable function ....
def other_func():
print("other_func called")
def func_with_func_arg(func: my_functions): # only my_functions are accepted.
# def func_with_func_arg(func: type(func_as_param)):
print("func_with_func_arg called")
func()
def test_func_with_func_arg():
print("test_func_with_func_arg")
func_with_func_arg(func_as_param)
func_with_func_arg(other_func) # <- here IDE must complain that only func_as_param is expected
in the above code, my IDE (pycharm) does complain that other_func is of the wrong type, this however doesn't do any restriction at runtime, it only allows IDE linter and mypy to issue warning on violation.
Edit: removed the arguments by declaring the call function as static.
I don't know if it's just me that Python doesn't make any changes to the type Annotations
Syntax :
def greeting(name: str) -> str:
return 'Hello ' + name
you can find more here: https://docs.python.org/3/library/typing.html

Autocomplete and linting with class methods defined in separate file

I have written a class in python 3.8 and for reasons, I need some of the class methods to be defined in a different file. The method I used for putting this function as the class method was by adding it later with setattr. This allows me to selectively add methods I want, I wont get into too many details on that because its irrelevant
myClass.py:
# This bit will scan the external file and add relevant methods to the class via a decorator
# just to give an idea, don't get hungh up on the implementation
def add_methods():
def decorator(Class):
import methods from external.py
for method in external.py:
if method is what I want:
setattr(Class, method_name, method)
#add_method()
def myClass():
def __init__(self):
dostuff
def method_1(self, arg1 , arg2):
dostuff
def method_2(self, arg1 , arg2, arg3):
dostuff
Inside another folder, we got the method file I mentioned
method_folder/external.py
def ext_added_method_1(self):
do stuff here
def ext_added_method_2(self):
do stuff here
Then finally, the class is used by a code
main.py
from myClass import myClass
handler = myClass()
dostuff
This all works just fine, the externally added methods are there and work just as expected. but there is one annoying bit that is when I'm working on the 'external.py' file which is where most of the coding is done anyway. My VSCode understandably does not know what is inside of the 'self' parameter of ext_added_method_1 and ext_added_method_2, so there is no linting.
The question is, is there a way of telling vscode, that on external.py, the 'self' parameter should be linted from the myClass object?
I don't need linting of the methods inside external.py (ext_added_method_1 and ext_added_method_2) I know that would be impossible since they are dynamically added. But for the methods that are defined in myClass.py, such as method_1 and method_2 I would like to have autocomplete and all that magical stuff.
Any way to achieve that without changing the architecture?
thank you!
Thx rioV8m, I wasn't familiar with that term. But then I looked it up and came up with the following solution
external.py
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from myClass import myClass
def ext_added_method_1(self: 'myClass'):
do stuff here
def ext_added_method_2(self: 'myClass'):
do stuff here
With that import check, I have no issues with circular imports so it basically ignores that, but is enough for VSCode to pick up on the class.
Also, I add the type in single quotes which are called 'forward references'. This is then only used by the editor to inform it of the class contents.

Meaning of "..." in Python function signature

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?

Why is a method of a Python class declared without "self" and without decorators not raising an exception?

I thought that the following code would result in an error because as far as I have read, a method in a Python class must either have "self" (or any other label, but "self" by convention) as its first argument, or "cls" or similar if the #classmethod decorator is used, or none if the #staticmethod decorator is used.
How come I get no error running this with Python 3.5 in the Terminal, even though test_method does not meet these requirements? It seems to work fine as a static method, but without the decorator.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
class MyClass:
def test_method(args):
print(args[1])
#staticmethod
def static_method():
print("static_method")
#classmethod
def class_method(cls):
print("class_method")
def main(args):
MyClass.test_method(args)
if __name__ == '__main__':
sys.exit(main(sys.argv))
Output:
$ python3 testscript.py "testing"
$ testing
EDIT:
My question could also be phrased differently, drawing attention away from self and to #staticmethod: "How come I'm getting a seemingly working static method without the #staticmethod decorator?"
In Python 2, functions defined in a class body are automatically converted to "unbound methods", and cannot be called directly without a staticmethod decorator. In Python 3, this concept was removed; MyClass.text_method is a simple function that lives inside the MyClass namespace, and can be called directly.
The main reason to still use staticmethod in Python 3 is if you also want to call the method on an instance. If you don't use the decorator, the method will always be passed the instance as the first parameter, causing a TypeError.
There is nothing special about this. In python 3 there is no difference between a function defined inside a class or a function defined outside a class. Both of them are normal functions.
The self that you are talking about here or maybe cls comes into picture only when you access the function through an instance. Hence here you didn't get any error.
However if you modify your code just a little bit to look like the following, then you'd get an error that you expected.
def main(args):
MyClass().test_method(args)
# Should throw an error
EDIT:
#staticmethod will work on both class instances like MyClass().test_method(args)and just a regular direct call like MyClass.test_method(args)
However a regular method(without self in it) can't be called on a class instance. So you will always have to call it as MyClass.test_method(args)
self isn't necessarily required. However, if you want to reference any variable or value that is associated with the object(instantiation of the class) (E.g. for a class about cars, it's speed, self.speed) you'll need to have self as a parameter in the function. For this reason, it's common practice to always have self as an argument, otherwise you aren't really using the class for the right reason.
EDIT:
This will actually throw an error if you do the following:
class a():
def __init__(self, x):
self.asd = x
def hello(x):
print(x)
>>> g = a(4)
>>> g.hello(5)
as when calling "hello", both "self" and "4" will be passed as parameters. It would work in the following instance, which is what I was saying above:
>>> g = a
>>> g.hello(4)
or
>>> a.hello(4)
To add on to the existing answers here and provide a code example:
class MyClass:
def __init__(self):
pass
def myStaticMethod():
print("a static method")
#staticmethod
def myStaticMethodWithArg(my_arg):
print(my_arg)
print("a static method")
MyClass.myStaticMethod()
MyClass.myStaticMethodWithArg("skhsdkj")
abc = MyClass()
abc.myStaticMethodWithArg("avc")
Try removing the #staticmethod decorator and rerunning the code and see what happens! (The very last call will fail since the method is passed in both self and the string input. By adding the decorator, we can guide the interpreter to perform our desired action)

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