How Should Callables Retrieved With Super() Be Called?
Solution 1:
I am wondering which of the two B.f implementations above is idiomatic
The simpler one is idiomatic.
I can't really see a good reason why a subclass would do isinstance(self, Subclass)
at all, or why a caller would want to do B.f(B)
.
Solution 2:
Typically, there are instance methods (which tend to have self
as the first argument, and no decorator), static methods (which have the @staticmethod
decorator), and class methods (which have the @classmethod
decorator). You will see these two decorators (staticmethod, classmethod) are listed as built-in functions.
When you call B().f()
, since B()
is an instance, there is an implicit transformation of the method into a function whose first argument is the instance, which is described in the docs here.
When you call B.f()
, on the other hand, this does not happen; no extra argument values are inserted (the same docs explain this as well). However, since the method f
is defined with one argument that has no default value, that is where the error "missing 1 required positional argument..." comes in.
As for code conventions, you can add isinstance
checks but oftentimes, the code will assume that the user will call the method with the correct types or from an instance of the correct type, if necessary; otherwise the user will encounter an error. This way, your code can remain simple.
b = B()
# The `self` identifier inside `B.f` will be identical in the following two calls
b.f()
B.f(b) # Would not expect users to call `f` this way unless marked as static
# The `self` identifier in `B.f` below is a class rather than an instance of the class, as in the above two cases
# Typically, you would _not_ expect users to call `f` this way.
B.f(B)
Solution 3:
I have just realised with this A.g
implementation that the issue is not specific to using super()
:
>>> class A:
... def f(self): print('foo')
... def g(self):
... self.f()
... print('bar')
...
>>> A.g(A())
foo
bar
>>> A.g(A)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in g
TypeError: f() missing 1 required positional argument: 'self'
We typically don’t write this A.g
implementation:
>>> class A:
... def f(self): print('foo')
... def g(self):
... if isinstance(self, A): self.f() # bound method call
... else: self.f(self) # function call
... print('bar')
...
>>> A.g(A())
foo
bar
>>> A.g(A)
foo
bar
But should we? I wonder what are these ‘advanced usages’ of non-instance first arguments that Guido van Rossum is referring to.
Post a Comment for "How Should Callables Retrieved With Super() Be Called?"