I have two classes in separate files, a.py and b.py.
# a.py import logging LOG = logging.getLogger(__name__) class A: def name(self): # Do something LOG.debug("Did something") # LOG reads __name__ and print it.
I can not modify a.py in any way.
# b.py import a class B(A): pass
The log output is:
DEBUG:<TIME> called in a, Did something
I want to change
__name__ so that a child class’s call should log
called in b.
My project logs its module name as part of the output. However, when I inherit a class and call the parent class method, I cannot know it is called from
B because it only shows the parent’s module name. How can I change it? Or, some other way to avoid this?
There is a way to do it, but it’s more involved than you might think.
Where does the name
__name__ come from? The answer is that it’s a global variable. This is to be expected, since the global namespace is the module namespace. For example:
>>> globals()['__name__'] '__main__'
If you look up callables in the Standard Type Hierarchy of python’s Data Model documentation, you will find that a function’s
__globals__ attribute is read-only. That means that the function
A.name will always look up its global attributes in the namespace of module
a (not that the module namespace itself is read-only).
The only way to alter globals for a function is to copy the function object. This has been hashed out on Stack Overflow at least a couple of times:
Copying classes has come up as well:
from haggis.objects import copy_class import a class B(copy_class(A, globals(), __name__)): pass
This will copy
A. All function code and non-function attributes will be referenced directly. However, function objects will have the updated
__module__ attributes applied.
The major problem with this method is that it breaks your intended inheritance hierarchy slightly. Functionally, you won’t see a difference, but
issubclass(b.B, a.A) will no longer be true.
Solution 2 (probably the best one)
I can see how it would be a bit cumbersome to copy all the methods of
A and break the inheritance hierarchy by doing this. Luckily, there is another method,
haggis.objects.copy_func, which can help you copy just the
name method out of
from haggis.objects import copy_func import a class B(A): name = copy_func(A.name, globals(), __name__)
This solution is a bit more robust, because copying classes is much more finicky than copying functions in python. It’s fairly efficient because both versions of
name will actually share the same code object: only the metadata, including
__globals__, will be different.
You have a much simpler solution available if you can alter
A ever so slightly. Every (normal) class in python has a
__module__ attribute that is the
__name__ attribute of the module where it was defined.
You can just rewrite
class A: def name(self): # Do something print("Did in %s" % self.__module__)
self.__module__ instead of
__name__ is roughly analogous to using
type(self) instead of
Another simple idea is to properly override
class B(A): def name(self): # Do something LOG.debug("Did something")
Of course I can see how this would not work well if
# Do something is a complex task that is not implemented elsewhere. It’s probably much more efficient to just copy the function object directly out of