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 A
or B
because it only shows the parent’s module name. How can I change it? Or, some other way to avoid this?
Advertisement
Answer
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:
Solution 1
I’ve gone ahead and implemented both techniques in a utility library I made called haggis. Specifically, haggis.objects.copy_class
will do what you want:
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 __globals__
and __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 A
:
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.
Solution 3
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 A
as
class A: def name(self): # Do something print("Did in %s" % self.__module__)
Using self.__module__
instead of __name__
is roughly analogous to using type(self)
instead of __class__
.
Solution 4
Another simple idea is to properly override A.name
:
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 A
.