Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Create classes at runtnime

Reply
Thread Tools

Create classes at runtnime

 
 
Marc Aymerich
Guest
Posts: n/a
 
      02-04-2011
Hi!
I need to create a pretty complex class at runtime. something like
this one:

(note: "...." means that the number of attributes can be variable)

class VirtualUserLimitForm(ModelForm):
swap_limit = forms.CharField(max_length=100,
initial=monitor1.default_limit)
memory_limit = forms.CharField(max_length=100,
initial=monitor2.default_limit)
...

class Meta:
model = model

def __init__(self, *args, **kwargs):
super(VirtualUserLimitForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
self.fields['swap_limit'].initial =
kwargs['instance'].monitoring.filter(monitor=monitor1)[0].current
self.fields['memory_limit'].initial =
kwargs['instance'].monitoring.filter(monitor=monitor2)[0].current
...

I can generate all the needed code as string and then use exec(), but
it seems ugly to me. I'm wondering if there is another way more
elegant to do that? metaclasses maybe? What is your recommendation?

Thanks!!
 
Reply With Quote
 
 
 
 
Peter Otten
Guest
Posts: n/a
 
      02-04-2011
Marc Aymerich wrote:

> I need to create a pretty complex class at runtime. something like
> this one:


I have a hunch that you've never heard the famous Kernighan quote:

"Everyone knows that debugging is twice as hard as writing a program in the
first place. So if you're as clever as you can be when you write it, how
will you ever debug it?"

Or that if you've heard it you don't heed it.

> (note: "...." means that the number of attributes can be variable)
>
> class VirtualUserLimitForm(ModelForm):
> swap_limit = forms.CharField(max_length=100,
> initial=monitor1.default_limit)
> memory_limit = forms.CharField(max_length=100,
> initial=monitor2.default_limit)
> ...
>
> class Meta:
> model = model
>
> def __init__(self, *args, **kwargs):
> super(VirtualUserLimitForm, self).__init__(*args, **kwargs)
> if 'instance' in kwargs:
> self.fields['swap_limit'].initial =
> kwargs['instance'].monitoring.filter(monitor=monitor1)[0].current
> self.fields['memory_limit'].initial =
> kwargs['instance'].monitoring.filter(monitor=monitor2)[0].current
> ...
>
> I can generate all the needed code as string and then use exec(), but
> it seems ugly to me. I'm wondering if there is another way more
> elegant to do that? metaclasses maybe?


The metaclass does indeed take a dictionary argument where you can provide
class attributes, e. g.:

>>> C = type("C", (), dict(a=1, b=lambda self: 42))
>>> C().a, C().b()

(1, 42)

> What is your recommendation?


Find something that is simple and robust. Something else.

Peter
 
Reply With Quote
 
 
 
 
Carl Banks
Guest
Posts: n/a
 
      02-04-2011
On Feb 4, 1:32*pm, Peter Otten <__pete...@web.de> wrote:
> Marc Aymerich wrote:
> > I need to create a pretty complex class at runtime. something like
> > this one:

>
> I have a hunch that you've never heard the famous Kernighan quote:
>
> "Everyone knows that debugging is twice as hard as writing a program in the
> first place. So if you're as clever as you can be when you write it, how
> will you ever debug it?"
>
> Or that if you've heard it you don't heed it.


[snip]

> > What is your recommendation?

>
> Find something that is simple and robust. Something else.



I've done things like this before, and I would say the Kernigan quote
is not appropriate here, and the implication that isn't not simple and
robust is incorrect. It's repetitive code like the OP posted that's
complex and flimsy. Repetitive code is the exact opposite of the DRY
principle, and there's a reason DRY is one of the most often cited
principles here.

Using advanced techniques like metaclasses to exploit the similarity
to reduce or eliminate the repetitiveness can, if done well, simplify
code. By a lot.

Advanced is not the same thing as complex.


In this particular case I'd say the recommendation to do something
else is a good one, but it's not true in general.


Carl Banks
 
Reply With Quote
 
Carl Banks
Guest
Posts: n/a
 
      02-04-2011
On Feb 4, 11:17*am, Marc Aymerich <glicer...@gmail.com> wrote:
> Hi!
> I need to create a pretty complex class at runtime. something like
> this one:
>
> (note: "...." means that the number of attributes can be variable)
>
> class VirtualUserLimitForm(ModelForm):
> * * swap_limit = forms.CharField(max_length=100,
> initial=monitor1.default_limit)
> * * memory_limit = forms.CharField(max_length=100,
> initial=monitor2.default_limit)
> * * ...
>
> * * class Meta:
> * * * * model = model
>
> * * def __init__(self, *args, **kwargs):
> * * * * super(VirtualUserLimitForm, self).__init__(*args, **kwargs)
> * * * * if 'instance' in kwargs:
> * * * * * * self.fields['swap_limit'].initial =
> kwargs['instance'].monitoring.filter(monitor=monitor1)[0].current
> * * * * * * self.fields['memory_limit'].initial =
> kwargs['instance'].monitoring.filter(monitor=monitor2)[0].current
> * * * * * * ...
>
> I can generate all the needed code as string and then use exec(), but
> it seems ugly to me. I'm wondering if there is another way more
> elegant to do that? *metaclasses maybe? What is your recommendation?


I'd recommend against using metaclasses (in the normal way) with
Django ORM, since it (and pretty much all ORMs) already makes heavy
use of metaclasses, and I doubt those metaclasses were designed to
share metaclassing duties.

At a minimum you'd have to derive your own metaclasses from Djangos,
and that's not likely to work out well at all.

The right way to do this might be to reorganize your database. It
seems like you have all kinds of tables with different sets of
columns, and the tables themselves are dynamic? Like if you get some
data somewhere and it doesn't fit into an existing schema you make a
new table for it? If that's the case then you'd be better off,
perhaps, to reorganize the tables into some kind of association list.
Put the "relational" aspect to use. (Left as an exercise for now but
if you want pointers feel free to follow up.)

But maybe you have no control of the tables; you simply have a lot of
them and want to cut down on repetition. Then I think a factory
function that builds a dict and passes it to type() would be
reasonable. See the documentation of type() for explanation of
arguments.

def create_user_form(name,fields,_model):
class Meta:
model = _model
dct = { 'Meta': Meta }
for field in fields:
dct[field] = forms.CharField(max_length=100,
> initial=monitor1.default_limit)

return type(name,(ModelForm,),dct)


Carl Banks
 
Reply With Quote
 
Ian
Guest
Posts: n/a
 
      02-04-2011
On Feb 4, 2:32*pm, Peter Otten <__pete...@web.de> wrote:
> > (note: "...." means that the number of attributes can be variable)

>
> > class VirtualUserLimitForm(ModelForm):
> > * * swap_limit = forms.CharField(max_length=100,
> > initial=monitor1.default_limit)
> > * * memory_limit = forms.CharField(max_length=100,
> > initial=monitor2.default_limit)
> > * * ...

>
> > * * class Meta:
> > * * * * model = model

>
> > * * def __init__(self, *args, **kwargs):
> > * * * * super(VirtualUserLimitForm, self).__init__(*args, **kwargs)
> > * * * * if 'instance' in kwargs:
> > * * * * * * self.fields['swap_limit'].initial =
> > kwargs['instance'].monitoring.filter(monitor=monitor1)[0].current
> > * * * * * * self.fields['memory_limit'].initial =
> > kwargs['instance'].monitoring.filter(monitor=monitor2)[0].current
> > * * * * * * ...

>
> > I can generate all the needed code as string and then use exec(), but
> > it seems ugly to me. I'm wondering if there is another way more
> > elegant to do that? *metaclasses maybe?

>
> The metaclass does indeed take a dictionary argument where you can provide
> class attributes, e. g.:
>
> >>> C = type("C", (), dict(a=1, b=lambda self: 42))
> >>> C().a, C().b()

>
> (1, 42)


This approach will basically work, but since you're subclassing the
Django ModelForm, note that it already has its own metaclass:

>>> django.forms.ModelForm.__class__

<class 'django.forms.models.ModelFormMetaclass'>

So you will need to either subclass ModelFormMetaclass for your
metaclass and override the __init__ method, or modify what Peter
posted like so:

>>> C = django.forms.ModelForm.__class__("VirtualUserLimit Form", (django.forms.ModelForm,), dict(a=1, b=lambda self: 42))


Cheers,
Ian
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      02-05-2011
On Fri, 04 Feb 2011 11:17:39 -0800, Marc Aymerich wrote:

> Hi!
> I need to create a pretty complex class at runtime. something like this
> one:
>
> (note: "...." means that the number of attributes can be variable)
>
> class VirtualUserLimitForm(ModelForm):
> swap_limit = forms.CharField(max_length=100,
> initial=monitor1.default_limit)
> memory_limit = forms.CharField(max_length=100,
> initial=monitor2.default_limit)
> ...


[...]
> I can generate all the needed code as string and then use exec(), but it
> seems ugly to me. I'm wondering if there is another way more elegant to
> do that? metaclasses maybe? What is your recommendation?


Just add in the class attributes you want after creating the class.

class VirtualUserLimitForm(ModelForm):
pass


f = forms.CharField # alias for brevity
for name, value in [
('swap_limit', f(max_length=100, initial=monitor1.default_limit)),
('memory_limit', f(max_length=100, initial=monitor2.default_limit)),
('spam', 'spam spam spam spam'),
('eggs', 'fried sunny side up'),
]:
setattr(VirtualUserLimitForm, name, value)


There's probably a name for this sort of technique, and an entire chapter
in "Design Patterns For People With Too Much Time On Their Hands" (15th
edition), but I don't remember it



--
Steven
 
Reply With Quote
 
Ian
Guest
Posts: n/a
 
      02-05-2011
On Feb 4, 5:49*pm, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> Just add in the class attributes you want after creating the class.
>
> class VirtualUserLimitForm(ModelForm):
> * * pass
>
> f = forms.CharField *# alias for brevity
> for name, value in [
> * * ('swap_limit', f(max_length=100, initial=monitor1.default_limit)),
> * * ('memory_limit', f(max_length=100, initial=monitor2.default_limit)),
> * * ('spam', 'spam spam spam spam'),
> * * ('eggs', 'fried sunny side up'),
> * * ]:
> * * setattr(VirtualUserLimitForm, name, value)


This would probably cause some problems with the Django metaclass. It
inspects the class dictionary for fields at the time the class in
constructed, and so it would miss any fields added after construction
time.
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      02-05-2011
On Fri, 04 Feb 2011 17:19:23 -0800, Ian wrote:

> On Feb 4, 5:49Â*pm, Steven D'Aprano <steve
> +comp.lang.pyt...@pearwood.info> wrote:
>> Just add in the class attributes you want after creating the class.

[...]
> This would probably cause some problems with the Django metaclass. It
> inspects the class dictionary for fields at the time the class in
> constructed, and so it would miss any fields added after construction
> time.



What Django metaclass? The OP didn't say anything about using Django.



--
Steven
 
Reply With Quote
 
Chris Rebert
Guest
Posts: n/a
 
      02-05-2011
On Fri, Feb 4, 2011 at 6:10 PM, Steven D'Aprano
<steve+> wrote:
> On Fri, 04 Feb 2011 17:19:23 -0800, Ian wrote:
>> On Feb 4, 5:49Â*pm, Steven D'Aprano <steve
>> +comp.lang.pyt...@pearwood.info> wrote:
>>> Just add in the class attributes you want after creating the class.

> [...]
>> This would probably cause some problems with the Django metaclass. Â*It
>> inspects the class dictionary for fields at the time the class in
>> constructed, and so it would miss any fields added after construction
>> time.

>
> What Django metaclass? The OP didn't say anything about using Django.


His code mentions "forms.CharField" and declares a nested class named
"Meta"; this is indicative of Django.

Cheers,
Chris
--
http://blog.rebertia.com
 
Reply With Quote
 
Marc Aymerich
Guest
Posts: n/a
 
      02-05-2011
Thank you all for the answers!

I'll try to give you the context in which I need to generate classes
like this.

I'm developing a reusable control panel for an ISP. I have several
modules (VirtualUser, SystemUser, VPS, VirtualHost, ...) and they all
share the fact that you can have limits on resource consumption. for
example.

VPS: has limits on swap, memory, cpu and disk.
SystemUser: has limits on disk and traffic.

As all modules share this property of being limited I have decided to
split the limit functionality in a separate module (called resources).
Resources are associated dinamically with (vps, systemuser ...)
through a Monitor class. So it's up to you decide what limits you are
interested to put in the VPS or in the SystemUser ...

So when you attach the "disk limit" to SystemUser model is necessary
to create a web form for SystemUser a bit different than if you decide
to have disk and traffic limits. That is what my posted code is
supposed to do.

Do you think that I am on the wrong way designing the control panel
like this?

Btw, I've seen several interesting ideas to create the class
dinamically (with class factorys, MetaClass, dictionaries ..), but I
have not yet clear whether this will be usefull for create the
__init__ function at runtime too. Any clue on this?

Many, many thanks!!
Marc

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Classes within classes David ASP .Net 2 07-22-2005 07:13 PM
What is the difference between nested classes and inner classes ? Razvan Java 5 07-27-2004 07:59 PM
Modifiers applied to attributes, local variables, member functions, classes and inncer classes ! Razvan Java 11 07-17-2004 08:57 PM
Can I using reflection to get all child classes or classes undera package dynamically? Carfield Yim Java 1 05-31-2004 05:33 PM
How to access inner classes variables & methods from outer classes lonelyplanet999 Java 1 11-13-2003 01:54 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57