I've been thinking a bit about macros and what use they might be in Python. Basically, I was contemplating writing an import hook that would allow you to use code quoting and unquoting and stuff for your Python modules. My motive was just that Lisp people seem to rave about how awesome macros are all the time, so I figured they must be cool.
As I sat down to actually start figuring out what macro definitions and uses should look like in Python, I thought, hey, I'll just throw together a use case. But I haven't been able to come up with one (yet).
Most of the examples I found on the web focused on "hey, you can implement a 'while' loop with macros in Lisp!" or "hey, look at all the cool stuff the 'setf' macro can do!" So I started to wonder whether maybe Lisp people love macros because it allows them to extend Lisp's minimalist syntax with new constructs (like object-oriented programming with CLOS, while loops, etc.) Python, OTOH, has pretty rich syntax. It has a nice OOP system with syntactic support, while and for loops, generators, iterators, context managers, primitive coroutines, comprehensions, destructuring bind,.... -- What would I use macros for? (OK, depending on the syntax, I could add a "switch" statement, but that hardly seems worth the trouble.)
I should mention that I also saw some examples of people using macros for performance; you basically get rid of a function call and you can potentially make the inner loop of some critical function run really fast. But if that's all it buys me in Python-land (well, that and a switch statement), my motivation is pretty low. Because let's face it -- if your critical inner loop is written in pure Python, you can pretty easily throw it at Cython and get better performance than Python macros could ever provide.
So here's the question: does anyone out there have an idea of what macros would add to Python's power or expressiveness? Or maybe some Lisp, Meta OCAML, or Template Haskell hackers who can enlighten me as to what macros can add to a language with already rich syntax?Update 2008-03-19
I have implemented MetaPython 0.1, a macro and code quoting system for Python, covered in the next blog post.
CARVIEW |
Select Language
HTTP/2 302
server: nginx
date: Wed, 16 Jul 2025 22:15:55 GMT
content-type: text/plain; charset=utf-8
content-length: 0
x-archive-redirect-reason: found capture at 20090323084008
location: https://web.archive.org/web/20090323084008/https://blog.pythonisito.com/2009/03/python-macros.html
server-timing: captures_list;dur=0.680512, exclusion.robots;dur=0.023931, exclusion.robots.policy;dur=0.010462, esindex;dur=0.013299, cdx.remote;dur=58.426334, LoadShardBlock;dur=427.182797, PetaboxLoader3.datanode;dur=120.424200, PetaboxLoader3.resolve;dur=107.275650
x-app-server: wwwb-app224
x-ts: 302
x-tr: 524
server-timing: TR;dur=0,Tw;dur=0,Tc;dur=0
set-cookie: SERVER=wwwb-app224; path=/
x-location: All
x-rl: 0
x-na: 0
x-page-cache: MISS
server-timing: MISS
x-nid: DigitalOcean
referrer-policy: no-referrer-when-downgrade
permissions-policy: interest-cohort=()
HTTP/2 200
server: nginx
date: Wed, 16 Jul 2025 22:15:56 GMT
content-type: text/html; charset=UTF-8
x-archive-orig-expires: Mon, 23 Mar 2009 08:40:07 GMT
x-archive-orig-date: Mon, 23 Mar 2009 08:40:07 GMT
x-archive-orig-cache-control: public, max-age=0, must-revalidate, proxy-revalidate
x-archive-orig-last-modified: Sat, 21 Mar 2009 13:47:30 GMT
x-archive-orig-etag: "c659fec1-2da0-4594-84b9-1e66e71ad413"
x-archive-orig-vary: Accept-Encoding
x-archive-orig-x-content-type-options: nosniff
x-archive-orig-content-length: 80431
x-archive-orig-server: GFE/1.3
x-archive-orig-connection: Close
x-archive-guessed-content-type: text/html
x-archive-guessed-charset: utf-8
memento-datetime: Mon, 23 Mar 2009 08:40:08 GMT
link: ; rel="original", ; rel="timemap"; type="application/link-format", ; rel="timegate", ; rel="first memento"; datetime="Mon, 23 Mar 2009 08:40:08 GMT", ; rel="memento"; datetime="Mon, 23 Mar 2009 08:40:08 GMT", ; rel="next memento"; datetime="Sun, 05 Apr 2009 12:42:27 GMT", ; rel="last memento"; datetime="Fri, 25 Apr 2025 02:04:24 GMT"
content-security-policy: default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob: archive.org web.archive.org web-static.archive.org wayback-api.archive.org athena.archive.org analytics.archive.org pragma.archivelab.org wwwb-events.archive.org
x-archive-src: 52_8_20090323043433_crawl102-c/52_8_20090323083942_crawl100.arc.gz
server-timing: captures_list;dur=0.538645, exclusion.robots;dur=0.019563, exclusion.robots.policy;dur=0.008577, esindex;dur=0.010671, cdx.remote;dur=185.566938, LoadShardBlock;dur=323.506583, PetaboxLoader3.datanode;dur=208.244560, PetaboxLoader3.resolve;dur=311.750181, load_resource;dur=210.408863
x-app-server: wwwb-app224
x-ts: 200
x-tr: 837
server-timing: TR;dur=0,Tw;dur=0,Tc;dur=0
x-location: All
x-rl: 0
x-na: 0
x-page-cache: MISS
server-timing: MISS
x-nid: DigitalOcean
referrer-policy: no-referrer-when-downgrade
permissions-policy: interest-cohort=()
content-encoding: gzip
Just a little Python: Python Macros?
skip to main |
skip to sidebar
Blog about all things Python that intersect my work and hobbies
Thursday, March 12, 2009
Python Macros?
Posted by
Rick Copeland
at
10:17 AM
Labels: lisp, macros, programming, python
Subscribe to:
Post Comments (Atom)
Labels
- python (12)
- programming (10)
- sqlalchemy (7)
- lisp (2)
- macros (2)
- python programming (2)
- REST (1)
- ajax (1)
- cascade (1)
- cherrypy (1)
- compiler (1)
- datalog (1)
- decorator (1)
- descriptor (1)
- dynamic (1)
- javascript (1)
- linux (1)
- posgresql (1)
- turbogears (1)
- wxpython (1)
- wxwindows (1)

I'm learning PLT Scheme right now and while the IDE is quite cool, I don't see much point to macros either.
I think macros could create idiosyncratic constructs, which not pythonic.
Macros could allow you to not use explicit self in every class method...
@anonymous: Not that I think it's a good idea, but you can actually do that without macros using metaclasses.
A coworker and I were just discussing this the other day...
In C++ development at my previous employment, we used macros all the time, to reduce repeated code patterns. The most common example was in exception handling, for example, catching, logging, and then re-throwing an exception.
As I was writing "except Exception, e: ..." for yet another time the other day, I realized that with macros I could have avoided this repetition. Avoiding repetition means I only have to write it once, and thus, less potential for bugs!
@chphilli: I kind of see what you're saying, but couldn't you achieve the same thing with either a decorator (to guard a function) or a context manager (to guard an arbitrary block of code)?
with logging_exceptions:
some
potentially
exception
raising
stuff
Yeah, I suppose you could -- I like the idea of a context manager approach -- in fact, I'll probably implement it that way sometime next week now that you point it out.
I think the original point probably still stands though -- any where you have a set of boiler-plate code, it may be nice to have macro support.
That said, as you've pointed out, it's nowhere near as important as in less flexible languages like C++!
I guess Lisp programmers when creating templating or configuration languages want to keep programming in Lisp plus a few additions, whereas Python programmers either need something completely different so that a few macros would not help them much anyway or if they want Python they want to stay as close as possible to its syntax.
Macros allow your code-definition code to look more like regular code.
The type() example from last night's PyATL meetup is a good example:
class Test{BROWSER}(BrowserTestCase):
browser = {BROWSER}
looks a lot more like a normal Python class definition than:
type("Test%s" %(CLASSNAME, BROWSER), (BrowserTestCase,), dict(browser=BROWSER))
@Zellyn: I see what you're saying, and I agree that the type(...) approach can be off-putting, but I can also just write the following:
class Test(BrowserTestCase):
browser = browser
Test.__name__ = 'Test%s' % browser
In Python 2.6, I could even write a class decorator to clean it up:
@setname('Test%s' % browser)
class Test(BrowserTestCase):
browser = browser
where I have
def setname(name):
def inner(o):
o.__name__ = name
return o
return inner
Another thing I didn't like about the type(...) approach is that it doesn't account for metaclasses, but that's a minor pet peeve.
Actually, since you mentioned this, I can now think of a good use case for macros -- it would clean up the collections.namedtuple class generator quite a bit. Right now, it puts together a string and eval()s it. Kind of a poor man's macro anyway.
Rick, it was good seeing you last night. I think Python and Lisp are pretty close in terms of expressiveness. They have different philosophies though, and it attributes to why/how they are different. I much prefer Python's syntax to Lisp - code is generally much more pleasant to read, which is a big win. On the other hand, I like the simplicity of Lisp, in that there's just not that much to it. Macro's are so powerful you can do almost anything with it. On the other hand in Python, you have a large set of tools at your disposal(metaclasses, decorators, generators, the *with* clause, etc.). There's more you need to learn and more to chose from. Also, since they are language features, you can't extend them yourself - we have to design them by committee. Python wasn't always this expressive - It took many years to get to this point. Lispers were already enjoying their Macros almost at the beginning. Back then, there was nothing close to it, so it's no wonder they are so enthusiastic about them. Things are a bit different now - other languages have caught up.
Macros are great for conditionally ignoring a section of code. The one particular use I have in mind is for debug logging. Our massive code project is littered with function calls to do debugging, protected by `if trace_level` conditionals to prevent evaluation of the function call and its (sometimes obscenely large) argument payload. Two of the most expensive things one can do in Python are create objects and call functions, and logging generally does both.
@Scott: I suppose I can see using macros to differentiate between debug and optimized code, although it looks like you have a passable workaround with simple Python conditionals. It seems that a macro facility would probably make your "massive code project" code look cleaner (i.e. without the conditionals).
Between that example and collections.namedtuple, I'm just about convinced that macros could add something to Python. After reading the comments here, however, I am also convinced that Python needs them a lot less than a minimalist language like Lisp.