Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I dynamically instantiate (by classname as a string) the class to call getThings() in this example?

I want to see the source definition for object.__new__() and decided to use this quick way for accessing that code. Why is python giving me a TypeError (is not a type:method), when type() tells me it's a type:method?

(1)
>>> import inspect
>>> print inspect.getsource(object.__new__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 701, in getsource
    lines, lnum = getsourcelines(object)
  File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 690, in getsourcelines
    lines, lnum = findsource(object)
  File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 526, in findsource
    file = getfile(object)
  File "/usr/local/Cellar/python/2.7.3/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 420, in getfile
    'function, traceback, frame, or code object'.format(object))
TypeError: <built-in method __new__ of type object at 0x10a2d9410> is not a module, class, method, function, traceback, frame, or code object

>>> type(object.__new__)
<type 'builtin_function_or_method'>
>>>

The reason I wanted to see the definition in the first place is because the method I'm trying to call requires an instance of a specific class as its first argument.

(2)
TypeError: unbound method getThings() must be called with FunClass instance as first argument (got SadClass instance instead)

It is also worth noting that FunClass is a subclass of SadClass, and as the error message implies, I already have an instance of SadClass. I feel like I have everything I need to call getThings(), but I'm wondering if there's a clever way to get an instance of FunClass, with which I could call getThings()?

I would just declare an instance of what I need, but I'm having trouble because the class and function that end up getting called are based on user method input arguments (strings) which I was using getattr() to get a class object (FunClass), and a method object (getThings). My attempts at creating an instance of FunClass ended up here:

(3)
>>> ClassObject = getattr(FunClass,"FunClass")
>>> ClassObject
<class 'FunClass.FunClass'>
>>> ClassObject.__new__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object.__new__(): not enough arguments
>>>

Ultimately, I just want to be able to call getThings, which I suppose requires an instance of FunClass. I also have a variable ClassMethod, which I tried using to call the method. Again, my class name and method name come in as strings, and I was using getattr() to declare objects, like this:

(4)
>>> ClassMethod = getattr(FunClass, "getThings")
>>> ClassMethod
<unbound method FunClass.getThings>

When I call ClassMethod(), I get the error seen in (4). I might have more than one problem, but insight to any aspect would be great! Thanks.

EDIT:

(as per @nneonneo 's answer)

I ended up doing:

ClassInstance = globals()["FunClass"](self.thing1, self.thing2)
ClassMethod = getattr(ClassInstance, "getThing")
ClassMethod() # to call the function

where "FunClass" and "getThing" are actually strings passed into the function, and could be any number of class/method combinations. Thing1 and Thing2 are required to instantiate the parent class (SadClass) somewhere else, so this allows me to instantiate its subclass, "FunClass" using those existing attributes, and then call one of FunClass's class methods. SadClass has the __init__ and FunClass does not. Problem solved.

like image 578
samstav Avatar asked Apr 09 '13 05:04

samstav


2 Answers

Why are you calling __new__ directly, at all? Just make a new instance like this:

obj = FunClass()

or, to use a dynamic name, globals()['FunClass']().

Then you can just do

obj.getThings()

or, dynamically, getattr(obj, 'getThings')().

By the way, you can't see the source of object.__new__ because it's a built-in method (and so it is implemented at a lower level, e.g. C for CPython).

like image 174
nneonneo Avatar answered Sep 22 '22 11:09

nneonneo


If you really need to call __new__ directly for some reason, instead of just calling the class per nneonneo's answer, you need to understand that __new__ - for whatever reason - isn't actually a classmethod, even though it has the signature of one, and you need to pass in the class explicitly:

inst = ClassObject.__new__(ClassObject)

should work. But don't forget that calling the class itself doesn't stop there - you probably want to do the rest of the work as well by calling inst.__init__().

like image 36
lvc Avatar answered Sep 21 '22 11:09

lvc