1

Let's say I have this:

from PySide2 import QtWidgets

class MyClass(object):
    def __init__(self, parent=None):
        self.class_variable = QtWidgets.QWidget()

class_instance = MyClass()
variable = class_instance.class_variable
class_instance_returned = mystery_method(variable)  # How to make this return class_instance?

How should I define mystery_method so that it would return the class_instance instance?

The real-world case I have is that I'm sending a QWidget which I'm using as a base instance for .ui file loading into a function. Inside this function I need to figure out which class instance it belongs to.

fredrik
  • 9,631
  • 16
  • 72
  • 132

3 Answers3

2

Python 2.7

class MyClass(object):
    def foo():
        return 'bar'

instance = MyClass()

def mystery_method(method):
    return method.im_self.__class__

print mystery_method(instance.foo)

Python 3

class MyClass(object):
    def foo():
        return 'bar'

instance = MyClass()

def mystery_method(method):
    return method.__self__.__class__

print mystery_method(instance.foo)

EDIT

After the OP was edited:

class ParentClass():
    def foo():
        return 'bar'

class MyClass(object):
    def __init__(self, parent=None):
        self.instance_attribute = ParentClass()

def mystery_method(method):
    return method.__class__

class_instance = MyClass()
print mystery_method(class_instance.instance_attribute)
Ivan Chaer
  • 6,980
  • 1
  • 38
  • 48
  • 1
    Sorry, I realized I had done a big mistake in my original post. I had defined the class as a function. I meant having a class... I just edited my question. – fredrik Apr 03 '17 at 09:14
  • 1
    `foo` is suppose to be a class-level data attribute, not a method. – juanpa.arrivillaga Apr 03 '17 at 09:15
  • Hi @fredrik! There is still something off with the code on your post. See, you set `foo` as a local variable of the `__init__` method, but then you try to pass `foo` to `mystery_method` as if `foo` was a method of `MyClass`. – Ivan Chaer Apr 03 '17 at 09:20
  • @IvanChaer yeah, sorry ...I totally messed up here. I've again edited my question. – fredrik Apr 03 '17 at 09:28
  • @fredrik, If all you need is the class of an object, you can use \__class__. I edited my answer with this in mind. Please, let me know if I understood it correctly. – Ivan Chaer Apr 03 '17 at 09:36
1

One way would we to define foo as a custom property that returns both its value and the related instance when its value is fetched:

from collections import namedtuple


class Prop(object):
    def __init__(self, val):
        self.val = val

    def __get__(self, instance, type):
        return namedtuple('Prop', ('value', 'instance'))(self.val, instance)

    def __set__(self, instance, val):
        self.val = val


class MyClass(object):
    foo = Prop('bar')

Now in your program you can explicitly use its value and the related instance using foo's value and instance attributes respectively.

Demo:

>>> instance = MyClass()
>>> instance.foo
Prop(value='bar', instance=<__main__.MyClass object at 0x10effbcd0>)
>>> instance.foo.value
'bar'
>>> instance.foo.instance
<__main__.MyClass object at 0x10effbcd0>
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
0

In general you cannot (at least not without a lot of searching through all the objects in the system) but if all you want is to find which instances of a class match a particular value then it's fairly easy.

You can create a set of all instances and iterate over them to find what you need.

from weakref import WeakSet
class MyClass(object):
    _instances = WeakSet()

    def __init__(self, foo):
        self._instances.add(self)
        self.foo = foo

    @classmethod
    def findFoo(cls, foo):
        return [instance for instance in cls._instances if instance.foo == foo]


>>> instance1 = MyClass('bar')
>>> instance2 = MyClass('baz')
>>> MyClass.findFoo('baz')
[<__main__.MyClass object at 0x7f6723308f50>]
>>> MyClass.findFoo('bar')
[<__main__.MyClass object at 0x7f6723308c50>]

Note that deleting the object won't remove it immediately, it may not go until garbage collected:

>>> del instance1
>>> MyClass.findFoo('bar')
[<__main__.MyClass object at 0x7f6723308c50>]
>>> import gc
>>> gc.collect()
16
>>> MyClass.findFoo('bar')
[]

However in general you would be better to keep the reference to the original object hanging around and just use that.

Also, note that you cannot reliably tell which instance holds 'bar' if it is stored in more than one object: they could be the same 'bar' or they could be different ones, and whether they are the same or different is an implementation detail.

Duncan
  • 92,073
  • 11
  • 122
  • 156