Yesterday I learnt an interesting aspect of python called closures.
def a(aa1, aa2=10):
def b(bb1, bb2=aa2):
print aa1, aa2, bb1, bb2
aa1+=100
aa2+=100
return b
>>> a(20)(5)
What is the output?
The answer by intuition would have been 20 10 5 10. The answer however is 120 110 5 10.
I was amazed by this result and wanted to figure out how this works. I ran pdb on this function using
$ python -m pdb test_fn.py
The result of it was:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | > /home/theju/test_fn.py(1)<module>()
-> def a(aa1, aa2=10):
(Pdb) s
> /home/theju/test_fn.py(8)<module>()
-> a(20)(5)
(Pdb)
--Call--
> /home/theju/test_fn.py(1)a()
-> def a(aa1, aa2=10):
(Pdb)
> /home/theju/test_fn.py(2)a()
-> def b(bb1, bb2=aa2):
(Pdb) dir()
['aa1','aa2']
(Pdb) s
> /home/theju/test_fn.py(4)a()
-> aa1+=100
(Pdb) dir()
['aa1', 'aa2', 'b']
(Pdb) dir(b)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__',
'__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure',
'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
(Pdb) p b.func_defaults
(10,)
(Pdb) s
> /home/theju/test_fn.py(5)a()
-> aa2+=100
(Pdb)
> /home/theju/test_fn.py(6)a()
-> return b
(Pdb)
--Return--
> /home/theju/test_fn.py(6)a()-><functio...b7c6adbc>
-> return b
(Pdb)
--Call--
> /home/theju/test_fn.py(2)b()
-> def b(bb1, bb2=aa2):
(Pdb)
> /home/theju/test_fn.py(3)b()
-> print aa1, aa2, bb1, bb2
(Pdb)
120 110 5 10
--Return--
> /home/theju/test_fn.py(3)b()->None
-> print aa1, aa2, bb1, bb2
(Pdb)
--Return--
> /home/theju/test_fn.py(8)<module>()->None
-> a(20)(5)
(Pdb)
--Return--
> <string>(1)<module>()->None
(Pdb)
|
The default parameter values are evaluated when the function definition is executed. See this for more details.
Thanks to Gopi for helping me learn this concept and more.
Today I launched my second project online after django-check-constraints, safebrowsing-python. The project is based on the Google Safebrowsing API. Check out the announcement on the google-safebrowsing-api mailing list and also the usage wiki page at the project home.
Wish you a happy and safe browsing on the Web.
On thursday, my previous semester results were out. I logged on to the site and checked out my result. My result was anything but surprising. Then I thought to myself, "Why not give people the freedom to compare their marks with their peers'".
This idea excited me and I wrote a python script that used BeautifulSoup to soup everyone's marks and then create an HTML table that would use YUI for the ajaxy effect.
Thanks to Python's ultra simplicity and sheer power, my script was done in one and half hour after the results were out.
Check out the results at http://puthraya.webfactional.com/results.html and http://puthraya.webfactional.com/consolidated_results.html
This is the final part in the "I blog in ReSt" series. This part discusses about how to get the templates going for the blog.
{% load custom_filters %}
{% load comments %}
{% load markup %}
{% load tagging_tags %}
{% block title %}Title of the Blog{% endblock %}
{% block content %}
{% for blog in blog_object_list %}
{{ blog.blog_title }}
{{ blog.blog_file|open_file|restructuredtext }}
Published on {{ blog.blog_date_pub|date:"F j, Y" }}
{% get_free_comment_list for blog_app.blog_model blog.id as comment_list %}
{% get_free_comment_count for blog_app.blog_model blog.id as comment_count %}
{% if comment_count %}
{% for comment in comment_list %}
<div class="comment_{% cycle odd,even %}" id="c{{ comment.id }}">
<span class="comnum"><a id="c{{ comment.id }}"
href="#c{{ comment.id }}">#{{ forloop.counter }}</a></span>
<p><strong>{{ comment.person_name|escape }}</strong> commented,
on {{ comment.submit_date|date:"F j, Y" }} at {{ comment.submit_date|date:"P" }}:</p>
{{ comment.comment|restructuredtext }}
</div>
{% endfor %}
{% endif %}
<p>Post a comment</p>
{% free_comment_form for blog_app.blog_model blog.id %}
{% endfor %}
Viewing page <strong>{{ page }} / {{ pages }}</strong>. Total number of posts are {{ hits }}.
{% if has_previous %}
{% if year %}
{% if month %}
<p><a href="/blog/{{ year }}/{{ month }}/page/{{ previous }}/">Newer Posts</a></p>
{% else %}
<p><a href="/blog/{{ year }}/page/{{ previous }}/">Newer Posts</a></p>
{% endif %}
{% else %}
{% if tag %}
<p><a href="/blog/tag/{{ tag }}/page/{{ previous }}/">Newer Posts</a></p>
{% else %}
<p><a href="/blog/page/{{ previous }}/">Newer Posts</a></p>
{% endif %}
{% endif %}
{% endif %}
{% if has_next %}
{% if year %}
{% if month %}
<p><a href="/blog/{{ year }}/{{ month }}/page/{{ next }}/">Older Posts</a></p>
{% else %}
<p><a href="/blog/{{ year }}/page/{{ next }}/">Older Posts</a></p>
{% endif %}
{% else %}
{% if tag %}
<p><a href="/blog/tag/{{ tag }}/page/{{ next }}/">Older Posts</a></p>
{% else %}
<p><a href="/blog/page/{{ next }}/">Older Posts</a></p>
{% endif %}
{% endif %}
{% endif %}
{% endblock %}
You might be wondering what {% load custom_filters %} is. As the name suggests it is a custom filter to read the contents of the file and return them to the restructuredtext filter.
from django import template
register = template.Library()
def open_file(value):
"Opens the contents of the file given in value."
try:
f = open(value,"r")
contents = f.read()
except IOError:
return None
f.close()
return contents
# Register the Filter
register.filter(open_file)
The restructuredtext is a filter from django.contrib.markup. The django.contrib.markup currently supports three markup languages Textile, Markdown and ReSt.
Django has an in-built comment system that can be activated almost instantly. For activating comments add the django.contrib.comments to your INSTALLED_APPS in settings.py. There are two types of Comments Systems:
The best part about Django's Comment System is that it is extremely easy to use but its biggest drawback is that it hasn't been documented yet. This is no lame excuse for not using Django Comments. In fact lots of users have blogged on how to use the Comment System. Feel free to search and you'll get hold of them. The one link that explains the comment system in detail would be this.
The rest of the code is regarding pagination. Refer here for more details regarding it.
Now you have a blog written in Django that has all the basic features that Blogger has. Also the blog is highly extensible. Extend the blog and let me know of some cool features that you built into your blog.
Once done with setting up the project, we need to code our views and templates to complete the blog. This part deals with coding the views for the blog. The different views that we require are
So let's edit the urls.py file in the project directory to reflect on the urlpatterns.
('^blog/$', get_blog_first_page),
('^blog/(?P<year>\d{4})/$', get_posts_by_year),
('^blog/(?P<year>\d{4})/page/(?P<page>\d)/$', get_blog_page_by_year),
('^blog/(?P<year>\d{4})/(?P<month>\d{2})/$', get_posts_by_month),
('^blog/(?P<year>\d{4})/(?P<month>\d{2})/page/(?P<page>\d)/$', get_blog_page_by_month),
('^blog/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', get_posts_by_day),
('^blog/tag/(?P<tag>[^/]+(?u))/$',tagged_object_list,dict(model=Blog_Model, paginate_by=1, \
allow_empty=True,template_object_name='blog_object', template_name='blog.html',\
extra_context={"tag":1,})),
('^blog/tag/(?P<tag>[^/]+(?u))/page/(?P<page>[0-9]+)/$',tagged_object_list,dict(model=Blog_Meta, \
paginate_by=1, allow_empty=True, template_object_name='blog_object', template_name='blog.html',)),
By doing so we will be accepting all urls like
/blog/ # the first page with latest items
/blog/2007/ # the first page of all blog posts from 2007
/blog/2007/page/4/ # the fourth page of blog posts from 2007
/blog/2007/10/ # blog posts from October 2007
/blog/2007/09/page/2/ # the second page from November 2007
/blog/2007/11/15/ # the post on 15 November 2007
/blog/tag/django/ # Posts with tag django
/blog/tag/django/page/7/ # Seventh page with tag django
Django provides a date-based generic view that saves us from the writing views for date filtering etc. But date-based generic views do not provide pagination by default. But django is powerful and highly extensible. It provides an Object Paginator which can be used for paginations. So I will use a list-based generic view to mimic the date-based generic views with pagination. Open the views.py file in blog_app and paste the following code.
from django.core.paginator import ObjectPaginator
from blog_app.models import Blog_Model
from django.http import HttpResponse, Http404
from django.template import loader, Context
from django.views.generic.date_based import archive_day
from django.views.generic.list_detail import object_list
import tagging
# Number of Entries per page (paginate_by)
num_entries_per_page = 7
def get_blog_page(request,page):
queryset = Blog_Model.objects.order_by("-blog_date_pub")
paginator = ObjectPaginator(queryset, num_entries_per_page)
cloud_list = tagging.models.Tag.objects.cloud_for_model(Blog_Model,steps=2)
paginator.pages
if int(page) in paginator.page_range:
return object_list(request,queryset,num_entries_per_page,page,template_name='blog.html',\
template_object_name='blog_object',extra_context={'cloud_list': cloud_list,})
else:
raise Http404
def get_blog_first_page(request):
return get_blog_page(request,1)
def get_blog_page_by_year(request,year,page):
queryset = Blog_Model.objects.filter(**{"blog_date_pub__year":year}).order_by("-blog_date_pub")
try:
assert queryset
except AssertionError:
raise Http404
paginator = ObjectPaginator(queryset, num_entries_per_page)
paginator.pages
cloud_list = tagging.models.Tag.objects.cloud_for_model(Blog_Model,steps=2)
if int(page) in paginator.page_range:
return object_list(request,queryset,num_entries_per_page,page,template_name='blog.html',\
template_object_name='blog_object',extra_context={"year":year,'cloud_list': cloud_list,})
else:
raise Http404
def get_posts_by_year(request, year):
return get_blog_page_by_year(request,year,1)
def get_posts_by_day(request,year,month,day):
month_dict = {1:"Jan",2:"Feb",3:"Mar",4:"Apr",5:"May",6:"Jun",7:"Jul",8:"Aug",\
9:"Sep",10:"Oct",11:"Nov",12:"Dec"}
try:
month_name = month_dict[int(month)]
except ValueError:
raise Http404
cloud_list = tagging.models.Tag.objects.cloud_for_model(Blog_Model,steps=2)
return archive_day(request,year,month_name,day,Blog_Model.objects.all(),'blog_date_pub',\
template_name='blog.html',template_object_name='blog_object',\
extra_context={'cloud_list': cloud_list,})
I have not given the complete view. You can check it out here. The next part in this series talks about templates.