简体   繁体   中英

Get class of instance variable

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! :)

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) .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM