1

From the introspection perspective is there a way to get a class instances variables without creating an instance. When an instance is created it is easy:

class Super:
    def __init__(self, name):
        self.name = name

class Sub(Super):
    pass

a = Super("Nice_Name")

print(a.__dict__.keys())

But I want to get that information without creating an instance, just to introspect. Is there way to do?

Vy.Iv
  • 829
  • 2
  • 8
  • 17
  • 1
    See http://stackoverflow.com/questions/109087/how-to-get-instance-variables-in-python – jonrsharpe Apr 24 '16 at 22:00
  • What would their values be if they had no instance? – Natecat Apr 24 '16 at 22:01
  • 4
    The problem is that class instances don't have any attributes at all until they are assigned; the class does not "declare" them. `__init__` simply assigns values to attributes created on the fly after the instance is first created. Essentially, you are asking to simulate the execution of `__init__`. – chepner Apr 24 '16 at 22:03
  • 2
    More so the `__init__` function may not even create a deterministic set of properties. It could depend on arguments, global state, or even be something absurd such as `setattr(self, str(random.random()), "derp")` – Matti Virkkunen Apr 24 '16 at 22:09
  • 1
    OK, but lets say if I will try to use Sub class and doesn't assign any value to it. It will give me error. As Sub "knows" that it needs "name". The same with Super class. So that means that somehow that information is there. So if subclass knows where to look for instance variables. Can't I do that? I hope that I am not mixing something here as I am quite new to Python. – Vy.Iv Apr 24 '16 at 22:18
  • @Vy.Iv: Can you post an example of code where that error happens? – Matti Virkkunen Apr 24 '16 at 22:33
  • 1
    @Vy.Iv It won't give you an error. – Natecat Apr 24 '16 at 22:44
  • 1
    So, lets say a = Sub() and error: TypeError: __init__() takes exactly 2 arguments (1 given). As I understand from comments and Natecat answer, when I don't add anything it raises errror as it tries to create instance and it can see that intance variable is missing. Before I do that it is not there. Is that the case? – Vy.Iv Apr 24 '16 at 22:44
  • 1
    @Vy.Iv: That error isn't related to instance variables - it's related to the arguments to the constructor function (`__init__`). And those you could indeed find out with introspection, but they're not what you're after. – Matti Virkkunen Apr 24 '16 at 22:45
  • @Matti Virkkunen: It is a bit related, atleast it will solve my problem, I think. Just to be sure that I understand it. Those arguments using constructor function will be hooked to the instance variable that we can't introspect without instance creation, but we can inspect arguments without instance creation? – Vy.Iv Apr 24 '16 at 23:09
  • 2
    @Vy.Iv: The arguments have nothing to with instance variables. They're just arguments to the function. It's free to do whatever it wants with them. – Matti Virkkunen Apr 24 '16 at 23:11
  • 1
    @Vy.Iv: You're suffering from a misconception, that "`Sub` 'knows' that it needs `name`.". It doesn't, as you can see by looking at it. When you call _any_ instance method, including the invisible call to `__init__`, Python searches the instance for that method. If the method isn't found (it isn't, because `Sub` doesn't have / "know about" `__init__`), Python searches related classes in Method Resolution Order until it finds the method or gives up. The next class is `Super`, which _does_ have an `__init__`. Python runs `Super`'s `__init__`, assigning the new `Sub` instance to `self`. – Kevin J. Chase Apr 25 '16 at 01:01
  • 1
    @Vy.Iv: Assuming you come from some other OOP language... `__init__` is an _initializer_, not a constructor. (The constructor is `__new__` --- you will rarely if ever fiddle with it.) By the time the initializer runs, the instance of `Sub` has already been constructed (containing only Python-provided fields like `__class__` and `__dict__`). That's how there is an instance of _anything_ to assign to the `self` parameter of `WhicheverClass.__init__`. The rest of the `__init__` parameters are assigned using the arguments from your (implicit) call to the initializer: `sub = Sub("Whatever")`. – Kevin J. Chase Apr 25 '16 at 01:15

2 Answers2

0

No, you cannot, because python is not a statically typed language, so before an object is instantiated, that object has no type. For example, imagine a class like this:

class A:
def __init__(self):
    self.a = "a"

def change_type(self):
    self.a = 1

What would the type of self.a be before an object is instantiated? If you need introspection, you cannot use python.

Community
  • 1
  • 1
Natecat
  • 2,175
  • 1
  • 17
  • 20
  • Simply because this is not an answer or is a link-only answer. put this as comment. Just look at the other persons comments to the question: they are longer and more constructive. SO rules, sorry – DevLounge Apr 24 '16 at 22:09
  • @Apero I disagree. His question is wholly answered by this, and the link is to clarify what a statically typed language IS, and by it self doesn't answer his question. – Natecat Apr 24 '16 at 22:11
  • Indeed, so this should be a comment, not an answer ;-) – DevLounge Apr 24 '16 at 22:13
  • @Apero I was talking about the link, not the answer. – Natecat Apr 24 '16 at 22:42
0

I've been doing some research into this and there appear to be a few scenarios where you can do this, however no perfect solution:

  1. If you define instance variables using @property decorators, then it will show up when doing dir(class_type)
  2. Similarly, you can define the allowed list of instance attributes using __slots__ magic var, and then just do class_type.__slots__ to get your instance vars.
  3. A bit more hacky, but you can read the source code of the class, and then look for any references to self.varname = expression. This of course assumes self will is always how the class instance is referenced, and would fail in scenarios like inner classes or self being reassigned to another varname. Also wouldn't pick up things like setattr(self, varname, expression), which you'd need other logic to detect.
  4. Probably more robust than option #3, you could use the ast module and use that to parse the class source code. Then do some searching to find expressions that might write attributes to the class self.

Check out this example:

import dill, re

class Test:
    __slots__ = ["x","y","z"]
    def __init__(self):
        self.x = 0
        self.y = 1
        self.z = 2
    @property
    def q(self):
        return self.x

# this grabs those defined using __slots__ or @property
print(dir(Test))

# this looks at class source
# may need to play around with the regex more...
s = dill.source.getsource(Test)
var_finder = re.compile("self\.([^\W]+)\s*\=")
print(set(var_finder.findall(s)))

I personally think adding __slots__ on your class is the best approach. For sphinx documentation, which was my use case, that seems to be the best way to get autosummary directive to pick up those instance variables.

Azmisov
  • 6,493
  • 7
  • 53
  • 70