dynamic type returning NameError:

Tim O'Callaghan tocallaghan at gmail.com
Mon Jul 29 10:34:22 EDT 2013


On Monday, July 29, 2013 1:43:39 AM UTC-4, Steven D'Aprano wrote:
> On Sun, 28 Jul 2013 18:38:10 -0700, Tim O'Callaghan wrote:
> 
> 
> 
> > 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 classes from 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. 

**

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

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 the request(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? 
 
> 
> 
> > 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.
> 
> 
Was any of this clear? 

And Steven, thank you again. 
> 
> 
> 
> -- 
> 
> Steven



More information about the Python-list mailing list