A recent thread about the proposed extension of set methods raised a question I often wondered about, and which I find interesting because it’s not really settled : is the first parameter of a function defined in a class body (or found in a class’s scope) already special ?
(In context this was more specifically about using an *args parameter to catch the first argument along with many others, but I think the point applies just the same.)
On the one hand, several languages, including C++, JavaScript and Java if memory serves correctly, allow cls.function to be used as a function taking an instance of cls instead of being a method accessed on such an instance. Python functions-in-classes support that as well.
In addition to that, Python-coded functions (in CPython at least) do support passing a value of any type as the first parameter, with no check being made at any point in the process:
>>> class S(str):
... def ad(self, other):
... return self+other
... def __init__(self):
... raise NotImplementedError
...
>>> S.ad(5, 3)
8
Taking advantage of that feature can be useful, in order (for example, and in addition to the example given in the thread) to make a function either extract data on an instance of a certain type, or take the same data directly.
Take the inspect.Signature class for example : it mainly represents a sequence of Parameter objects, with some additional constraints - for one, you can’t order them in any way you want. Now, say I want to create a function on sequences of Parameter objects with any order.
I could make two methods on the Signature class (or my own subclass), one static taking a sequence and one method extracting the list of Parameters from the instance it’s called on and passing it to the static one. But they would need to have different names.
I could make a function outside of the class to take either a Signature or a sequence, but that function really belongs in/with the Signature class so it’s a shame to have to separate them.
I could almost use functools.single_dispatch, to group under the same name the method and static method described above , but I would need to pass the class to the decorator, and that can’t be done from inside the class’s own body.
On the other hand, type checkers completely ignore this “feature” by assuming the first parameter is always an instance of the class, even when you write self=5 for example. Ones I use at least behave as follows:
class A:
def a(self=5):
# self is typed as "A", or as "Self@A"
if isinstance(self, A):
...
else:
# self is typed as "Nothing" here even though this code is perfectly reachable
...
This is also impossible on methods of C-builtin types - the ones I tried it on at least - which could be interpreted as a sign that this is only an implementation detail and not a feature. However, built-in functions are already known as having limitation compared to their pure-Python counterparts : the ability to set arbitrary attributes on them for example, or in some cases to pass parameters by keyword.
I believe this question would gain from being settled. However unprecedented in the stdlib and in native types, is it a feature to be able to pass values of any type to the first parameter of functions defined (in pure Python) in class bodies or found in classes’ scopes ?