Reputation: 139
I try to get the class of an instance variable, I think that is very simple, but at the moment I can not figure out that.
class A():
def __init__(self):
self.a = 1
class B():
def __init__(self):
self.b = A()
def getClassOfVariable(abc):
print(abc)
getClassOfVariable(B().b)
<__main__.A object at 0x7f4b2652ac18>
For example I have a function where I pass B().b as argument (whyever) to a function and in this function I need the class of the variable where is it defined, so class B() is what I want in the function. What I know is only that in the function getClassOfVariable I get only classes like B.
Thanks for helping! :)
Upvotes: 1
Views: 329
Reputation: 23753
You could use a descriptor for the attribute. In the descriptor's __set__
method, add an attribute to its value that can be inspected inside the function.
from weakref import WeakKeyDictionary
class A:
def __init__(self):
self.a = 1
class X:
"""A descriptor that knows where it was defined"""
def __init__(self, default):
self.default = default
self.data = WeakKeyDictionary()
def __get__(self, instance, owner):
# we get here when someone calls x.d, and d is an X instance
# instance = x
# owner = type(x)
return self.data.get(instance, self.default)
def __set__(self, instance, value):
# we get here when someone calls x.d = val, and d is an X instance
# add an attribute to the value and assign the instance
#value.defining_class = instance
value.defining_class = instance.__class__
self.data[instance] = value
class B:
b = X(None)
def __init__(self):
self.b = A()
def getClassOfVariable(abc):
print(f'abc: {abc}')
print(f'defining_class: {abc.defining_class}')
getClassOfVariable(B().b)
Result:
abc: <__main__.A object at 0x.....>
defining_class: <class '__main__.B'>
I adapted this descriptor from Python Descriptors Demystified which I always refer to when writing a descriptor.
Caveat: Although this works, this feels like a hack and certainly hasn't been tested for pitfalls. In this example an instance of A
has had an attribute added that says it was defined in B
; if that instance gets passed around a bit it might lose its context and upon introspection that added attribute might seem strange indeed. -but for this simple example it seems OK. Maybe downvotes with comments, or even edits will elucidate. It seems too easy to just add an attribute and maybe there should be some protection involved. Or maybe value.defining_class
should just be the string instance.__class__
- edited to that effect
Upvotes: 0
Reputation: 30151
You can't do that.
In Python, variables are just names for values. A value can have many names, for example 60
might be called seconds_per_minute
, minutes_per_hour
, or even speed_limit_mph
, and these names obviously have nothing to do with each other. A value can also have no name at all, for example print(60)
doesn't give 60
any name.
An important point to remember is that when you call a function, your arguments are passed by assignment. That is, the function's parameters become new names for the values that you passed in. So the called function has no idea what name you use for the object you passed, it just knows its own name for that object.
In this case, the object itself doesn't know what class it was created in. You know it, because you know the name of the object (it's B().b
). But the name of the object is not passed to the called function, so getClassOfVariable
has no way of determining which class your A
object was created in.
So, how to work around this limitation? The simplest route is to provide this information to your A
object in its constructor, by passing type(self)
(or self.__class__
, for Python 2.x classic classes) as an argument to A()
and handling it in the A.__init__()
method, like this:
class A():
def __init__(self, owner=None):
self.a = 1
self.owner = owner
class B():
def __init__(self):
self.b = A(type(self))
You can then inspect the B().b.owner
attribute to find out which class created your A
object. However, if you create a subclass of B
, then type(self)
will be that subclass and not B
. If you still need to get B
in that case, then you should pass B
instead of type(self)
.
Upvotes: 2