dynamic type returning NameError:

T

Tim O'Callaghan

Hi,

I hope that this hasn't been asked for the millionth time, so my apologies if it has.

I have a base class (BaseClass - we'll call it for this example) with an http call that i would like to inherit into a dynamic class at runtime. We'll call that method in BaseClass; 'request'.

I have a dictionary(json) of key (class name): value(method) that I would like to create inheriting this 'request' method from the BaseClass. So the derived class would look something like this

definition in json:
{"Whatever": [{"method1": "Some Default", "async": True},{"method2": "Some Other Default", "async": True}]}

Ideally I'd like the class def to look something like this if i were to type it out by hand

[excuse the indents]

class Whatever(BaseClass):
def method1(self):
stupid_data = super(Whatever, self).request("method1")
return stupid_data

def method2(self):
stupid_data = super(Whatever, self).request("method1")
return stupid_data

Now, I've been trying to do this using the python cli, with out success.

So, attempting this at runtime I get a plethora of wonderful errors that I suspect has broken my brain.

Here is what i've tried:

# trying with just an empty object of type BaseClass
obj = type("Object", (BaseClass,), {})

whatever = type("WhatEver", (obj,), {"method1": super(WhatEver, self).request("method1")})

but when i try this I get 'NameError: name 'self' is not defined'

defining these classes manually works...

I hope that this was clear enough, apologies if it wasn't. It's late(ish), I'm tired and borderline frustrated :) But enough about me...

Thanks in advance.
 
T

Terry Reedy

Hi,

I hope that this hasn't been asked for the millionth time, so my apologies if it has.

I have a base class (BaseClass - we'll call it for this example) with an http call that i would like to inherit into a dynamic class at runtime. We'll call that method in BaseClass; 'request'.

I have a dictionary(json) of key (class name): value(method) that I would like to create inheriting this 'request' method from the BaseClass. So the derived class would look something like this

definition in json:
{"Whatever": [{"method1": "Some Default", "async": True},{"method2": "Some Other Default", "async": True}]}

Ideally I'd like the class def to look something like this if i were to type it out by hand

[excuse the indents]

class Whatever(BaseClass):
def method1(self):
stupid_data = super(Whatever, self).request("method1")
return stupid_data

def method2(self):
stupid_data = super(Whatever, self).request("method1")
return stupid_data

Now, I've been trying to do this using the python cli, with out success.

So, attempting this at runtime I get a plethora of wonderful errors that I suspect has broken my brain.

Here is what i've tried:

# trying with just an empty object of type BaseClass
obj = type("Object", (BaseClass,), {})

whatever = type("WhatEver", (obj,), {"method1": super(WhatEver, self).request("method1")})

'method1' has to be mapped to a function object.
 
T

Tim O'Callaghan

Hi,

I hope that this hasn't been asked for the millionth time, so my apologies if it has.

I have a base class (BaseClass - we'll call it for this example) with an http call that i would like to inherit into a dynamic class at runtime. We'll call that method in BaseClass; 'request'.

I have a dictionary(json) of key (class name): value(method) that I would like to create inheriting this 'request' method from the BaseClass. So the derived class would look something like this

definition in json:
{"Whatever": [{"method1": "Some Default", "async": True},{"method2": "Some Other Default", "async": True}]}
Ideally I'd like the class def to look something like this if i were to type it out by hand
[excuse the indents]
class Whatever(BaseClass):
def method1(self):
stupid_data = super(Whatever, self).request("method1")
return stupid_data

def method2(self):
stupid_data = super(Whatever, self).request("method1")
return stupid_data

Now, I've been trying to do this using the python cli, with out success.

So, attempting this at runtime I get a plethora of wonderful errors that I suspect has broken my brain.

Here is what i've tried:

# trying with just an empty object of type BaseClass
obj = type("Object", (BaseClass,), {})

whatever = type("WhatEver", (obj,), {"method1": super(WhatEver, self).request("method1")})



'method1' has to be mapped to a function object.

But isn't that what calling super is doing? Calling the function object of the parent class BaseClass?
 
S

Steven D'Aprano

Hi,

I hope that this hasn't been asked for the millionth time, so my
apologies if it has. [...]
I hope that this was clear enough, apologies if it wasn't.

Clear as mud.

It's late(ish), I'm tired and borderline frustrated :)

I see your smiley, but perhaps you would get better results by waiting
until you can make a better post.

It *really* helps if you post actual "working" (even if "working" means
"fails in the way I said"), *short*, *simple* code. Often you'll find
that trying to simplify the problem gives you the insight to solve the
problem yourself.

http://www.sscce.org/


I'm going to try to guess what you're attempting, but I may get it
completely wrong. Sorry if I do, but hopefully you'll get some insight
even from my misunderstandings.

I have a base class (BaseClass - we'll call it for this example) with an
http call that i would like to inherit into a dynamic class at runtime.
We'll call that method in BaseClass; 'request'.

If I read this literally, you want to do this:

class BaseClass(DynamicParent):
def request(self):
...

except that DynamicParent isn't known until runtime. Am I close?

Obviously the above syntax won't work, but you can use a factory:

def make_baseclass(parent):
class BaseClass(parent):
def request(self):
...
return BaseClass

class Spam: ...

BaseClass = make_baseclass(Spam)


Or you can use the type() constructor directly:

BaseClass = type('BaseClass', (Spam,), dict_of_methods_and_stuff)


which is probably far less convenient. But all this assumes I read you
literally, and reading on, I don't think that's what you are after.

I have a dictionary(json) of key (class name): value(method) that I
would like to create inheriting this 'request' method from the
BaseClass. So the derived class would look something like this

definition in json:
{"Whatever": [{"method1": "Some Default", "async": True},{"method2":
"Some Other Default", "async": True}]}


Pure gobbledygook to me. I don't understand what you're talking about,
how does a derived class turn into JSON? (Could be worse, it could be
XML.) Is BaseClass the "derived class", or are you talking about
inheriting from BaseClass? What's "Some Default"? It looks like a string,
and it certainly isn't a valid method name, not with a space in it.

Where did async and method2 come from? How do these things relate to
"request" you talk about above? I think you're too close to the problem
and don't realise that others don't sharing your knowledge of the problem.

But, moving along, if I've understood you correctly, I don't think
inheritance is the right solution here. I think that composition or
delegation may be better. Something like this:

class BaseClass:
def request(self):
# Delegate to a method set dynamically, on the instance.
return self.some_method()


a = BaseClass()
a.some_method = one_thing.method1

b = BaseClass()
b.some_method = another_thing.method2


Now you have instance a.request calling method1 of another object, and
b.request calling method2 of a different object. Does that solve your
problem, or am I on a wild-goose chase?

Ideally I'd like the class def to look something like this if i were to
type it out by hand

[excuse the indents]

class Whatever(BaseClass):
def method1(self):
stupid_data = super(Whatever, self).request("method1")
return stupid_data

def method2(self):
stupid_data = super(Whatever, self).request("method1")
return stupid_data


Since request is not the method you are currently in, the above is
equivalent to:

class Whatever(BaseClass):
def method1(self):
return self.request("method1")
def method2(self):
return self.request("method2")

where "request" is defined by BaseClass, and assuming you don't override
it in the subclass. (I assume "method1" in your code above was a typo.)

Now, I've been trying to do this using the python cli, with out success.

So, attempting this at runtime I get a plethora of wonderful errors that
I suspect has broken my brain.

Here is what i've tried:

# trying with just an empty object of type BaseClass
obj = type("Object", (BaseClass,), {})

"obj" here is a class called "Object", inheriting from BaseClass. It
overrides no methods. Why does it exist? It doesn't do anything.

whatever = type("WhatEver", (obj,), {"method1": super(WhatEver,
self).request("method1")})

but when i try this I get 'NameError: name 'self' is not defined'

This is what you are doing:

* look up names WhatEver and self, in the current scope (i.e. the scope
where you are running this call to type, which is likely the global
scope);

* pass those objects (if they exist!) to super(), right now;

* on the object returned, look up the attribute "request", right now;

* call that object with string argument "method1", right now;

* take the result of that sequences of calls, let's call it x, and set it
in a new dict with key "method1";

* and then create a new type using dict {'method1': x}.


Notice that everything you do is done immediately. You should be getting
a NameError for WhatEver too, but since you are not, I can only imagine
that you have already (accidentally?) defined something, anything, with
that name.

What you actually want (but probably not what you *need*, see above about
delegation) is to delay evaluation of super() and subsequent calls until
after your WhatEver class is created and initialised and the method1
*method* is called. That means putting it all those calls inside a
function object, for later use, instead of executing them directly *right
now*:

def method1(self):
return super(WhatEver, self).request("method1")

WhatEver = type("WhatEver", (BaseClass,), {"method1": method1})

You could alternatively use lambda:

WhatEver = type("WhatEver", (BaseClass,),
{"method1":
lambda self: super(WhatEver, self).request("method1")
}
)


Note that the class name inside super() *must* match the global name the
class is assigned to, "WhatEver". The internal class __name__ -- the
first argument to type() -- doesn't have to match, but it should, to
avoid confusion.

The above should fix the NameError you are getting, and it might even
work, but I think delegation is a better solution to this problem.
 
T

Tim O'Callaghan

Hi,

I hope that this hasn't been asked for the millionth time, so my
apologies if it has.
[...]

I hope that this was clear enough, apologies if it wasn't.



Clear as mud.
Alright, let me see if I can clear this up. And by the way, thanks for chiming in on this. It's appreciated.

I have a 3rd party api definition that I'm using to generate python classesfrom so that I can access this api using python. The api definition currently changes, so what I've done is saved a local copy of the html (the api definition from the vendor) and screen scraped the categories, and methods for this api. So when the api changes, I can just get a fresh definition from the vendors site, parse the html, and generate the classes again. This screen scape is saved to a json object in the format I originally mentioned:

returned from json from screen scrape:
{"Whatever": [{"method1": "Some Default", "async": "True"},{"method2": "Some Other Default", "async": "True"}]}


**note:

"method1": "Some Default"
"method2": "Some Other Default"

are just dummy values.

**
I see your smiley, but perhaps you would get better results by waiting

until you can make a better post.



It *really* helps if you post actual "working" (even if "working" means

"fails in the way I said"), *short*, *simple* code. Often you'll find

that trying to simplify the problem gives you the insight to solve the

problem yourself.



http://www.sscce.org/

I would normally post 'working' code, but I'm really not there yet. All I've been doing up until this point is basically proof of concept.
I'm going to try to guess what you're attempting, but I may get it

completely wrong. Sorry if I do, but hopefully you'll get some insight

even from my misunderstandings.










If I read this literally, you want to do this:



class BaseClass(DynamicParent):

def request(self):

...



except that DynamicParent isn't known until runtime. Am I close?

The parent is the stable/static part. That has the http request method to communicate with the vendor api. The request method in BaseClass creates therequest(signs and authorizes the call).

Right now the BaseClass.request("api_call_to_vendor") will work and return data, but again I would like to separate each api category into classes with the appropriate methods.
Obviously the above syntax won't work, but you can use a factory:



def make_baseclass(parent):

class BaseClass(parent):

def request(self):

...

return BaseClass



class Spam: ...



BaseClass = make_baseclass(Spam)





Or you can use the type() constructor directly:



BaseClass = type('BaseClass', (Spam,), dict_of_methods_and_stuff)
This, on the surface is what I'm after. Except the 'dict_of_methods_and_stuff' call would be something like this:

Vendor_API_Cateogry = type("Vendor_API_Category", (BaseClass,), {"api_call_from_vendors_category": "call_supers_request_method_passing_in_vendor_call"})

resulting in a call something like this:

vendor_category = Vendor_API_Category()
vendor_category.api_call_from_vendors_category()
which is probably far less convenient. But all this assumes I read you

literally, and reading on, I don't think that's what you are after.




I have a dictionary(json) of key (class name): value(method) that I
would like to create inheriting this 'request' method from the
BaseClass. So the derived class would look something like this

definition in json:
{"Whatever": [{"method1": "Some Default", "async": True},{"method2":
"Some Other Default", "async": True}]}





Pure gobbledygook to me. I don't understand what you're talking about,

how does a derived class turn into JSON? (Could be worse, it could be

XML.) Is BaseClass the "derived class", or are you talking about

inheriting from BaseClass? What's "Some Default"? It looks like a string,

and it certainly isn't a valid method name, not with a space in it.



Where did async and method2 come from? How do these things relate to

"request" you talk about above? I think you're too close to the problem

and don't realise that others don't sharing your knowledge of the problem..

Agreed.
But, moving along, if I've understood you correctly, I don't think

inheritance is the right solution here. I think that composition or

delegation may be better. Something like this:



class BaseClass:

def request(self):

# Delegate to a method set dynamically, on the instance.

return self.some_method()





a = BaseClass()

a.some_method = one_thing.method1



b = BaseClass()

b.some_method = another_thing.method2





Now you have instance a.request calling method1 of another object, and

b.request calling method2 of a different object. Does that solve your

problem, or am I on a wild-goose chase?




Ideally I'd like the class def to look something like this if i were to
type it out by hand
[excuse the indents]
class Whatever(BaseClass):
def method1(self):
stupid_data = super(Whatever, self).request("method1")
return stupid_data

def method2(self):
stupid_data = super(Whatever, self).request("method1")
return stupid_data





Since request is not the method you are currently in, the above is

equivalent to:



class Whatever(BaseClass):

def method1(self):

return self.request("method1")

def method2(self):

return self.request("method2")



where "request" is defined by BaseClass, and assuming you don't override

it in the subclass. (I assume "method1" in your code above was a typo.)




Now, I've been trying to do this using the python cli, with out success..

So, attempting this at runtime I get a plethora of wonderful errors that
I suspect has broken my brain.

Here is what i've tried:

# trying with just an empty object of type BaseClass
obj = type("Object", (BaseClass,), {})



"obj" here is a class called "Object", inheriting from BaseClass. It

overrides no methods. Why does it exist? It doesn't do anything.
'obj" was just an example. I was trying to inherit from BaseClass (attempting to get the BaseClass.request method into 'obj') and then try and extend that?
This is what you are doing:



* look up names WhatEver and self, in the current scope (i.e. the scope

where you are running this call to type, which is likely the global

scope);



* pass those objects (if they exist!) to super(), right now;



* on the object returned, look up the attribute "request", right now;



* call that object with string argument "method1", right now;



* take the result of that sequences of calls, let's call it x, and set it

in a new dict with key "method1";



* and then create a new type using dict {'method1': x}.





Notice that everything you do is done immediately. You should be getting

a NameError for WhatEver too, but since you are not, I can only imagine

that you have already (accidentally?) defined something, anything, with

that name.



What you actually want (but probably not what you *need*, see above about

delegation) is to delay evaluation of super() and subsequent calls until

after your WhatEver class is created and initialised and the method1

*method* is called. That means putting it all those calls inside a

function object, for later use, instead of executing them directly *right

now*:



def method1(self):

return super(WhatEver, self).request("method1")



WhatEver = type("WhatEver", (BaseClass,), {"method1": method1})



You could alternatively use lambda:



WhatEver = type("WhatEver", (BaseClass,),

{"method1":

lambda self: super(WhatEver, self).request("method1")

}

)





Note that the class name inside super() *must* match the global name the

class is assigned to, "WhatEver". The internal class __name__ -- the

first argument to type() -- doesn't have to match, but it should, to

avoid confusion.



The above should fix the NameError you are getting, and it might even

work, but I think delegation is a better solution to this problem.
Was any of this clear?

And Steven, thank you again.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top