How to know where an object was instantiated in Python?

Without getting into the merits of why would you want to do this, here is a way to do it:

# assume the file is saved as "temp.py"

import inspect

class RegisteredObject(object):
    def __new__(cls, *args, **kwargs):
        new_instance = super(RegisteredObject, cls).__new__(cls, *args, **kwargs)
        stack_trace = inspect.stack()
        created_at = '%s:%d' % (
            stack_trace[1][1], stack_trace[1][2])
        new_instance.created_at = created_at 
        return new_instance

    def get_file_of_object_creation(self):
        return self.created_at

class MyObject(RegisteredObject):
    pass

def create_A():
    return MyObject()

def create_B():
    return MyObject()

if __name__ == '__main__':
    t1 = create_A()
    t2 = create_B()
    t3 = create_A()
    t4 = create_B()
    t5 = MyObject()
    print '"t1" was created at "%s"' % t1.get_file_of_object_creation()
    print '"t2" was created at "%s"' % t2.get_file_of_object_creation()
    print '"t3" was created at "%s"' % t3.get_file_of_object_creation()
    print '"t4" was created at "%s"' % t4.get_file_of_object_creation()
    print '"t5" was created at "%s"' % t5.get_file_of_object_creation()

Output:

$ python temp.py
"t1" was created at "temp.py:19"
"t2" was created at "temp.py:22"
"t3" was created at "temp.py:19"
"t4" was created at "temp.py:22"
"t5" was created at "temp.py:29"

This answer is slightly different in that it does not use inspect.stack, considering I observed it to be particularly slow in Python 3.

import inspect


class Locatable:

    def __new__(cls, *_args, **_kwargs):
        # Background: http://eli.thegreenplace.net/2012/04/16/python-object-creation-sequence
        obj = super().__new__(cls)
        obj.location = obj._initialization_location()  # pylint: disable=protected-access
        return obj

    @staticmethod
    def _initialization_location():
        # Background: https://stackoverflow.com/a/42653524/
        frame = inspect.currentframe()
        while frame:
            if frame.f_code.co_name == '<module>':
                return {'module': frame.f_globals['__name__'], 'line': frame.f_lineno}
            frame = frame.f_back

    @property
    def name(self):
        module_name = self.__module__
        class_name = self.__class__.__qualname__  # pylint: disable=no-member
        return module_name + '.' + class_name

The above is a base class that can be inherited from.

The location attribute should contain the name of the module where the class is instantiated, e.g. mypackage.mymodule.


All the caveats about this only being a good idea for debugging aside, you can use the inspect module.

import inspect

def get_caller():
    return inspect.stack()[2]   # 1 is get_caller's caller

def trace_call():
    _, filename, line, function, _, _ = get_caller()
    print("Called by %r at %r:%d" % (function, filename, line))

def main():
    trace_call()

main()

produces

Called by 'main' at 'trace.py':11

Tags:

Python