![]() |
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
Newbie
Join Date: Jul 2006
Posts: 8
Rep Power: 0
![]() |
Metaprogramming
Can someone please explain metaclasses & metaprogramming? I read a few definitions but I dont understand why they are necessary.... thanks
|
|
|
|
|
|
#2 |
|
Programming Guru
![]() Join Date: Aug 2005
Location: England
Posts: 1,499
Rep Power: 5
![]() |
Metaprogramming is the writing of programs that are designed to write/rewrite other programs (or often themselves). Python is relatively well equipped in this regard.
Metaclasses are to classes what classes are to objects. Or, to put it another way, if you instantiate a class, you get an object, whilst if you instantiate a metaclass, you get a class. For instance, a string in Python is instantiated from the str object. The class of a string is str: >>> print "Some string".__class__ <type 'str'> >>> "Some string".__class__.__class__ <type 'type'> Confusingly, type has two functions in Python. If type is called with one argument, it merely returns the type of an object. But if type is called with three arguments, it constructs a new class: BlankClass = type("BlankClass", (), {})As well as using the generic type metaclass to construct a new class on the fly, one can dynamically alter existing classes by specifying a metaclass with the __metaclass__ attribute. Before I touch on that, however, I'll give you a quick runthrough of decorators, and explain, roughly, what metaprogramming is. Take this simple function: def add(x, y): return x + y def add(x, y): print "Entering add" output = x + y print "Leaving add" return output This is a decorator function: def log(func): def logged(*args, **kwargs): print "Entering " + func.__name__ output = func(*args, **kwargs) print "Leaving " + func.__name__ return output return logged The classic way to use the decorator would be: def add(x, y): return x + y add = log(add) @log def add(x, y): return x + y Moving back onto metaclasses. Let's say we had a class that looked something like: class Foobar(object): @log def foo(self): print "foo" @log def bar(self): print "bar" from types import FunctionType class Log(type): def __new__(cls, name, bases, members): functions = [v for v in members.values() if type(v) is FunctionType] for function in functions: members[function.__name__] = log(function) return type.__new__(cls, name, bases, members) class Test(object): __metaclass__ = Log def foo(self): print "foo" def bar(self): print "bar" Phew! Sorry for going on for so long, but this is one of my favourite programming subjects ![]() Last edited by Arevos; Jul 5th, 2006 at 8:37 PM. |
|
|
|
|
|
#3 |
|
Newbie
Join Date: Jul 2006
Posts: 8
Rep Power: 0
![]() |
wow thanks alot! I have a way better grasp on it now.... One thing though, is it absolutley necessary for the logged function to be nested within log? Can't whats the point of having it nested?
|
|
|
|
|
|
#4 | |
|
Programming Guru
![]() Join Date: Aug 2005
Location: England
Posts: 1,499
Rep Power: 5
![]() |
Quote:
def log(func): def logged(*args, **kwargs): # note how func comes from the outer function print "Entering " + func.__name__ output = func(*args, **kwargs) print "Leaving " + func.__name__ return output return logged I can probably illustrate this better with a simpler example: def add(x): def inc(y): return x + y return inc For instance: >>> increment_by_one = add(1) >>> increment_by_one(2) 3 >>> increment_by_three = add(3) >>> increment_by_three(2) 5 >>> add(1)(2) 3 >>> add(3)(2) 5 The log function is similar, but instead of taking a number (x) from the outer function, it takes a function (func). |
|
|
|
|
|
|
#5 |
|
Programming Guru
![]() |
Wow. That was definitely a good explanation Arevos. I had tried reading from a tutorial somewhere on the internet earlier, and it didn't explain well or help me very much. This cleared up almost everything for me. You should consider posting this in the tutorial section, or add it to one of the python tutorial threads.
![]() |
|
|
|
|
|
#6 |
|
Programming Guru
![]() Join Date: Aug 2005
Location: England
Posts: 1,499
Rep Power: 5
![]() |
Thanks for the encouragement, Sane
![]() Another nice trick that I found whilst browsing the SQLObject codebase, is that __metaclass__ can be inherited. This can make adding metaclasses to a class somewhat neater: class LogMixin(object): __metaclass__ = Log class Test(LogMixin): def foo(self): print "foo" def bar(self): print "bar" __metaclass__ = Log class Test: def foo(self): print "foo" def bar(self): print "bar" |
|
|
|
|
|
#7 |
|
Programming Guru
![]() |
Ahh, I like that inheritance method. It really helps bundle your code in to logical groups. And it will give you quick control over which functions contain the metaclass, and which do not.
The second method seems like poor form however. Because, if you were writing a program, and you knew that you would need 50 classes that use the metaclass, it sounds like a good idea right? But what if at the end you need an unrelated class to not have a metaclass? You might have screwed yourself over. Perhaps you could add "__metaclass__ = None", I'm not sure. Regardless of that, it doesn't sound like the best way to go. |
|
|
|
|
|
#8 |
|
Professional Programmer
Join Date: Apr 2005
Location: London, England
Posts: 459
Rep Power: 4
![]() |
If you have 50 classes that need the metaclass I'd assume they'd all be related enough to warrant their own module. This other stray class would not belong in that module. Problem solved
|
|
|
|
![]() |
| Bookmarks |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | |
| Display Modes | |
|
|