1

Is there a way in Python to get a reference to an object on which a method was called?

And in case it is, is it possible even in a nested way?

my_class.py:

from modules import math_ops

class A():
    def __init__(self):
        self.math_ops = math_ops.B()
        self.number = 1

modules/math_ops.py:

class B():
    def add_1():
        where_to_add = # Get instance of A() object
        where_to_add.number += 1

To execute this:

>>> a = A()
>>> a.math_ops.add_1()

And get this:

>>> a.number
2

I'm asking because I am interested in writing a static method which works with the object on which it was called, but would like to avoid using the object as an argument as it would be much nicer to call a method like my_object.prop.static_method() instead of my_object.prop.static_method(my_object).

pesekon2
  • 676
  • 1
  • 6
  • 12
  • 2
    If your static method needs the object on which it is called, why not just use a regular instance method? – merlyn Nov 16 '18 at 10:59
  • If you mean using `self` in `B.add_1()`, then it would refer to the instance of `B` (e.g. `A.math_ops`) and not to the instance of `A`, I think. – pesekon2 Nov 16 '18 at 12:27
  • Then I guess the statement you have a problem with is not `my_object.static_method(my_object)`, but rather `my_object.prop.static_method(my_object)`. Is that correct? – merlyn Nov 16 '18 at 12:40
  • Ouch. Right, I forgot to include it there. Sorry. Edited. – pesekon2 Nov 16 '18 at 12:43

2 Answers2

1

If you never plan on reassigning math_ops outside A, this is fairly simple to do.

from modules import math_ops

class A():
    def __init__():
        self.math_ops = math_ops.B(self)
        self.number = 1

modules/math_ops.py:

class B():
    def __init__(self, creator):
        self.creator = creator

    def add_1():
        creator.number += 1

I will mention it again in case you skimmed the first line, the following will generate unexpected results since B is tracking the creator of the object rather than the caller.

a1 = A()
a2 = A()
a1.math_ops = a2.math_ops
a1.math_ops.add_1() # a2 is updated

If that looks like something you might wanna do, the answer is a tad more complicated. Here's my attempt:

from modules import math_ops
class A():
    def __init__(self):
        self._math_ops = math_ops.B(self)
        self.number = 1

    @property
    def math_ops(self):
        self._math_ops.set_caller(self)
        return self._math_ops

    @math_ops.setter
    def math_ops(self, new_math_ops):
        self._math_ops = new_math_ops

modules/math_ops.py:

class B():
    def __init__(self, caller):
        self.caller = caller

    def set_caller(self, caller):
        self.caller = caller

    def add_1(self):
        self.caller.number += 1
merlyn
  • 2,273
  • 1
  • 19
  • 26
  • Looks good, but one question: Wouldn't it create a circular link and therefore also a possibility for memory leaks to keep the reference to the `A` object in `B` as we are also keeping a reference to `B` from `A`? – pesekon2 Nov 16 '18 at 13:52
  • @pesekon2 Not if they don't have delete methods. See [this answer](https://stackoverflow.com/a/8025922/2676937). – merlyn Nov 16 '18 at 14:01
  • Oh, I see. This is for sure the correct answer, just one more question before accepting it: The memory is not released immediately and then when I am creating in a loop a lot of big objects reqriting each other (e.g. in each loop is `a1 = A()`), I can run out of memory when not using `gc.collect()`. Is there another way to avoid it besides including it in the property definition? – pesekon2 Nov 19 '18 at 14:37
  • @pesekon2 You don't have to use `gc.collect()`. The gc in python is enabled by default. It will collect unclaimed objects automatically before you run out of memory. – merlyn Nov 19 '18 at 15:05
-1
class A():
   number = 1

class B():
    def add_1():
        where_to_add = A
        where_to_add.number += 1

B.add_1()
print(A.number)
B.add_1()
print(A.number)
B.add_1()
print(A.number)
Karl
  • 5,573
  • 8
  • 50
  • 73