The following is a derived excerpt from the cookiejar.py file for FileCookieJar and MozillaCookieJar implementation. https://github.com/python/cpython/blame/main/Lib/http/cookiejar.py#L1802
class FCJ():
def __init__(self):
pass
def load(self):
self.__really_load()
class MCJ(FCJ):
def __init__(self):
super().__init__()
def __really_load(self):
pass
if __name__ == "__main__":
m = MCJ()
f = FCJ()
print(m, f)
In case of class FCJ, load is calling a function __really_load
which is not defined in its construct. It is first defined in the subclass “MCJ” which according to my understanding FCJ should not have any information unless it is created as a part of MCJ creation. This seems to be some kind of an abstract class implementation.
But why does python not fail this during initial checks when executing this ?
I am using python3.7 and the code has been there since python2.4 so I think I am missing something very fundamental with respect to how python actually parses the file for errors during execution, but I could not understand what.
1
Python only checks for syntax errors before runtime. And in practice, even that generally happens during runtime, at the moment when an import
statement is executed.
Observe this:
faultymodule.py
:
# This module contains invalid Python code
def bar() # <-- missing colon
print("xx")
main.py
:
if __name__ == "__main__":
print("This will be printed before the error is raised")
import faultymodule # <-- The error will only be raised during the execution of this line
Running main.py
will start running your code, as you can see from the print statement.
As for your specific example, the fact that Python does not check whether _really_load
is defined before runtime is a feature of the language. As long as the function is defined before the invocation of FCJ.load()
, the code would execute fine.
For instance:
class FCJ:
def load(self):
self._really_load()
if __name__ == "__main__":
f = FCJ()
# Calling f.load() now would trigger an AttributeError
def foo():
print("xx")
f._really_load = foo
# But calling f.load() now executes fine
f.load()
By the way, I replaced __really_load
with _really_load
here. Are you aware of the significance of the double leading underscore? See more about name mangling here.