<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Curia Blog</title><link>https://blog.curiasolutions.com/</link><description></description><atom:link href="https://blog.curiasolutions.com/feeds/all.rss.xml" rel="self"></atom:link><lastBuildDate>Sat, 19 Apr 2014 18:07:00 -0700</lastBuildDate><item><title>Announcing the pyramid_assetmutator package</title><link>https://blog.curiasolutions.com/announcing-the-pyramid_assetmutator-package.html</link><description>&lt;p&gt;If you're like me, one of the first things you do when starting a new
web application project is set up a CSS alternative such as LESS or
SASS/SCSS. Those who aren't particularly fond of JavaScript might also
tend to add CoffeeScript. These &amp;quot;metalanguages&amp;quot; can assist in making
client-side/asset code more pleasurable to work with, as they typically
provide functionality that's &amp;quot;missing&amp;quot; in the the languages they get
interpreted into.&lt;/p&gt;
&lt;p&gt;Some frameworks (e.g. Ruby on Rails) give you support for these
alternatives out-of-the-box, but in the land of less opinionated
software things can require a bit more work. Since I am personally a fan
of the
&lt;a class="reference external" href="http://docs.pylonsproject.org/en/latest/docs/pyramid.html"&gt;Pyramid&lt;/a&gt;
web framework, I've used the
&lt;a class="reference external" href="https://github.com/sontek/pyramid_webassets"&gt;pyramid_webassets&lt;/a&gt;
and&amp;nbsp;&lt;a class="reference external" href="https://github.com/FormAlchemy/pyramid_fanstatic"&gt;pyramid_fanstatic&lt;/a&gt;
packages in the past to provide this support. However, these packages
are so powerful and comprehensive that they can also tend to be somewhat
involved in regards to configuration, so I&amp;nbsp;recently&amp;nbsp;decided to roll my
own Pyramid
add-on:&amp;nbsp;&lt;a class="reference external" href="http://pyramid_assetmutator.readthedocs.org/"&gt;pyramid_assetmutator&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Although definitely not as &amp;quot;feature-full&amp;quot; as the packages I have
mentioned earlier, as of this writing&amp;nbsp;it&amp;nbsp;provides the following:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Support for piping (a.k.a &amp;quot;mutating&amp;quot;) assets through pretty much any
command you like (its core functionality is quite rudimentary).&lt;/li&gt;
&lt;li&gt;The ability to specify whether to have your assets &amp;quot;mutated&amp;quot; during
each request, or on each &amp;quot;application boot&amp;quot;&amp;nbsp;(typically best for prod
setups).&lt;/li&gt;
&lt;li&gt;Partial support for template language parsing in your asset files
(e.g. Mako or Jinja2 template tags in your JavaScript or CSS files).&lt;/li&gt;
&lt;li&gt;A unified Python 2/3 codebase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Feel free to &lt;a class="reference external" href="https://github.com/seedifferently/pyramid_assetmutator"&gt;check it out on
GitHub&lt;/a&gt; or
&lt;a class="reference external" href="http://pyramid_assetmutator.readthedocs.org/"&gt;browse the
documentation&lt;/a&gt;, and
share here or open a GitHub issue if you have any comments or
suggestions.&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Seth Davis</dc:creator><pubDate>Sat, 19 Apr 2014 18:07:00 -0700</pubDate><guid>tag:blog.curiasolutions.com,2014-04-19:announcing-the-pyramid_assetmutator-package.html</guid><category>Pyramid</category><category>Python</category><category>web-development</category><category>FOSS</category></item><item><title>An rsync-like utility for Amazon S3 and Google Storage</title><link>https://blog.curiasolutions.com/an-rsync-like-utility-for-amazon-s3-and-google-storage.html</link><description>&lt;p&gt;Every so often I find myself working on that odd job that requires
syncing files with Amazon's S3. In the beginning, I tried some of the
various S3 FUSE interfaces—hoping for something that would play nice
with rsync—but FUSE's stability always left something to be desired and
more often than not I'd be left with that one transfer that never&amp;nbsp;would
quite finish correctly.&lt;/p&gt;
&lt;p&gt;Eventually I discovered &lt;a class="reference external" href="http://github.com/boto/boto"&gt;boto&lt;/a&gt;&amp;nbsp;and
settled in to using a hacked together (but stable) Python/boto solution
for these type of tasks—all the while wondering why nobody took the time
to write a &amp;quot;real&amp;quot; rsync-like client for S3.&lt;/p&gt;
&lt;p&gt;Well, this last time around I&amp;nbsp;finally decided to stop whining and take
matters into my own hands. After a couple of late nights fleshing out my
original boto solution, I'm happy to announce what I'm calling &amp;quot;boto
rsync&amp;quot;—an rsync like wrapper for boto's cloud storage interfaces (both
S3 &lt;em&gt;and&lt;/em&gt;&amp;nbsp;Google Storage).&lt;/p&gt;
&lt;p&gt;Please take a look at the project on github and let me know what you
think:&amp;nbsp;&lt;a class="reference external" href="http://github.com/seedifferently/boto_rsync"&gt;http://github.com/seedifferently/boto_rsync&lt;/a&gt;&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Seth Davis</dc:creator><pubDate>Sun, 11 Dec 2011 17:55:00 -0800</pubDate><guid>tag:blog.curiasolutions.com,2011-12-11:an-rsync-like-utility-for-amazon-s3-and-google-storage.html</guid><category>AWS</category><category>Boto</category><category>programming</category><category>Python</category><category>S3</category><category>FOSS</category><category>cloud</category></item><item><title>The long overdue "Shootout" update</title><link>https://blog.curiasolutions.com/the-long-overdue-shootout-update.html</link><description>&lt;p&gt;It's been several months since I've had a chance to update &lt;a class="reference external" href="https://blog.curiasolutions.com/pages/the-great-web-framework-shootout.html"&gt;The Great Web
Framework Shootout&lt;/a&gt;,
but this weekend I decided that it was time to dig in and freshen things up a
bit.&lt;/p&gt;
&lt;p&gt;Not only have most of the frameworks seen new releases since the last
revision, but I finally decided to move all of the tests over to
Amazon's &amp;quot;release&amp;quot; version of the Ubuntu LTS AMI.&lt;/p&gt;
&lt;p&gt;Below is a quick summary of what's new in this revision:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;All tests were performed on the updated Ubuntu LTS AMI (ami-fbbf7892
ubuntu-images-us/ubuntu-lucid-10.04-amd64-server-20110719.manifest.xml)&lt;/li&gt;
&lt;li&gt;The updated AMI was configured with Python 2.6.5, PHP 5.3.2,
Ruby&amp;nbsp;1.9.2p290, Apache 2.2.14 (default config),&amp;nbsp;mod_wsgi 2.8
(embedded mode), and mod_passenger 3.0.9&lt;/li&gt;
&lt;li&gt;Rails 2.x and 3.0 were dropped from the &amp;quot;full stack(ish)&amp;quot; tests in
favor of Rails 3.1.&lt;/li&gt;
&lt;li&gt;CakePHP 1.2 was dropped from the PHP tests in favor of 1.3,&amp;nbsp;but
Symfony and Yii were added as they seem to have considerable market
share.&lt;/li&gt;
&lt;li&gt;CakePHP's caching engine was incorrectly configured during the last
round of tests, and this has been corrected.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a class="reference external" href="http://profiles.google.com/seedifferently"&gt;Circle me on Google+&lt;/a&gt; to
keep track of further updates, and feel free to contact me there with
any questions or comments.&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Seth Davis</dc:creator><pubDate>Mon, 12 Sep 2011 12:08:00 -0700</pubDate><guid>tag:blog.curiasolutions.com,2011-09-12:the-long-overdue-shootout-update.html</guid><category>Django</category><category>performance</category><category>PHP</category><category>Pylons</category><category>Pyramid</category><category>Python</category><category>Rails</category><category>TurboGears</category><category>web-development</category></item><item><title>The great web technology shootout – Round 4: Pyramid vs Django vs TG vs Rails 2 &amp; 3</title><link>https://blog.curiasolutions.com/the-great-web-technology-shootout---round-4-pyramid-vs-django-vs-tg-vs-rails-2-3.html</link><description>&lt;p class="alert alert-warning"&gt;Due to the popularity of these posts, I have decided to move
all of the benchmarking information over to its own dedicated page.
Please see the new &lt;a class="reference external" href="https://blog.curiasolutions.com/pages/the-great-web-framework-shootout.html"&gt;framework shootout page&lt;/a&gt; for the latest
information.&lt;/p&gt;
&lt;p class="alert alert-info"&gt;This post is the continuation of a series. Please read &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;Round 1&lt;/a&gt;, &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-technology-shootout-round-2-php-deserves-a-helping-hand.html"&gt;Round 2&lt;/a&gt;, and &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-technology-shootout-round-3-better-faster-and-shinier.html"&gt;Round 3&lt;/a&gt; first if you
are just now joining.&lt;/p&gt;
&lt;p&gt;While I had originally intended for round 4 to showcase how
microframeworks are changing the way we do &amp;quot;quick and dirty&amp;quot; web
development (and how they make using PHP as &amp;quot;an extension to HTML&amp;quot; old
hat), my current programming habits have kept me involved in the more
&amp;quot;full-stack&amp;quot; framework solutions.&amp;nbsp;So, rather than spitting out various
benchmarks of frameworks that I have little or no interaction with, and
since enough time has passed since the last &amp;quot;shootout&amp;quot; that the
landscape has changed a bit (with the introduction of
&lt;a class="reference external" href="http://docs.pylonshq.com/"&gt;Pyramid&lt;/a&gt; and the release of Rails 3), I
have instead decided to showcase the most recent data on the frameworks
that I personally find myself in contact with on a regular basis.&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="warning-everything-is-different-this-time-around"&gt;
&lt;h2&gt;Warning: Everything is different this time around.&lt;/h2&gt;
&lt;p&gt;These benchmarks were all run on a fresh Amazon EC2 instance in order to
(hopefully)&amp;nbsp;achieve a more isolated environment. Obviously, since these
benchmarks have all been run on a completely different box than any of
the previous rounds, no previous data should be compared with these
numbers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What you should know about Round 4:&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The EC2 instance used was: ami-da0cf8b3 m1.large
ubuntu-images-us/ubuntu-lucid-10.04-amd64-server-20101020.manifest.xml&lt;/li&gt;
&lt;li&gt;As a &amp;quot;Large&amp;quot; instance, Amazon describes the resources as: 7.5 GB of
memory, 4 EC2 Compute Units (2 virtual cores with 2 EC2 Compute Units
each), 850 GB of local instance storage, 64-bit platform.&lt;/li&gt;
&lt;li&gt;The various system software used was whatever was current as of
November 18, 2010 on Ubuntu 10.04's repositories.&lt;/li&gt;
&lt;li&gt;Apache 2.2.14 was used for all tests.&lt;/li&gt;
&lt;li&gt;Python 2.6.5 and mod_wsgi 3.3 (embedded mode) were used for the
Python tests.&lt;/li&gt;
&lt;li&gt;Ruby 1.8.7 (except for the last Rails 3 test) and Phusion Passenger
3.0 were used for the Rails tests.&lt;/li&gt;
&lt;li&gt;ApacheBench was run with -n 10000 and -c 10 about 5-10 times each,
and the &amp;quot;best guess average&amp;quot; was chosen (yeah, I'm lazy).&lt;/li&gt;
&lt;li&gt;The Pyramid/TG test apps used SQLAlchemy as the ORM and Jinja2 as the
templating system (Jinja2 was consistently around 25-50r/s faster
than Mako for me).&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Remember, nothing here is really all that scientific and your mileage
WILL vary. Now on to the results...&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="the-hello-world-string-test"&gt;
&lt;h2&gt;The &amp;quot;Hello World&amp;quot; string test&lt;/h2&gt;
&lt;p&gt;The &amp;quot;Hello World&amp;quot; string test simply spits out a string response.
There's no template or DB calls involved, so the level of processing
should be minimal.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2010/11/round_4_hello.png"&gt;&lt;img alt="The return hello world test chart" src="https://blog.curiasolutions.com/static/uploads/2010/11/round_4_hello.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-template-test"&gt;
&lt;h2&gt;The template test&lt;/h2&gt;
&lt;p&gt;The&amp;nbsp;template test simply spits out Lorem Ipsum via a template (thus
engaging the framework's templating systems).&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2010/11/round_4_hellos.png"&gt;&lt;img alt="The template test chart" src="https://blog.curiasolutions.com/static/uploads/2010/11/round_4_hellos.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-template-db-query-test"&gt;
&lt;h2&gt;The template + DB query test&lt;/h2&gt;
&lt;p&gt;The&amp;nbsp;template/db test simply loads 5 rows of Lorem Ipsum from a SQLite DB
and spits it out via a template (thus engaging both the framework's ORM
and templating system).&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2010/11/round_4_hellodb.png"&gt;&lt;img alt="Template test with database query" src="https://blog.curiasolutions.com/static/uploads/2010/11/round_4_hellodb.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="closing-thoughts"&gt;
&lt;h2&gt;Closing thoughts&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The Pyramid Alpha results excite me quite a bit with all the talk of
the various frameworks looking to merge with it. Could this really
become the &amp;quot;one Python framework to rule them all&amp;quot;?&lt;/li&gt;
&lt;li&gt;Django is still going strong, and continues to be an exceptional
choice if you're in to its way of doing things.&lt;/li&gt;
&lt;li&gt;The Rails results &lt;span class="strike"&gt;left me a little confused&lt;/span&gt; are
interesting because Rails 2 apparently outperforms Rails 3 on Ruby
1.8. Of course, once you move to Ruby 1.9, Rails 3 runs quite nicely.&lt;/li&gt;
&lt;li&gt;Don't believe these numbers? Feel free to boot up your own EC2
instance and run one of the test apps:&lt;ul&gt;
&lt;li&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2010/11/pyramid.zip"&gt;Pyramid 1.0a3 test app
code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2010/11/django.zip"&gt;Django 1.2 test app
code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2010/11/tg21.zip"&gt;TurboGears 2.1 test app
code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2010/11/rails3.zip"&gt;Rails 3 test app
code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2010/11/rails2.zip"&gt;Rails 2 test app
code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Seth Davis</dc:creator><pubDate>Thu, 18 Nov 2010 17:00:00 -0800</pubDate><guid>tag:blog.curiasolutions.com,2010-11-18:the-great-web-technology-shootout---round-4-pyramid-vs-django-vs-tg-vs-rails-2-3.html</guid><category>Django</category><category>performance</category><category>Pyramid</category><category>Python</category><category>Rails</category><category>TurboGears</category><category>web-development</category></item><item><title>Python as a PHP replacement?</title><link>https://blog.curiasolutions.com/python-as-a-php-replacement.html</link><description>&lt;p&gt;I recently sat down to coffee with a new acquaintance of mine who spends
much of his time implementing F/OSS projects at non-profit
organizations, and who had just stepped into a lead web developer
position using PHP. After sharing pleasantries we began trading stories
and talking about each of our &amp;quot;tools of the trade.&amp;quot; When I mentioned
that I used to do most of my web development in PHP, but have spent the
past year or so trying to move as completely as possible to Python, his
response was: &amp;quot;Huh, I have never really thought of Python as a PHP
replacement.&amp;quot;&lt;/p&gt;
&lt;p&gt;Now, this guy hasn't exactly been living under a rock for the past 10
years—his resume was quite impressive and included projects in a number
of different programming languages—but as you can imagine, I was rather
surprised by his response and it made me wonder: Has the Python
community really been that bad at promoting the strengths of Python for
web development? Or, does the nirvana experienced by switching from a
language like PHP to Python just make us so at peace with the world that
we forget the hordes of developers still stuck with C-style syntax?
Either way, it got me thinking about a few of the reasons why &lt;em&gt;I&lt;/em&gt;
decided to switch from PHP to Python; and why I not only see Python as
an excellent PHP replacement, but am surprised it is such a &amp;quot;best-kept
secret&amp;quot; for web development.&lt;/p&gt;
&lt;p&gt;There are already plenty of pages out there discussing &lt;a class="reference external" href="http://wiki.python.org/moin/PythonVsPhp"&gt;Python vs. PHP
as a language&lt;/a&gt;, so I
probably won't get too technical here. I also want to try avoid turning
this into a &amp;quot;Python is better than PHP because...&amp;quot; rant (for more on
that, please see the end of this post), so I will simply share with you
a few of the main reasons why &lt;em&gt;I&lt;/em&gt; decided to replace PHP with Python as
&lt;em&gt;my&lt;/em&gt; primary language for web development:&lt;/p&gt;
&lt;div class="section" id="python-is-more-flexible-than-php"&gt;
&lt;h2&gt;» Python is more flexible than PHP&lt;/h2&gt;
&lt;p&gt;Python was engineered from the beginning to be a general purpose
programming language. PHP on the other hand was written to overcome many
of the &amp;quot;limitations&amp;quot; of the
&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Cgi-bin"&gt;cgi-bin&lt;/a&gt;. This should say a
lot about the &amp;quot;soul&amp;quot; of each language, but the conclusion I have come to
is:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;PHP is meant to do web things really well&lt;/li&gt;
&lt;li&gt;Python is meant to do most things really well&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sure, these days you &lt;em&gt;could&lt;/em&gt; write a desktop GUI in PHP, but it would
most likely &lt;em&gt;not&lt;/em&gt; be the right tool for the job (and you'd probably get
laughed at). On a somewhat related note: many PHP developers I know find
themselves switching between PHP for their website tasks, and a variety
of other languages for other tasks (Shell Scripting for command-line
scripts, Perl for text processing, etc.). As a Python programmer, I can
typically stick to &lt;em&gt;one programming language&lt;/em&gt; no matter what the task at
hand requires (especially when armed with
&lt;a class="reference external" href="http://ipython.scipy.org/"&gt;iPython&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Now, the obvious argument for PHP at this point is: &amp;quot;But wait, since PHP
was written specifically for the web, shouldn't that actually give it
&lt;em&gt;more&lt;/em&gt; credibility as the right tool for the web job?&amp;quot; Well, 5 or 10
years ago I might have agreed with you, but web development has changed
a lot in the past few years (not to mention the introduction of Python's
&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface"&gt;WSGI
spec&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The internet as a platform has grown and mutated drastically and now
requires much more than &amp;quot;a server-side extension to HTML.&amp;quot; We are now in
an age of full-blown internet &lt;em&gt;applications&lt;/em&gt;, which require a true
&amp;quot;application-level&amp;quot; language. In my opinion, PHP has only recently begun
to move in that direction with the v5.3 release (as well as updates
expected in PHP 6). That alone should give you pause when pondering
PHP's ability to &amp;quot;keep up&amp;quot; with the growth of the web.&lt;/p&gt;
&lt;p&gt;In short, the internet continues to transform into an arena that is much
less of a website platform, and much more of an application platform. To
me, this makes a general purpose language such as Python the obvious
choice for &amp;quot;future-proof&amp;quot; internet development.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="python-is-typically-faster-than-php"&gt;
&lt;h2&gt;» Python is typically faster than PHP&lt;/h2&gt;
&lt;p&gt;You might find PHP beating out Python in a simple &amp;quot;hello world&amp;quot;-type
test, but &amp;quot;real-world&amp;quot; applications will typically run much faster in
Python. In a Web 2.0 world where everyone has broadband, speed has become
an important factor in keeping your visitors happy.&lt;/p&gt;
&lt;p&gt;In addition to &amp;quot;raw, under-the-hood&amp;quot; speed, Python's language philosophy
means it will typically &lt;a class="reference external" href="http://groups.google.com/group/comp.lang.python/msg/83907b65bdee30f6"&gt;scale better than
PHP&lt;/a&gt;
(although this can be argued a million different ways, since scalability
usually involves much more than the language).&lt;/p&gt;
&lt;p&gt;I've already done
&lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;benchmarks&lt;/a&gt; comparing a
few popular web frameworks, but if you're still curious I'd
suggest you &lt;a class="reference external" href="http://www.google.com/search?q=is+python+or+php+faster%3F"&gt;dig around with Google&lt;/a&gt; for a while.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="python-is-easier-to-read-and-maintain-than-php"&gt;
&lt;h2&gt;» Python is easier to read and maintain than PHP&lt;/h2&gt;
&lt;p&gt;Any veteran programmer will tell you that code &lt;em&gt;readability&lt;/em&gt; plays a
huge factor in code &lt;em&gt;maintainability&lt;/em&gt;. With that in mind, Python was
designed with some pretty strict syntax rules in regards to code-blocks
and whitespace. While this may initially frustrate programmers who are
used to a looser &amp;quot;bracket-encapsulated&amp;quot; language, it actually forces you
to write more readable (and therefore, more maintainable) code.&lt;/p&gt;
&lt;p&gt;Other than readability, Python's clean syntax also means:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;You can write more code with less errors (because they're so easy to
spot). This often comes as a pleasant surprise to programmers
migrating from C-style languages (take &lt;a class="reference external" href="http://www.python.org/about/success/esr/"&gt;Eric
Raymond&lt;/a&gt; for example).&lt;/li&gt;
&lt;li&gt;You end up typing less — No more semicolons or brackets, and fewer
parentheses.&lt;/li&gt;
&lt;li&gt;Your eyes don't have to move around as much to figure out what's
going on.&lt;/li&gt;
&lt;li&gt;Your first guess about what's happening in a code-block is usually
correct.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you have time to kill, I'd recommend a stroll through &lt;a class="reference external" href="http://rosettacode.org/wiki/Main_Page"&gt;Rosetta
Code&lt;/a&gt; to compare how different
languages &amp;quot;look&amp;quot; when tackling the same problem. Unless you just love
brackets, I think you'll find the non C-style syntaxes much easier to
read.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="python-is-more-consistent-than-php"&gt;
&lt;h2&gt;» Python is more consistent than PHP&lt;/h2&gt;
&lt;p&gt;Continuing somewhat in the vein of readability is the topic of
consistency. Since PHP's lack of consistency has already been studied
exhaustively by the Perl folks, I will spare you my rant and suggest you
&lt;a class="reference external" href="http://tnx.nl/php.html"&gt;read theirs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As a side note on this topic: I often hear PHP programmers talk about
how awesome PHP's documentation is in that you can open a web browser
and just type &amp;quot;php.net/some_function&amp;quot; to get help. Well, documentation
recall usually isn't that big of a deal for me (because I have a
somewhat photographic memory), but I will say that after switching to
Python I found myself turning to its documentation &lt;em&gt;significantly&lt;/em&gt; less
than I had with PHP, simply because I didn't have to keep reminding
myself whether that infrequently used function was named some_function
or somefunction. With Python, the function/method I need is usually
named &lt;em&gt;exactly&lt;/em&gt; what I expect it to be named, and returns &lt;em&gt;exactly&lt;/em&gt; what
I expect it to return.&lt;/p&gt;
&lt;p&gt;Oh, and don't even get me started on PHP's recent
&lt;a class="reference external" href="http://blog.fedecarg.com/2008/10/28/php-namespaces-controversy/"&gt;namespace&lt;/a&gt;
&lt;a class="reference external" href="http://michaelkimsal.com/blog/disappointed-with-php-namespace-seperator-decision/"&gt;decision&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="however-php-may-still-excel-in-a-few-areas"&gt;
&lt;h2&gt;However, PHP may still excel in a few areas&lt;/h2&gt;
&lt;p&gt;Having said all of that, there are a few areas where I believe PHP may
still shine brighter than Python:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="php-hosting-support-may-be-more-prevalent-than-python"&gt;
&lt;h2&gt;» PHP hosting support may be more prevalent than Python&lt;/h2&gt;
&lt;p&gt;It's practically unheard of to find yourself on a web host that doesn't
support PHP out of the box. However, this is becoming less and less of
an issue since most Python web applications can now be deployed using
tools such as FastCGI or mod_proxy (as opposed to the popular
&lt;a class="reference external" href="http://code.google.com/p/modwsgi/"&gt;mod_wsgi&lt;/a&gt; setup, which often
requires root access to configure). These days any &amp;quot;reputable&amp;quot; web host
is probably going to support some sort of strategy for deploying Python
web applications. Of course, this only really applies to shared hosting
environments without root access. If you've got &lt;a class="reference external" href="https://www.digitalocean.com/?refcode=861e7a0ad743"&gt;a VPS or dedicated
server&lt;/a&gt; you're fine.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="php-is-arguably-more-seasoned-on-the-web-than-python"&gt;
&lt;h2&gt;» PHP is arguably &amp;quot;more seasoned&amp;quot; on the web than Python&lt;/h2&gt;
&lt;p&gt;The communities behind projects like Drupal and WordPress really stand
out to me as examples of the maturity of PHP on the web. Although
similar Python web projects have been slowly emerging (and
&lt;a class="reference external" href="http://plone.org/"&gt;Plone&lt;/a&gt; has been around for years, if that's your
cup of tea), they don't really compete yet in my opinion.&lt;/p&gt;
&lt;p&gt;Of course, I am only talking about specific web projects here. Language
wise, Python has been around longer than PHP. Web 2.0 framework wise,
Python's offerings have been around about as long as PHP's (pretty much
every language had its own answer to Ruby on Rails in 2005).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="php-seems-easier-than-python-at-first-and-might-be-for-simple-deployments"&gt;
&lt;h2&gt;» PHP &lt;em&gt;seems&lt;/em&gt; easier than Python at first (and might be for simple deployments)&lt;/h2&gt;
&lt;p&gt;There's something magical about throwing together a few quick
index.php/about.php/contact.php files and uploading them to the httpdocs
directory of your webserver and it &amp;quot;just working.&amp;quot; A lot of simple
websites have no need for acronyms like MVC, ORM, and DRY, and in those
cases PHP is an excellent &amp;quot;server-side extension&amp;quot; to HTML. However, if
and when you begin to scale, you'll either find yourself switching to a
full-blown framework, or throwing one together to help facilitate your
growth. At that point, you'll probably begin discovering the &amp;quot;dark
corners&amp;quot; of PHP, and may find yourself wishing you had &amp;quot;done it right&amp;quot;
the first time.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="so-which-is-better"&gt;
&lt;h2&gt;So, which is better?&lt;/h2&gt;
&lt;p&gt;The most honest answer is probably: &amp;quot;It depends.&amp;quot; Python definitely has
some significant advantages over PHP in certain circumstances, but the
same could probably be said of PHP in other circumstances (though mostly
limited to the web I imagine).&lt;/p&gt;
&lt;p&gt;Remember, the point of this post is to solidify the fact that Python can
be an excellent PHP &lt;em&gt;replacement&lt;/em&gt;. When it comes to programming
languages, &amp;quot;better&amp;quot; is often more a case of personal preference than
anything else. However, to &lt;em&gt;me&lt;/em&gt; the points discussed above make Python
the better choice when it comes to what &lt;em&gt;I&lt;/em&gt; like in a web development
language.&lt;/p&gt;
&lt;p&gt;I hope this post has encouraged you PHP programmers out there to take
another look at Python. It may take some getting used to (Python's way
of doing things is inherently quite different than PHP's), but you will
probably find yourself thanking me later.&lt;/p&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Seth Davis</dc:creator><pubDate>Mon, 30 Nov 2009 11:11:00 -0800</pubDate><guid>tag:blog.curiasolutions.com,2009-11-30:python-as-a-php-replacement.html</guid><category>PHP</category><category>programming</category><category>Python</category><category>web-development</category></item><item><title>Reorganizing data with list &amp; dict comprehensions</title><link>https://blog.curiasolutions.com/reorganizing-data-with-list-dict-comprehensions.html</link><description>&lt;p&gt;While writing scripts, I frequently run into the issue of needing to
re-arrange sets of data into a more &amp;quot;process friendly&amp;quot; format. A common
issue I encounter is needing to turn a list (array) into a dictionary
(associative array) or vice versa. More often than not, I find myself
needing to be able to access list elements by a key, but since they
aren't setup in a dictionary I have to pull out a looping technique to
reorganize the data for this to be possible.&lt;/p&gt;
&lt;p&gt;Take the following set of data for example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;John Smith&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Jane Doe&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;superuser&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Sam Jones&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What we have here is a few rows of user data. In this example, the data
is in a Python list (which in PHP would be an array).&lt;/p&gt;
&lt;p&gt;In PHP, this would look something like (using print_r):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="x"&gt;Array (&lt;/span&gt;
&lt;span class="x"&gt;    [0] =&amp;gt; Array (&lt;/span&gt;
&lt;span class="x"&gt;        [0] =&amp;gt; 1&lt;/span&gt;
&lt;span class="x"&gt;        [1] =&amp;gt; John Smith&lt;/span&gt;
&lt;span class="x"&gt;        [2] =&amp;gt; admin&lt;/span&gt;
&lt;span class="x"&gt;    )&lt;/span&gt;
&lt;span class="x"&gt;    [1] =&amp;gt; Array (&lt;/span&gt;
&lt;span class="x"&gt;        [0] =&amp;gt; 2&lt;/span&gt;
&lt;span class="x"&gt;        [1] =&amp;gt; Jane Doe&lt;/span&gt;
&lt;span class="x"&gt;# (...etc...)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So, what if I found myself writing some code that needed to be able to
access each record by its first value, which in this case would be the
user_id?&lt;/p&gt;
&lt;p&gt;Well, in PHP the easiest way to make this happen would be a
good-old-fashioned &lt;em&gt;foreach&lt;/em&gt; loop:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="x"&gt;foreach ($row as $val) {&lt;/span&gt;
&lt;span class="x"&gt;    $out[$val[0]] = $val;&lt;/span&gt;
&lt;span class="x"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Not very &amp;quot;sexy&amp;quot; for something I find myself having to more often than I
would like, but it works nonetheless.&lt;/p&gt;
&lt;p&gt;Now, here's where the beauty of Python kicks in. Python has a built-in
feature called &amp;quot;comprehension&amp;quot; which allows you to modify and/or convert
sets of data quickly and easily.&lt;/p&gt;
&lt;p&gt;Remember what our original data set looks like? Here's a refresher:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;
&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;John Smith&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Jane Doe&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;superuser&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Sam Jones&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, in Python all we need to do is use the dictionary comprehension:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# Python 3.X&lt;/span&gt;
&lt;span class="c"&gt;# (or, for Python 2.X)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Python 2.4+&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;John Smith&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Jane Doe&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;superuser&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Sam Jones&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See how easy that was?&lt;/p&gt;
&lt;p&gt;And what if I had a dictionary in the above format, and needed to turn
it into the type of list I had previously? Well, I could simply use the
list comprehension to re-generate this set of data as a list:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;John Smith&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Jane Doe&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;superuser&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Sam Jones&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is an extremely simple comprehension example. For a deeper look,
check out Wikipedia's &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Python_syntax_and_semantics#Functional_programming"&gt;Python syntax
page&lt;/a&gt;
or the &lt;a class="reference external" href="http://docs.python.org/3.1/tutorial/datastructures.html"&gt;official Python
documentation&lt;/a&gt;.&lt;/p&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Seth Davis</dc:creator><pubDate>Fri, 30 Oct 2009 20:58:00 -0700</pubDate><guid>tag:blog.curiasolutions.com,2009-10-30:reorganizing-data-with-list-dict-comprehensions.html</guid><category>PHP</category><category>programming</category><category>Python</category></item><item><title>The great web technology shootout - Round 3: Better, Faster, and Shinier</title><link>https://blog.curiasolutions.com/the-great-web-technology-shootout-round-3-better-faster-and-shinier.html</link><description>&lt;p class="alert alert-warning"&gt;A lot of the information below is out of date. Please see the new
&lt;a class="reference external" href="https://blog.curiasolutions.com/pages/the-great-web-framework-shootout.html"&gt;framework shootout page&lt;/a&gt; for the latest
benchmarks.&lt;/p&gt;
&lt;p class="alert alert-info"&gt;This post is the continuation of a series. Please read &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;Round 1&lt;/a&gt; and &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-technology-shootout-round-2-php-deserves-a-helping-hand.html"&gt;Round 2&lt;/a&gt; first if you
are just now joining.&lt;/p&gt;
&lt;p&gt;As I mentioned briefly in &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;Round
1&lt;/a&gt;, this whole thing
came about as an experiment to satisfy my own curiosity. Unfortunately, I
wasn't expecting these posts to draw the amount of attention they have
been getting, and several people informed me of a few &amp;quot;issues&amp;quot; with the
first round. Since my initial approach to this topic was somewhat
casual, I didn't really take the time to perform each test in a &amp;quot;proper
scientific fashion.&amp;quot; Although this was clearly stated in the
introduction to round one, it unfortunately resulted in performance
estimations that were somewhat less than accurate.&lt;/p&gt;
&lt;p&gt;After input from various people much smarter than myself, I quickly went
to work tweaking my test environment and building &amp;quot;proper&amp;quot; test apps. In
the midst of this, a conversation about PHP accelerators prompted me to
put PHP under the spotlight, which brought about &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-technology-shootout-round-2-php-deserves-a-helping-hand.html"&gt;Round
2&lt;/a&gt; as an interim
round. This gave me a chance to demonstrate the necessity of PHP acceleration,
and only continued to solidify my opinion of PHP as an inferior web
development language (remember, I just said &lt;em&gt;my opinion&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Which brings us to Round 3. A lot of work has gone into &amp;quot;doing it right&amp;quot;
this time, so I am fairly confident that these results are a much more
accurate representation of each test subject's performance estimations.
Remember, benchmark test code typically has no real-world value, so
&amp;quot;performance estimations&amp;quot; are about all I can promise here. &lt;strong&gt;Your
mileage *will* vary.&lt;/strong&gt; As a wise person once said:&lt;/p&gt;
&lt;blockquote&gt;
&amp;quot;All this benchmarking is doing is proving what we already know:
More code takes longer to execute.&amp;quot; - Ben Bangert (dev lead of
Pylons)&lt;/blockquote&gt;
&lt;p&gt;What you should know about Round 3:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The hardware/software platform is the same as in &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;Round
1&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Python v2.5.4 was used for all the Python tests. Ruby v1.8.6 was used
for the Rails test.&lt;/li&gt;
&lt;li&gt;Apache v2.2.3 was used, with
&lt;a class="reference external" href="http://code.google.com/p/modwsgi/"&gt;mod_wsgi&lt;/a&gt; v2.5 for the Python
tests and &lt;a class="reference external" href="http://www.modrails.com/"&gt;Phusion Passenger&lt;/a&gt; v2.2.5 for
the Rails test.&lt;/li&gt;
&lt;li&gt;Note that while the mod_wsgi tests in Round 1 were using &amp;quot;daemon
mode&amp;quot;, the mod_wsgi tests here are using &amp;quot;embedded mode&amp;quot; which is
often faster (although &lt;a class="reference external" href="http://blog.dscpl.com.au/2009/03/load-spikes-and-excessive-memory-usage.html"&gt;not necessarily the recommended
configuration)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The Python component versions used here were: SQLAlchemy v0.5.6,
Genshi v0.5.1, Mako v0.2.5, and Jinja 2.2.1.&lt;/li&gt;
&lt;li&gt;ApacheBench was run with -n 10000 -c 10.&lt;/li&gt;
&lt;li&gt;Each test was run several times to make sure that there were no
anomalies, with the &amp;quot;optimum average&amp;quot; chosen as the numbers
represented here.&lt;/li&gt;
&lt;li&gt;In an attempt to try to make these tests more scientific, I am now
including the source code for each test.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="plain-wsgi-wsgi-test-src-zip"&gt;
&lt;h2&gt;Plain WSGI – &lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/10/wsgi_test_src.zip"&gt;wsgi_test_src.zip&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let's start with a basic &amp;quot;hello world&amp;quot; WSGI test to set the baseline:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
The 'hello world' test:

WSGIServer/0.1
Document Path:          /
Document Length:        12 bytes
Requests per second:    1532.76 [#/sec] (mean)


Apache/2.2.3 (mod_wsgi)
Document Path:          /
Document Length:        12 bytes
Requests per second:    5549.73 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As you can see, mod_wsgi allows Apache to put some serious muscle into
Python's WSGI. It's no wonder that mod_wsgi is quickly becoming the
de-facto standard for Apache Python deployment.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="turbogears-v2-0-3-tg2-test-src-zip"&gt;
&lt;h2&gt;TurboGears v2.0.3 – &lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/10/tg2_test_src.zip"&gt;tg2_test_src.zip&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I mentioned in Round 1, &lt;a class="reference external" href="http://www.turbogears.org/"&gt;TurboGears&lt;/a&gt;
has quickly become my web framework of choice. Building a
&amp;quot;best-of-breed&amp;quot; component stack on top of Pylons &lt;a class="reference external" href="http://percious.com/blog/?p=31"&gt;may not be
easy&lt;/a&gt;, but &lt;a class="reference external" href="http://percious.com/blog/?p=32"&gt;when it
works&lt;/a&gt; I believe it pays off
immensely.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
The 'hello world' test:

Document Path:          /
Document Length:        12 bytes
Requests per second:    935.13 [#/sec] (mean)


Genshi template test:

Document Path:          /genshi_hello/
Document Length:        923 bytes
Requests per second:    601.07 [#/sec] (mean)


Mako template test:

Document Path:          /mako_hello/
Document Length:        932 bytes
Requests per second:    723.18 [#/sec] (mean)


Jinja2 template test:

Document Path:          /jinja_hello/
Document Length:        937 bytes
Requests per second:    764.35 [#/sec] (mean)


Database query tests:

Document Path:          /raw_sql/
Document Length:        1270 bytes
Requests per second:    569.71 [#/sec] (mean)

Document Path:          /genshi_sql/
Document Length:        2409 bytes
Requests per second:    388.96 [#/sec] (mean)

Document Path:          /mako_sql/
Document Length:        2418 bytes
Requests per second:    515.07 [#/sec] (mean)

Document Path:          /jinja_sql/
Document Length:        2472 bytes
Requests per second:    535.06 [#/sec] (mean)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="turbogears-v2-1a1-tg21-test-src-zip"&gt;
&lt;h2&gt;TurboGears v2.1a1 – &lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/10/tg21_test_src.zip"&gt;tg21_test_src.zip&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The TurboGears community is alive and well, and just before finishing
this post the release of v2.1 Alpha 1 was announced. Significant
improvements were made to TG's object dispatch, so I was excited to see
what the new numbers would look like.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
The 'hello world' test:

Document Path:          /
Document Length:        11 bytes
Requests per second:    1118.92 [#/sec] (mean)


Genshi template test:

Document Path:          /genshi_hello/
Document Length:        923 bytes
Requests per second:    713.97 [#/sec] (mean)


Mako template test:

Document Path:          /mako_hello/
Document Length:        932 bytes
Requests per second:    812.64 [#/sec] (mean)


Jinja2 template test:

Document Path:          /jinja_hello/
Document Length:        937 bytes
Requests per second:    1000.98 [#/sec] (mean)


Database query tests:

Document Path:          /raw_sql/
Document Length:        1270 bytes
Requests per second:    673.04 [#/sec] (mean)

Document Path:          /genshi_sql/
Document Length:        2409 bytes
Requests per second:    433.45 [#/sec] (mean)

Document Path:          /mako_sql/
Document Length:        2418 bytes
Requests per second:    601.89 [#/sec] (mean)

Document Path:          /jinja_sql/
Document Length:        2472 bytes
Requests per second:    630.66 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As you can see, the future of TurboGears looks very bright, and I only
expect these numbers to get better as the final 2.1 release approaches
(expected in Q1 2010).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pylons-v0-9-7-pylons-test-src-zip"&gt;
&lt;h2&gt;Pylons v0.9.7 – &lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/10/pylons_test_src.zip"&gt;pylons_test_src.zip&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.pylonshq.com/"&gt;Pylons&lt;/a&gt; is the foundation which TurboGears
is built upon, and for those who don't need all &amp;quot;the extras&amp;quot; it is an
excellent choice.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
The 'hello world' test:

Document Path:          /hello/index
Document Length:        12 bytes
Requests per second:    2593.47 [#/sec] (mean)


Mako template test:

Document Path:          /hello/hello
Document Length:        932 bytes
Requests per second:    1737.03 [#/sec] (mean)


Database query tests:

Document Path:          /hello/raw_sql
Document Length:        1270 bytes
Requests per second:    964.07 [#/sec] (mean)

Document Path:          /hello/hellodb
Document Length:        2418 bytes
Requests per second:    831.26 [#/sec] (mean)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="django-v1-1-django-test-src-zip"&gt;
&lt;h2&gt;Django v1.1 – &lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/10/django_test_src.zip"&gt;django_test_src.zip&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While &lt;a class="reference external" href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt; is probably Python's
most popular web framework, I have never really clicked with it. Its way
of doing things is significantly different than TG/Pylons, but if you
don't mind its
&amp;quot;&lt;a class="reference external" href="http://docs.djangoproject.com/en/dev/faq/general/#django-appears-to-be-a-mvc-framework-but-you-call-the-controller-the-view-and-the-view-the-template-how-come-you-don-t-use-the-standard-names"&gt;MTV&lt;/a&gt;&amp;quot;
architectural pattern and can survive without SQLAlchemy (although there
are hacks), it is a very capable framework.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
The 'hello world' test:

Document Path:          /hello
Document Length:        12 bytes
Requests per second:    3376.48 [#/sec] (mean)


Django template test:

Document Path:          /hellos
Document Length:        936 bytes
Requests per second:    1781.43 [#/sec] (mean)


Database query test:

Document Path:          /hellodb
Document Length:        2476 bytes
Requests per second:    972.11 [#/sec] (mean)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="bottle-0-5-8-bottle-test-src-zip"&gt;
&lt;h2&gt;Bottle 0.5.8 – &lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/10/bottle_test_src.zip"&gt;bottle_test_src.zip&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://bottle.paws.de/"&gt;Bottle&lt;/a&gt; caught my attention a few months ago
while surveying the WSGI landscape for an alternative to PHP when
building websites that only require a few pieces of dynamic
functionality. As a &amp;quot;micro-framework&amp;quot;, it does an excellent job at
bridging the gap between &amp;quot;pure WSGI&amp;quot; and its &amp;quot;feature-rich&amp;quot; big
brothers.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
The 'hello world' test:

Document Path:          /
Document Length:        12 bytes
Requests per second:    5545.96 [#/sec] (mean)


Mako template test:

Document Path:          /hello
Document Length:        923 bytes
Requests per second:    3588.04 [#/sec] (mean)


Database query test (SQLAlchemy):

Document Path:          /hellodb
Document Length:        2413 bytes
Requests per second:    1479.54 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As you can see, there was practically no difference in speed between
Bottle and pure WSGI in a basic &amp;quot;hello world&amp;quot; test. Even with the
addition of Mako and SQLAlchemy, Bottle performed significantly faster
than a bare Pylons or Django setup.&lt;/p&gt;
&lt;p&gt;(Note: Mako template inheritance was not used in the Bottle Mako test.)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ruby-on-rails-v2-3-3-rails-test-src-zip"&gt;
&lt;h2&gt;Ruby on Rails v2.3.3 – &lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/10/rails_test_src.zip"&gt;rails_test_src.zip&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What web technology shootout would be complete without
&lt;a class="reference external" href="http://www.rubyonrails.org/"&gt;Rails&lt;/a&gt;? Although I am not a Rails fan
(mostly because I can't stand Ruby), I believe that in many ways we owe
much of the popularity and success of the web framework movement to
Rails.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
The 'hello world' test:

Document Path:          /hello/hello
Document Length:        12 bytes
Requests per second:    1048.18 [#/sec] (mean)


Rails template test:

Document Path:          /hello/hellos
Document Length:        937 bytes
Requests per second:    949.54 [#/sec] (mean)


Database query test:

Document Path:          /hello/hellodb
Document Length:        2482 bytes
Requests per second:    734.18 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;(Note: I think my version of Ruby was using a newer version of the
SQLite3 library than my version of Python. This may or may not have
given Rails an advantage on the database query test.)&lt;/p&gt;
&lt;p&gt;For those of you who like charts, here's a comparison of the &amp;quot;return
hello world&amp;quot; test results for each framework:&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/10/hello_world_test.png"&gt;&lt;img alt="The return hello world test chart" src="https://blog.curiasolutions.com/static/uploads/2009/10/hello_world_test.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And here's a comparison of the template test:&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/10/template_test.png"&gt;&lt;img alt="The template test chart" src="https://blog.curiasolutions.com/static/uploads/2009/10/template_test.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finally, here's the template test with the database query added:&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/10/database_test.png"&gt;&lt;img alt="Template test with database query" src="https://blog.curiasolutions.com/static/uploads/2009/10/database_test.png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="closing-thoughts"&gt;
&lt;h2&gt;Closing thoughts&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Gone are the days of Rails as &amp;quot;the one framework to rule them all.&amp;quot;
C'mon guys, it's not 2005 anymore.&lt;/li&gt;
&lt;li&gt;Python has gotten a lot of flak from other language camps for its
abundance of web frameworks. While some Pythonists have been
embarrassed by this, I only see it as proof of Python's success as a
language. My retort would be: &amp;quot;If it was as easy to write a web
framework in your language as it is in Python, you'd have the same
problem!&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="looking-for-updated-results-check-out-round-4"&gt;
&lt;h3&gt;Looking for updated results? Check out &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-technology-shootout---round-4-pyramid-vs-django-vs-tg-vs-rails-2-3.html"&gt;Round 4&lt;/a&gt;.&lt;/h3&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Seth Davis</dc:creator><pubDate>Mon, 05 Oct 2009 11:42:00 -0700</pubDate><guid>tag:blog.curiasolutions.com,2009-10-05:the-great-web-technology-shootout-round-3-better-faster-and-shinier.html</guid><category>Django</category><category>performance</category><category>Pylons</category><category>Rails</category><category>TurboGears</category></item><item><title>The great web technology shootout - Round 2: PHP deserves a helping hand</title><link>https://blog.curiasolutions.com/the-great-web-technology-shootout-round-2-php-deserves-a-helping-hand.html</link><description>&lt;p class="alert alert-warning"&gt;A lot of the information below is out of date. Please see the new
&lt;a class="reference external" href="https://blog.curiasolutions.com/pages/the-great-web-framework-shootout.html"&gt;framework shootout page&lt;/a&gt; for the latest
benchmarks.&lt;/p&gt;
&lt;p class="alert alert-info"&gt;This post is the continuation of a series. Please read &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;Round 1&lt;/a&gt; first if you are just
now joining.&lt;/p&gt;
&lt;p&gt;In &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;Round 1&lt;/a&gt;, PHP was
looking like quite the tortoise of the group. However, if you're familiar with
some of the core differences between Python &amp;amp; PHP, you'll know that
Python has been &amp;quot;cheating&amp;quot; slightly.&lt;/p&gt;
&lt;p&gt;Let me explain: By default, Python compiles each script into bytecode on
its first execution, allowing this bottleneck to be skipped on
subsequent runs. PHP, however does not perform this type of optimization
by default (in the 5.x line at least), so the PHP interpreter must
re-compile each file every time it is run. As you can imagine, this can
give PHP (without an accelerator) a huge disadvantage when compared to
languages such as Python.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;With this in mind, I have decided to take Round 2 to focus solely
PHP.&lt;/strong&gt; This will hopefully provide a clear picture of the benefits of
PHP bytecode caching (at least when it comes to page-views — the memory
benefits are a whole other story), and give you an idea of PHP's
performance with the help of an accelerator.&lt;/p&gt;
&lt;p&gt;There are &lt;a class="reference external" href="http://en.wikipedia.org/wiki/List_of_PHP_accelerators"&gt;many PHP accelerators
available&lt;/a&gt;,
but I have chosen &lt;a class="reference external" href="http://php.net/apc"&gt;APC&lt;/a&gt; for use here (mostly due
to its inclusion in the upcoming PHP 6 core).&lt;/p&gt;
&lt;div class="section" id="what-you-should-know-about-round-2"&gt;
&lt;h2&gt;What you should know about Round 2:&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;The hardware/software platform is the same as in &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;Round
1&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;PHP has been upgraded to v5.2.9.&lt;/li&gt;
&lt;li&gt;The APC tests are using APC v3.0.19.&lt;/li&gt;
&lt;li&gt;ApacheBench was run with -n # -c 10, with -n usually being 5000 or
10000, depending on the test subject's speed.&lt;/li&gt;
&lt;li&gt;Each test was run several times to make sure that there were no
anomalies, with the &amp;quot;optimum average&amp;quot; chosen as the numbers
represented here.&lt;/li&gt;
&lt;li&gt;In an attempt to try to make these tests more scientific, I am now
including the source code for each test (minus the CMS tests, which
were performed on a standard install).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My apc.ini settings:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
extension = apc.so
apc.enabled = 1
apc.shm_size = 96
apc.include_once_override = 1
apc.mmap_file_mask = /tmp/apc.XXXXXX
&lt;/pre&gt;
&lt;div class="section" id="raw-php-php-test-src-zip"&gt;
&lt;h3&gt;Raw PHP - &lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/09/php_test_src.zip"&gt;php_test_src.zip&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To investigate the speed of &amp;quot;raw PHP&amp;quot;, I created a couple of minimal PHP
scripts that printed out a simple &amp;quot;hello world&amp;quot;, and read a few lines of
text from a database. Take a look at the test source code for reference.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="apc-disabled"&gt;
&lt;h2&gt;APC Disabled&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
The 'hello world' test:

Document Path:          /hello.php
Document Length:        12 bytes
Requests per second:    4199.43 [#/sec] (mean)

The simple database query test:

Document Path:          /hello_db.php
Document Length:        2524 bytes
Requests per second:    2797.54 [#/sec] (mean)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="apc-enabled"&gt;
&lt;h2&gt;APC Enabled&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
The 'hello world' test:

Document Path:          /hello.php
Document Length:        12 bytes
Requests per second:    6618.42 [#/sec] (mean)

Simple database query test:

Document Path:          /hello_db.php
Document Length:        2524 bytes
Requests per second:    3713.99 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Right out of the gate, we can see that APC gives PHP a significant
boost.&lt;/p&gt;
&lt;p&gt;Of course, a script that simply does a &amp;quot;echo 'hello world'&amp;quot; isn't really
practical, so let's take a look at a couple of popular PHP frameworks to
see what a full stack looks like.&lt;/p&gt;
&lt;div class="section" id="cakephp-v1-2-5-cakephp-test-src-zip"&gt;
&lt;h3&gt;CakePHP v1.2.5 - &lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/09/cakephp_test_src.zip"&gt;cakephp_test_src.zip&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CakePHP was the slowest test subject by a large margin in &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;Round
1&lt;/a&gt;, but if you take a look
at the caching section of its core configuration file you can see that the
developers have written it with acceleration in mind. It includes
special configuration directives for not just APC, but also
&lt;a class="reference external" href="http://xcache.lighttpd.net/"&gt;Xcache&lt;/a&gt; and
&lt;a class="reference external" href="http://www.danga.com/memcached/"&gt;Memcache&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Still though, without acceleration CakePHP is a sad story indeed:&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="apc-disabled-file-caching-engine-default"&gt;
&lt;h2&gt;APC Disabled - File Caching Engine (default)&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
The return 'hello world' test:

Document Path:          /hellos/
Document Length:        13 bytes
Requests per second:    45.25 [#/sec] (mean)

The template hello world test:

Document Path:          /hellos/hello
Document Length:        931 bytes
Requests per second:    39.90 [#/sec] (mean)

The simple database query test (using the ORM):

Document Path:          /hellos/hello_db
Document Length:        2469 bytes
Requests per second:    38.80 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;However, with the help of APC things finally begin to look reasonable:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="apc-enabled-apc-caching-engine-in-core-php"&gt;
&lt;h2&gt;APC Enabled - APC Caching Engine (in core.php)&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
The return 'hello world' test:

Document Path:          /hellos/
Document Length:        13 bytes
Requests per second:    140.98 [#/sec] (mean)

The template hello world test:

Document Path:          /hellos/hello
Document Length:        931 bytes
Requests per second:    120.04 [#/sec] (mean)

The simple database query test (using the ORM):

Document Path:          /hellos/hello_db
Document Length:        2469 bytes
Requests per second:    117.56 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As you can see, APC increases CakePHP's speed about threefold in these
examples. These results may or may not be acceptable to you (remember
that we are running these tests with a concurrency of 10), but I felt
much better about CakePHP after pairing it with APC.&lt;/p&gt;
&lt;div class="section" id="kohana-v3-0-kohana3-test-src-zip"&gt;
&lt;h3&gt;Kohana v3.0 - &lt;a class="reference external" href="https://blog.curiasolutions.com/static/uploads/2009/09/kohana3_test_src.zip"&gt;kohana3_test_src.zip&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Kohana (a divergent of CodeIgnitor) is a much more lightweight PHP
framework than CakePHP, but still gives you a pretty full featureset.
Since v3.0 was released shortly after &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;Round
1&lt;/a&gt;, I decided to use the
new version for the tests here in Round 2.&lt;/p&gt;
&lt;p&gt;It should be noted that although file caching is enabled in a default
CakePHP install, Kohana's caching is disabled by default. I have
included a couple of tests with caching disabled here to exhibit the
performance of a default Kohana install.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="apc-disabled-caching-disabled-default"&gt;
&lt;h2&gt;APC Disabled - Caching Disabled (default)&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
The return 'hello world' test:

Document Path:          /hello/index
Document Length:        12 bytes
Requests per second:    86.73 [#/sec] (mean)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="apc-disabled-caching-enabled-bootstrap-php"&gt;
&lt;h2&gt;APC Disabled - Caching Enabled (bootstrap.php)&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
The return 'hello world' test:

Document Path:          /hello/index
Document Length:        12 bytes
Requests per second:    180.50 [#/sec] (mean)

The template hello world test:

Document Path:          /hellos/index
Document Length:        930 bytes
Requests per second:    162.27 [#/sec] (mean)

The simple database query test (using the Database module):

Document Path:          /hellodb/index
Document Length:        2525 bytes
Requests per second:    113.92 [#/sec] (mean)

The simple database query test (using the ORM module):

Document Path:          /helloorm/index
Document Length:        2525 bytes
Requests per second:    99.73 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As you can see, even without APC Kohana is quite a bit faster than
CakePHP.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="apc-enabled-caching-disabled"&gt;
&lt;h2&gt;APC Enabled - Caching Disabled&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
The return 'hello world' test:

Document Path:          /hello/index
Document Length:        12 bytes
Requests per second:    125.93 [#/sec] (mean)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="apc-enabled-caching-enabled-bootstrap-php"&gt;
&lt;h2&gt;APC Enabled - Caching Enabled (bootstrap.php)&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
The return 'hello world' test:

Document Path:          /hello/index
Document Length:        12 bytes
Requests per second:    280.67 [#/sec] (mean)

The template hello world test:

Document Path:          /hellos/index
Document Length:        930 bytes
Requests per second:    253.50 [#/sec] (mean)

The simple database query test (using the Database module):

Document Path:          /hellodb/index
Document Length:        2525 bytes
Requests per second:    173.28 [#/sec] (mean)

The simple database query test (using the ORM module):

Document Path:          /helloorm/index
Document Length:        2525 bytes
Requests per second:    153.90 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Although not a threefold gain as with CakePHP, Kohana with APC+caching
definitely makes a good attempt at shortening the gap seen in &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-development-shootout.html"&gt;Round
1&lt;/a&gt; between the PHP
frameworks and the offerings from other languages. Of course, if raw speed is
what you're looking for, there are &lt;a class="reference external" href="http://www.doophp.com/benchmark"&gt;faster PHP frameworks
available&lt;/a&gt; (note that the benchmarks
on that page were run on a completely different setup and should not be
compared with any of the numbers here).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Next, let's take a look at how APC enhances the performance of a few
of PHP's heavyweights.&lt;/strong&gt;&lt;/p&gt;
&lt;div class="section" id="wordpress-v2-8-4-default-install"&gt;
&lt;h3&gt;WordPress v2.8.4 (default install)&lt;/h3&gt;
&lt;p&gt;Although I've heard that WordPress has a built-in caching setting, a
quick glance at wp-settings.php did not reveal anything. So, there are
simply two WordPress tests—with APC, and without:&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;APC Disabled&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
Document Path:          /
Document Length:        5519 bytes
Requests per second:    24.80 [#/sec] (mean)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;APC Enabled&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
Document Path:          /
Document Length:        5519 bytes
Requests per second:    74.00 [#/sec] (mean)
&lt;/pre&gt;
&lt;div class="section" id="joomla-v1-5-14-default-install-without-sample-data"&gt;
&lt;h3&gt;Joomla v1.5.14 (default install, without sample data)&lt;/h3&gt;
&lt;p&gt;The results of these tests were quite disappointing to me, and made me
wonder if I was doing something wrong. Sadly, it looks as though Joomla
is quite the slow one of the bunch.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;APC Disabled&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
Joomla Caching Disabled (default):

Document Path:          /
Document Length:        4006 bytes
Requests per second:    27.90 [#/sec] (mean)

Joomla Caching Enabled (File):

Document Path:          /
Document Length:        4006 bytes
Requests per second:    31.46 [#/sec] (mean)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;APC Enabled&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
Joomla Caching Disabled:

Document Path:          /
Document Length:        4006 bytes
Requests per second:    41.27 [#/sec] (mean)

Joomla Caching Enabled (APC):

Document Path:          /
Document Length:        4006 bytes
Requests per second:    45.01 [#/sec] (mean)
&lt;/pre&gt;
&lt;div class="section" id="drupal-v6-14-default-install-clean-urls-disabled"&gt;
&lt;h3&gt;Drupal v6.14 (default install, clean URLs disabled)&lt;/h3&gt;
&lt;p&gt;Drupal has several caching settings, so I decided to see what each one
would look like and how much of a difference &amp;quot;aggressive&amp;quot; caching made.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;APC Disabled&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
Drupal Caching Disabled (default):

Document Path:          /
Document Length:        5548 bytes
Requests per second:    36.72 [#/sec] (mean)

&amp;quot;Normal&amp;quot; Drupal Caching:

Document Path:          /
Document Length:        5548 bytes
Requests per second:    289.69 [#/sec] (mean)

&amp;quot;Aggressive&amp;quot; Drupal Caching:

Document Path:          /
Document Length:        5548 bytes
Requests per second:    335.68 [#/sec] (mean)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;APC Enabled&lt;/h2&gt;
&lt;pre class="literal-block"&gt;
Drupal Caching Disabled:

Document Path:          /
Document Length:        5548 bytes
Requests per second:    83.93 [#/sec] (mean)

&amp;quot;Normal&amp;quot; Drupal Caching:

Document Path:          /
Document Length:        5548 bytes
Requests per second:    498.37 [#/sec] (mean)

&amp;quot;Aggressive&amp;quot; Drupal Caching:

Document Path:          /
Document Length:        5548 bytes
Requests per second:    564.48 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As you can see, Drupal's caching (especially when combined with APC)
changes things drastically. These results seem to indicate that Drupal's
caching does a great job of allowing the CMS to get out of the way.&lt;/p&gt;
&lt;div class="section" id="closing-thoughts"&gt;
&lt;h3&gt;Closing thoughts&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The benefits of a PHP accelerator are really too good to pass up.
Even without written-in caching, an accelerator can give your code a
significant boost.&lt;/li&gt;
&lt;li&gt;While these tests have only really explored the impact on website
page-load times, a PHP accelerator typically also cuts down on the
amount of memory used by your PHP scripts.&lt;/li&gt;
&lt;li&gt;PHP 6 has promised to fix a lot of things, including the lack of
built-in bytecode caching. It will be interesting to revisit these
tests in a few years (?) to see how much optimization the new version
brings to the table.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="can-t-get-enough-of-this-madness-just-wait-until-you-read-round-3"&gt;
&lt;h2&gt;Can't get enough of this madness? Just wait until you read &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-technology-shootout-round-3-better-faster-and-shinier.html"&gt;Round 3&lt;/a&gt;&lt;/h2&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Seth Davis</dc:creator><pubDate>Mon, 21 Sep 2009 23:53:00 -0700</pubDate><guid>tag:blog.curiasolutions.com,2009-09-21:the-great-web-technology-shootout-round-2-php-deserves-a-helping-hand.html</guid><category>performance</category><category>PHP</category><category>web-development</category></item><item><title>The great web technology shootout - Round 1: A quick glance at the landscape</title><link>https://blog.curiasolutions.com/the-great-web-development-shootout.html</link><description>&lt;p class="alert alert-warning"&gt;A lot of the information below is out of date. Please see the new
&lt;a class="reference external" href="https://blog.curiasolutions.com/pages/the-great-web-framework-shootout.html"&gt;framework shootout page&lt;/a&gt; for the latest
benchmarks.&lt;/p&gt;
&lt;p&gt;Recently I went on a benchmarking spree and decided to throw
&lt;a class="reference external" href="http://en.wikipedia.org/wiki/ApacheBench"&gt;ApacheBench&lt;/a&gt; at a bunch of
the different web development technology platforms I interact with on a
day-to-day basis. The results were interesting enough to me that I
decided I'd take a post to share them here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer: The following test results should be taken with a
*massive* grain of salt.&lt;/strong&gt; If you know anything about benchmarking, you
will know that the slightest adjustments have the potential to change
things drastically. While I have tried to perform each test as fairly
and accurately as possible, it would be foolish to consider these
results as scientific in any way. &lt;strong&gt;It should also be noted that my goal
here was not to see how fast each technology performs at its most
optimized configuration, but rather what a minimal out-of-the-box
experience looks like.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Test platform info:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The hardware was an Intel Core2Quad Q9300, 2.5Ghz, 6MB Cache,
1333FSB, 2GB DDR RAM.&lt;/li&gt;
&lt;li&gt;The OS was CentOS v5.3 32-bit with a standard Apache Webserver setup.&lt;/li&gt;
&lt;li&gt;ApacheBench was used with only the -n and -c flags (1000 requests for
the PHP frameworks, 5000 requests for everything else).&lt;/li&gt;
&lt;li&gt;Each ApacheBench test was run 5-10 times, with the &amp;quot;optimum average&amp;quot;
chosen as the numbers represented here.&lt;/li&gt;
&lt;li&gt;The PHP tests were done using the standard Apache PHP module.&lt;/li&gt;
&lt;li&gt;The mod_wsgi tests were done in daemon mode set to 2 processes/15
threads.&lt;/li&gt;
&lt;li&gt;The SQL tests were done with mysqli ($mysql-&amp;gt;query()) on PHP, and
SQLAlchemy (conn.execute()) on Python fetching and printing 5 rows of
data from a sample database.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="apache-v2-2-3"&gt;
&lt;h2&gt;Apache v2.2.3&lt;/h2&gt;
&lt;p&gt;We will start with the raw Apache benchmark.&lt;/p&gt;
&lt;p&gt;For this test, Apache loaded a simple HTML file with random text:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        6537 bytes
Requests per second:    8356.23 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As expected, Apache is lightning fast.&lt;/p&gt;
&lt;p&gt;Ok, so now that we've set the high water mark, let's take a look at some
popular web technology platforms...&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="php-v5-2-8"&gt;
&lt;h2&gt;PHP v5.2.8&lt;/h2&gt;
&lt;p&gt;For this test, I created a PHP file that simply printed the $_SERVER
array and some random text:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        6135 bytes
Requests per second:    2812.22 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Next, I decided to add some mysqli() code to connect to a database and
print out a few rows of data:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        283 bytes
Requests per second:    2135.46 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Not at all bad, but who really wants to build a webapp using plain old
PHP anymore?&lt;/p&gt;
&lt;p&gt;Next I decided to test the two PHP frameworks that I am most familiar
with.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="cakephp-v1-2-5"&gt;
&lt;h2&gt;CakePHP v1.2.5&lt;/h2&gt;
&lt;p&gt;For this test, I did a basic CakePHP install, and added a route that
printed &amp;quot;hello world&amp;quot; from a template:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        944 bytes
Requests per second:    42.00 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As you can see, frameworks can be a blessing and a curse. On a side
note, CakePHP was the only test that gave me failed requests.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="kohana-v2-3-4"&gt;
&lt;h2&gt;Kohana v2.3.4&lt;/h2&gt;
&lt;p&gt;For this test, I did a basic install and loaded Kohana's default
welcome/index setup (which seemed lightweight enough to me to qualify as
the test focal point):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        1987 bytes
Requests per second:    100.11 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Kohana fared much better than CakePHP, but it appears as though PHP's
speed breaks down quickly once you start building a framework around it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="python-v2-5-4"&gt;
&lt;h2&gt;Python v2.5.4&lt;/h2&gt;
&lt;p&gt;Unlike PHP, Python wasn't specifically created for the web. However,
several years ago the &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface"&gt;WSGI
spec&lt;/a&gt; was
introduced which makes building web applications with Python a breeze.
There are many different ways to depoly a WSGI app, but my tests were
limited to Python's built-in WSGIServer, and Apache's
&lt;a class="reference external" href="http://code.google.com/p/modwsgi/"&gt;mod_wsgi&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="wsgiserver"&gt;
&lt;h2&gt;WSGIServer&lt;/h2&gt;
&lt;p&gt;For this test, I used WSGI.org's
&lt;a class="reference external" href="http://hg.moinmo.in/moin/1.8/raw-file/tip/wiki/server/test.wsgi"&gt;test.wsgi&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        4123 bytes
Requests per second:    1658.35 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;These results were quite impressive to me, considering that this is pure
Python at work.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="mod-wsgi"&gt;
&lt;h2&gt;mod_wsgi&lt;/h2&gt;
&lt;p&gt;mod_wsgi is Apache's module to process WSGI scripts. This test used the
exact same script as the previous WSGIServer example:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        9552 bytes
Requests per second:    3994.30 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Don't look now, but those results are better than the vanilla PHP test!&lt;/p&gt;
&lt;p&gt;Next, I added SQLAlchemy imports to the test file and did a
conn.execute(), in an attempt to try to mimic the PHP mysqli() test:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        10212 bytes
Requests per second:    3379.87 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;I expected to lose quite a bit of speed with the introduction of an ORM,
but it looks like SQLAlchemy is quite fast. These results were still
over 1,000reqs/s more than the PHP example.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tornadoserver"&gt;
&lt;h2&gt;TornadoServer&lt;/h2&gt;
&lt;p&gt;There's been a lot of buzz lately about the new
&lt;a class="reference external" href="http://www.tornadoweb.org/"&gt;TornadoServer&lt;/a&gt;, so I thought I'd throw
it in the mix. This test uses their basic &amp;quot;hello world&amp;quot; example:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        12 bytes
Requests per second:    3330.00 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Nothing shocking here at first glance, but the Tornado is built to
handle extreme loads. So, I increased the concurrency to 1,000 and threw
10,000 requests at it:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        12 bytes
Concurrency Level:      1000
Complete requests:      10000
Requests per second:    3288.15 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Almost exactly the same results! On a side note, none of the other tests
subjects were able to handle a concurrency of 1,000.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="turbogears-v2-0-3-mod-wsgi"&gt;
&lt;h2&gt;TurboGears v2.0.3 (mod_wsgi)&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.turbogears.com/"&gt;TurboGears&lt;/a&gt; has quickly become my
framework of choice, so naturally it was the first Python framework I
tested.&lt;/p&gt;
&lt;p&gt;For this test I setup a default quickstart project, and then added a
hello() controller method which simply returned a &amp;quot;hello world&amp;quot; string:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        11 bytes
Requests per second:    470.18 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Next, I added a simple Genshi template with 1 level of inheritance:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        1351 bytes
Requests per second:    143.26 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;While those results are not terrible, they should encourage you to check
out the other templating options.&lt;/p&gt;
&lt;p&gt;Next, I performed the same test with Mako:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        1534 bytes
Requests per second:    394.41 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As you can see, Mako is significantly faster than Genshi.&lt;/p&gt;
&lt;p&gt;TurboGears also supports Jinja2, so I thought I'd also see how it held
up:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        1020 bytes
Requests per second:    401.04 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;I was not expecting Jinja2 to be faster than Mako, so I am not entirely
sure about these results. However, the difference is only slight, and
might have something to do with the fact that the dotted template names
finder is disabled for Jinja templates.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pylons-v0-9-7-mod-wsgi"&gt;
&lt;h2&gt;Pylons v0.9.7 (mod_wsgi)&lt;/h2&gt;
&lt;p&gt;Mark Ramm (the current development lead of TurboGears) has been quoted
as saying &amp;quot;TurboGears is to Pylons as Ubuntu is to Debian.&amp;quot; With that in
mind, I naturally wanted to see how much speed I was losing with
TurboGears' &amp;quot;additions&amp;quot;.&lt;/p&gt;
&lt;p&gt;For this test I tried to mimic the first TurboGears test (basic layout,
returning a string):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        11 bytes
Requests per second:    1961.00 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;While I was expecting a gain from TurboGears' omissions, I wasn't quite
expecting over four times the increase. I can only assume that the bare
Pylons setup is pretty minimal, while TurboGears bootstraps a lot of
stuff on for you by default.&lt;/p&gt;
&lt;p&gt;Duplicating the Mako test in Pylons was also quite fast:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        9743 bytes
Requests per second:    1183.39 [#/sec] (mean)
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="bottle-v0-5-7-mod-wsgi"&gt;
&lt;h2&gt;Bottle v0.5.7 (mod_wsgi)&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://bottle.paws.de/"&gt;Bottle&lt;/a&gt; caught my attention recently as a
minimalistic Python web framework, so I thought I'd put it through the
grind as well.&lt;/p&gt;
&lt;p&gt;For this test, I used their simple &amp;quot;hello world&amp;quot; example:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        12 bytes
Requests per second:    3912.43 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As you can see, Bottle is very close to bare WSGI as far as speed goes.
On a side note, adding a sample template using Bottle's default
templating package didn't seem to change these numbers at all.&lt;/p&gt;
&lt;p&gt;Next I tested Bottle with its templating system switched to Mako:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        41 bytes
Requests per second:    3134.61 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Then I added a SQLAlchemy and performed a query:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        9374 bytes
Requests per second:    2503.27 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;What was exciting to me about this test was that I found myself looking
at a minimal Python framework equipped with a full templating system and
ORM which all performed faster than the PHP+mysqli() test.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="django-v1-1-mod-wsgi"&gt;
&lt;h2&gt;Django v1.1 (mod_wsgi)&lt;/h2&gt;
&lt;p&gt;I don't use &lt;a class="reference external" href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;, but I thought it
would be worth including in this report. This is a basic Django setup
printing out a simple hello world template:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        1471 bytes
Requests per second:    564.41 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;As expected, a minimal Django setup is quite fast. (Note: This test
included a SQLite database query.)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ruby-on-rails-v2-2-3-mod-passenger"&gt;
&lt;h2&gt;Ruby On Rails v2.2.3 (mod_passenger)&lt;/h2&gt;
&lt;p&gt;I also don't use &lt;a class="reference external" href="http://rubyonrails.org/"&gt;Rails&lt;/a&gt;, but I figured this
report would be incomplete without Rails on the list. So, I took
&lt;a class="reference external" href="http://gems.mediatemple.net/testapp_rails.tgz"&gt;MediaTemple's Rails test
app&lt;/a&gt; and loaded up a
test page:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Document Length:        1291 bytes
Requests per second:    433.57 [#/sec] (mean)
&lt;/pre&gt;
&lt;p&gt;Note that the test app used here is doing a little bit more than just
returning &amp;quot;Hello World&amp;quot;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="my-conclusions"&gt;
&lt;h2&gt;My Conclusions&lt;/h2&gt;
&lt;p&gt;As I said in the disclaimer, to arrive at any sort of decisive
conclusions based on these numbers alone would be foolish. However, here
is what all of this left me thinking:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;All of these technologies performed adequately for use with most
projects.&lt;/li&gt;
&lt;li&gt;CakePHP was the only test that produced less than 100 requests per
second. However, there are many articles available on optimizing
CakePHP.&lt;/li&gt;
&lt;li&gt;These results made me wish I had moved away from PHP much earlier in
my web development carrier. There are just better options these days
when it comes to choosing a web development language (imho). If you
&lt;em&gt;must&lt;/em&gt; use PHP for a large project, I strongly suggest that you look
into &lt;a class="reference external" href="http://en.wikipedia.org/wiki/PHP_accelerator"&gt;an
accelerator&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The Bottle+Mako+SQLAlchemy results struck me as an extremely minimal
drop-in replacement for simple PHP sites.&lt;/li&gt;
&lt;li&gt;mod_wsgi is a huge winner in deploying Python webapps. Way to go
Graham!&lt;/li&gt;
&lt;li&gt;Do your own bencharking, because &lt;em&gt;your mileage **will*&lt;/em&gt; vary*!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="like-what-you-ve-seen-here-check-out-round-2"&gt;
&lt;h3&gt;Like what you've seen here? Check out &lt;a class="reference external" href="https://blog.curiasolutions.com/the-great-web-technology-shootout-round-2-php-deserves-a-helping-hand.html"&gt;Round 2&lt;/a&gt;.&lt;/h3&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Seth Davis</dc:creator><pubDate>Sat, 12 Sep 2009 22:29:00 -0700</pubDate><guid>tag:blog.curiasolutions.com,2009-09-12:the-great-web-development-shootout.html</guid><category>Django</category><category>performance</category><category>PHP</category><category>Python</category><category>Rails</category><category>TurboGears</category><category>web-development</category></item><item><title>Why I've fallen in love with Python</title><link>https://blog.curiasolutions.com/why-ive-fallen-in-love-with-python.html</link><description>&lt;p&gt;Now that I'm using Python for a large percentage of my development, I thought
it would be fun to highlight a few reasons why Python has become my new
language of choice.&lt;/p&gt;
&lt;p&gt;In an effort to help you understand where I'm coming from, let me
briefly rehash some of my programming history: I spent much of the
90's doing dynamic web development using Perl (weren't &lt;em&gt;those&lt;/em&gt; the
days). I eventually migrated to PHP which usually made things much
easier on the web; and subsequently replaced most of my console
scripting with BASH [shell scripting]. However, I'm kind of a hack and
love languages so I have occasionally been known to write something in
C; and although I'm not a complete stranger to Java and Ruby, I never
really felt like I &amp;quot;clicked&amp;quot; with either of those languages.&lt;/p&gt;
&lt;p&gt;Ok, now that I've hopefully convinced you that I'm not just a
fly-by-night programmer, let me show you some Python code. Brace
yourself, as this article is bound to get lengthy...&lt;/p&gt;
&lt;blockquote&gt;
Reason #1: &amp;quot;Whitespace done right&amp;quot; is actually a good thing&lt;/blockquote&gt;
&lt;p&gt;The first thing that people either absolutely love or adamantly hate
about Python is the fact that its syntax is heavily tied to proper
usage of whitespace. At first glance, this causes many curious onlookers
from other languages to shy away from Python and continue in their
brace-encapsulated bondage. I'll admit, at first I wasn't too wild
about these new restrictions either (I'm a programmer—I should be able
to format my code how I like!); but the first time I had to go back and do
something in PHP for a client, I quickly realized I never wanted to wrap
anything in curly braces again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# An example function that&amp;#39;s free from braces, (some) parenthesis, and semicolons&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;That isn&amp;#39;t a number!&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;This number is odd&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;This number is even&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Not only does this make Python source code incredibly readable, but you
also end up typing less.&lt;/p&gt;
&lt;blockquote&gt;
Reason #2: I almost always know what type of object I'm working with&lt;/blockquote&gt;
&lt;p&gt;One thing that always annoyed me about Perl was that you set an array or
hash using the '&amp;#64;' or '%' sigils, but typically accessed them using the
'$' sigil. PHP simplified this a bit, but &amp;quot;$array = array();&amp;quot; could be a
simple list or an associative array (known as a dictionary in Python).
With Python on the other hand, the enclosure defines the type (for most
built-in types at least), and since everything is an object there's no
need for silly sigils:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# the type of enclosure makes a distinction between a string , a list, and a dictionary&lt;/span&gt;
&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;This is a string&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;This is a string&amp;#39;&lt;/span&gt;

&lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;This&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;is&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;list&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;This&amp;#39;&lt;/span&gt;

&lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;first_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;This&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;second_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;is&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;third_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;fourth_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;dictionary&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fourth_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;dictionary&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;On a somewhat related note, you can use these objects on-the-fly in your
scripts without even having to assign them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;This&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;is&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;list&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;is&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;first_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;This&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;second_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;is&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;third_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;fourth_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;dictionary&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;first_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;This&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This isn't very practical of course, but it can be nice when debugging.&lt;/p&gt;
&lt;blockquote&gt;
Reason #3: I can throw practically any type of object almost anywhere I want&lt;/blockquote&gt;
&lt;p&gt;Ok, so that might be a slight exaggeration; but the truth is that since
Python treats everything as an object, you're free to pack things away
in the strangest of places. A great way of demonstrating the power and
flexibility of Python is in an example showing a few ways different
types of objects can be stored and accessed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# create a function&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Hello world!&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;# create a file handle&lt;/span&gt;
&lt;span class="n"&gt;hello_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;~/hello.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# create a list with the function, the file handle, and a string&lt;/span&gt;
&lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;hello_function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hello_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Hello world!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;Hello world!&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;Hello world!\n&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;Hello world!&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;# or how about a dictionary?&lt;/span&gt;
&lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;function&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hello_function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;file_object&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hello_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;string&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Hello world!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;function&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;Hello world!&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;file_object&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;Hello world!\n&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;string&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;Hello world!&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;# print types&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c"&gt;# prints&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;file_object&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c"&gt;# prints&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c"&gt;# prints  (str for string)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;em&gt;shelve&lt;/em&gt; and &lt;em&gt;pickle&lt;/em&gt; modules are also work a look if you like what
you've just seen here.&lt;/p&gt;
&lt;blockquote&gt;
Reason #4: The &amp;quot;in&amp;quot; operator simplifies sequences and makes
membership testing a breeze&lt;/blockquote&gt;
&lt;p&gt;To explain the power of &amp;quot;in&amp;quot;, it's probably easier to just show you a
few examples:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="s"&gt;&amp;#39;p&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;spam&amp;quot;&lt;/span&gt; &lt;span class="c"&gt;# == True&lt;/span&gt;
&lt;span class="s"&gt;&amp;#39;item1&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;item1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;item2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# == True&lt;/span&gt;
&lt;span class="s"&gt;&amp;#39;key&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;key&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# == True&lt;/span&gt;

&lt;span class="c"&gt;# prints anchor tags for a navigation&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Home&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;index.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;About&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;about.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;Contact&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;contact.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;!--  --&gt;
&lt;blockquote&gt;
Reason #5: Slicing, stepping, and striding are a walk in the park&lt;/blockquote&gt;
&lt;p&gt;Probably my most-hated function in PHP is the substr() function. It
should just be easier to access a parts of a string. Thankfully, in
Python it's a piece of cake:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#string slices and stepping/striding&lt;/span&gt;
&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;This is a string&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;s&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;is a&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[::&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# prints &amp;#39;Ti sasrn&amp;#39; (a stride of 2 means print every other item)&lt;/span&gt;

&lt;span class="c"&gt;# works on lists and dictionaries as well&lt;/span&gt;
&lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;This&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;is&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;list&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[::&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# prints [&amp;#39;This&amp;#39;, &amp;#39;a&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;!--  --&gt;
&lt;blockquote&gt;
Reason #6-XX: Python makes programming fun again&lt;/blockquote&gt;
&lt;p&gt;As you can see, Python is a flexible, powerful, and even &lt;em&gt;fun&lt;/em&gt; programming
language. For more Python concepts (I didn't have time here to go into list
comprehensions, generators, decorators, etc), I'd suggest you take a gander at
Wikipedia's page on &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Python_syntax_and_semantics"&gt;Python syntax and
semantics&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, I'll leave you with a few relevant links that are worth taking
the time to check out:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Bruce Eckel's slides: &lt;a class="reference external" href="http://www.mindviewinc.com/downloads/pub/eckel/LovePython.zip"&gt;Why I Love
Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://docs.python.org/tutorial/"&gt;Python's official tutorial&lt;/a&gt; (by
Guido—Python's creator)&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://diveintopython.org/"&gt;Dive Into Python&lt;/a&gt; - Free online book&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://xkcd.com/353/"&gt;xkcd's famous Python comic&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Seth Davis</dc:creator><pubDate>Mon, 27 Jul 2009 00:05:00 -0700</pubDate><guid>tag:blog.curiasolutions.com,2009-07-27:why-ive-fallen-in-love-with-python.html</guid><category>Python</category></item></channel></rss>