<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Outsourcing stories &#187; python</title>
	<atom:link href="http://alexlebedev.com/blog/category/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://alexlebedev.com/blog</link>
	<description>Alexander Lebedev writes about software development and outsourcing</description>
	<lastBuildDate>Tue, 28 Dec 2010 13:15:13 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Python переходит на Mercurial</title>
		<link>http://alexlebedev.com/blog/python-switches-to-mercruial/</link>
		<comments>http://alexlebedev.com/blog/python-switches-to-mercruial/#comments</comments>
		<pubDate>Tue, 31 Mar 2009 04:15:27 +0000</pubDate>
		<dc:creator>Alex Lebedev</dc:creator>
				<category><![CDATA[mercurial]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[новости]]></category>

		<guid isPermaLink="false">http://alexlebedev.com/blog/python-switches-to-mercruial/</guid>
		<description><![CDATA[Гвидо ван Россум окончательно выбрал Mercurial в качестве системы контроля исходного кода для разработки Python.  Оригинал новости

Что ж, как я писал в комментариях про git, можно ожидать существенного расширения экосистемы mercurial за счет связанных с python проектов.  Скорее всего, эффект будет не меньший, чем от перехода Ruby on Rails на git.  Теперь [...]]]></description>
			<content:encoded><![CDATA[<p>Гвидо ван Россум окончательно выбрал Mercurial в качестве системы контроля исходного кода для разработки Python.  <a href="http://mail.python.org/pipermail/python-dev/2009-March/087931.html">Оригинал новости</a></p>

<p>Что ж, как я писал в комментариях про git, можно ожидать существенного расширения экосистемы mercurial за счет связанных с python проектов.  Скорее всего, эффект будет не меньший, чем от перехода Ruby on Rails на git.  Теперь надо внимательно следить за Django, SQL Alchemy и pygtk &#8212; многое зависит от того, на что перейдут они.</p>
]]></content:encoded>
			<wfw:commentRss>http://alexlebedev.com/blog/python-switches-to-mercruial/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Django и контроль доступа с человеческим лицом</title>
		<link>http://alexlebedev.com/blog/django-nonpublic-middleware/</link>
		<comments>http://alexlebedev.com/blog/django-nonpublic-middleware/#comments</comments>
		<pubDate>Thu, 12 Feb 2009 06:19:20 +0000</pubDate>
		<dc:creator>Alex Lebedev</dc:creator>
				<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://alexlebedev.com/blog/django-nonpublic-middleware/</guid>
		<description><![CDATA[Давайте поговорим о безопасности.

Существует два самых распространенных подхода к описанию ограничений доступа в информационных системах.  Первый &#8212; нельзя ничего, кроме функций, явно указанных как разрешенные.  Второй &#8212; можно все, кроме явно запрещенного.  Ни один из подходов не является однозначно лучшим, выбирать нужно в зависимости от особенностей конкретной системы.  Если оная состоит [...]]]></description>
			<content:encoded><![CDATA[<p>Давайте поговорим о безопасности.</p>

<p>Существует два самых распространенных подхода к описанию ограничений доступа в информационных системах.  Первый &#8212; нельзя ничего, кроме функций, явно указанных как разрешенные.  Второй &#8212; можно все, кроме явно запрещенного.  Ни один из подходов не является однозначно лучшим, выбирать нужно в зависимости от особенностей конкретной системы.  Если оная состоит преимущественно из публичной части, то удобнее описывать политику доступа через ограничения.  Если публичная часть мала, а основное взаимодействие происходит с зарегистрированными пользователями &#8212; описываем, наоборот, через набор разрешений.  Ну и еще имеет значение то, в какую сторону в данной системе страшнее ошибиться &#8212; что-то лишнее разрешить или запретить.</p>

<h2>Проблема</h2>

<p>Какое отношение все это имеет к Django?  Наша команда в настоящий момент разрабатывает систему, в которой почти весь функционал предназначен для зарегистрированных пользователей.</p>

<p>В текущем версии фрэймворк не особо помогает нам с описанием прав доступа (да и в 1.1 не обещают улучшений по этой части).  А именно &#8211; доступен только запретительный механизм описания, осуществляемый через навешивание на view-функции декоратора <code>login_required</code></p>

<p>Примерно так:</p>

<pre><code>from django.contrib.auth.decorator import login_required

@login_required # Доступ ограничен
def some_view(requests):
   # blah-blah-blah

# Доступ свободный
def other_view(requests):
   # blah-blah-blah
</code></pre>

<p>Во всей красе проявляются недостатки такого подхода &#8212; нужно вешать декоратор на 90% представлений.  Плюс к тому, всегда есть риск получить неочевидную дыру в безопасности системы, если забыть поставить декоратор.</p>

<h2>Решение</h2>

<p>В рамках стремления к лучшему и концепции &#8220;сделай сам&#8221; разработан способ описывать права через набор разрешений.</p>

<pre><code>from middleware.security import public

# Доступ ограничен, как если бы стоял login_required
def some_view(requests):
   # blah-blah-blah

@public # Доступ свободный
def other_view(requests):
   # blah-blah-blah
</code></pre>

<h3>Инструкция по установке</h3>

<ol>
<li><p>Скачиваем модуль <a href="http://alexlebedev.com/blog/wp-content/uploads/2009/02/security.pi">security.py</a> и кладем его куда-нибудь.  У нас это директория <code>middleware</code>, общая для всех приложений.</p></li>
<li><p>В настойках добавляем <code>NonpublicMiddleware</code></p>

<pre><code>MIDDLEWARE_CLASSES = (
    ...,
    'middleware.security.NonpublicMiddleware', 
)
</code></pre></li>
<li><p>По необходимости правим функцию <code>is_public</code>, чтобы изменить логику определения известных публичных view (сейчас это все, находящееся в пакете <code>django</code>, за исключением <code>django.views.generic</code>)</p></li>
<li><p>Все, можно пользоваться.</p></li>
</ol>

<h3>security.py</h3>

<iframe src="http://alexlebedev.com/blog/wp-content/uploads/2009/02/security.pi" style="width: 100%; height: 20em;"></iframe>
]]></content:encoded>
			<wfw:commentRss>http://alexlebedev.com/blog/django-nonpublic-middleware/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Требуются веб-разработчики в Тольятти, $2000/мес.</title>
		<link>http://alexlebedev.com/blog/looking-for-developers/</link>
		<comments>http://alexlebedev.com/blog/looking-for-developers/#comments</comments>
		<pubDate>Sun, 11 Jan 2009 15:50:37 +0000</pubDate>
		<dc:creator>Alex Lebedev</dc:creator>
				<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[объявления]]></category>
		<category><![CDATA[работа]]></category>

		<guid isPermaLink="false">http://alexlebedev.com/blog/looking-for-developers/</guid>
		<description><![CDATA[В связи с повышением спроса со стороны потенциальных заказчиков, мы объявляем набор веб-разработчиков.

Нас 3 человека, находимся в Тольятти, работаем в области аутсорсинга около 5 лет.  Клиенты из США, Канады, Европы.  Как фирма не оформлены, планируем сделать это в ближайшем будущем.  В данный момент нам нужно еще 2-3 разработчика.

Занимаемся разработкой веб-систем на заказ с использованием наиболее современных технологий.  На сегодняшний день -- Django и Ruby on Rails.]]></description>
			<content:encoded><![CDATA[<p><strong>Вакансии закрыты, спасибо всем откликнувшимся!</strong></p>

<hr />

<p>В связи с повышением спроса со стороны потенциальных заказчиков, мы объявляем набор веб-разработчиков.</p>

<p>Нас 3 человека, находимся в Тольятти, работаем в области аутсорсинга около 5 лет.  Клиенты из США, Канады, Европы.  Как фирма не оформлены, планируем сделать это в ближайшем будущем.  В данный момент нам нужно еще 2-3 разработчика.</p>

<p>Занимаемся разработкой веб-систем на заказ с использованием наиболее современных технологий.  На сегодняшний день &#8212; Django и Ruby on Rails.</p>

<h2>Основные требования</h2>

<ul>
<li><p>Общий опыт работы от 2-3 лет</p></li>
<li><p>Опыт веб-разработки, выражающийся в понимании HTTP, HTML, CSS, javascript</p></li>
<li><p>Владение хотя бы 2-мя высокоуровневыми языками программирования</p></li>
<li><p>Хороший письменный английский</p></li>
</ul>

<h2>Приветствуется</h2>

<ul>
<li><p>Python, Ruby</p></li>
<li><p>SQL</p></li>
<li><p>Django, Ruby on Rails</p></li>
<li><p>Опыт работы на *nix-системах</p></li>
<li><p>Опыт написания юнит-тестов, test-driven development</p></li>
<li><p>Опыт использования систем управления версиями и багтреккинга (мы используем Mercurial и Trac соответственно).</p></li>
</ul>

<h2>Условия</h2>

<p>Готовы платить 2000 долларов в месяц подходящему нам человеку, который сможет адекватно встроиться в коллектив, и давать результат на уровне среднего по команде.  Тем, кто знает и умеет меньше, чем мы, первое время будем платить меньше.  Поднимать по мере обучения тоже будем, целевой уровень все те же 2000.  Нижняя граница стартовой суммы находится где-то в районе 1400, меньше подходящий нам кандидат, вероятно, не стоит.  Валюта оплаты зависит от конкретного проекта, обычно это доллары США.</p>

<p>Работа дома.  Организуете свое рабочее место своими силами и за свой счет.</p>

<p>График свободный в той степени, в которой он не мешает координации работ с коллегами и присутствию на проходищих несколько раз в неделю онлайн-обсуждениях.  Допускается работа с меньшей загрузкой в течение испытательного срока.</p>

<p>Тем не менее, мы ожидаем работы на полную ставку, 40 часов в неделю.  Формального учета рабочего времени нет, контроллируем по результатам.</p>

<p><strong>Дополнение</strong></p>

<p>Нужно будет 1-2 раза в неделю встречаться лично, поэтому совсем удаленная работа нам не подойдет, нужно чтобы вы находились в Тольятти или окрестностях.</p>

<hr />

<p>В качестве первого шага присылайте ваше резюме или портфолио.</p>

<p>Со мной можно связаться по почте (me [собака] alexlebedev.com) или ICQ (160365425).</p>

<p>&#8211; Александр Лебедев</p>
]]></content:encoded>
			<wfw:commentRss>http://alexlebedev.com/blog/looking-for-developers/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Идиомы и анти-идиомы языка Python</title>
		<link>http://alexlebedev.com/blog/python-idioms-and-anti-idioms/</link>
		<comments>http://alexlebedev.com/blog/python-idioms-and-anti-idioms/#comments</comments>
		<pubDate>Thu, 25 Dec 2008 11:40:50 +0000</pubDate>
		<dc:creator>Alex Lebedev</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[программирование]]></category>
		<category><![CDATA[рефакторинг]]></category>

		<guid isPermaLink="false">http://alexlebedev.com/blog/python-idioms-and-anti-idioms/</guid>
		<description><![CDATA[Недавно наткнулся на полезную, но немного устаревшую статью
<a href="http://docs.python.org/howto/doanddont.html">«Python Idioms and anti-idioms»</a>

Давайте разберем ее применительно к сегодняшней реальности.]]></description>
			<content:encoded><![CDATA[<p>Недавно наткнулся на полезную, но немного устаревшую статью
<a href="http://docs.python.org/howto/doanddont.html">«Python Idioms and anti-idioms»</a></p>

<p>Давайте разберем ее применительно к сегодняшней реальности.</p>

<h2>from module import *</h2>

<p>Цитата:</p>

<blockquote>
  <p><code>from module import *</code> is <em>invalid</em> inside function definitions.</p>
  
  <p>&#8230;</p>
  
  <p>While it is valid to use <code>from module import *</code> at module level it is usually a bad idea. For one, this loses an important property Python otherwise has — you can know where each toplevel name is defined by a simple “search” function in your favorite editor. You also open yourself to trouble in the future, if some module grows additional functions or classes.</p>
  
  <p>&#8230;</p>
  
  <p>Remember, you can never know for sure what names a module exports, so either take what you need — <code>from module import name1, name2</code>, or keep them in the module and access on a per-need basis — <code>import module; print module.name</code>.</p>
</blockquote>

<p>[<a href="http://docs.python.org/howto/doanddont.html#from-module-import">оригинал</a>]</p>

<p>Почти согласен.</p>

<ol>
<li><p>Возможность четко найти в текущем файле откуда взято любое имя действительно ценна.</p></li>
<li><p>Использование <code>import *</code> рано или поздно приводит к каскадному распространению имен, что значительно затрудняет отладку и снижает логическую изолированность частей программы.</p></li>
</ol>

<h2>Unadorned exec, execfile() and friends</h2>

<p>Цитата:</p>

<blockquote>
  <p>The word “unadorned” refers to the use without an explicit dictionary, in which case those constructs evaluate code in the current environment. This is dangerous for the same reasons from import * is dangerous — it might step over variables you are counting on and mess up things for the rest of your code. Simply do not do that.</p>
</blockquote>

<p>[<a href="http://docs.python.org/howto/doanddont.html#unadorned-exec-execfile-and-friends">оригинал</a>]</p>

<p>Полностью согласен.</p>

<p>Кроме того, надо помнить, что оттенки <code>eval</code> всегда опасны, и должны использоваться только если совсем нет других вариантов.  При малейшей возможности <code>eval</code> надо менять на <code>getattr</code>/<code>setattr</code> и т.п.</p>

<h2>from module import name1, name2</h2>

<p>Цитата:</p>

<blockquote>
  <p>This is a “don’t” which is much weaker then the previous “don’t”s but is still something you should not do if you don’t have good reasons to do that. The reason it is usually bad idea is because you suddenly have an object which lives in two separate namespaces. When the binding in one namespace changes, the binding in the other will not, so there will be a discrepancy between them. This happens when, for example, one module is reloaded, or changes the definition of a function at runtime.</p>
</blockquote>

<p>[<a href="http://docs.python.org/howto/doanddont.html#from-module-import-name1-name2">оригинал</a>]</p>

<p>Да, такая опасность есть.  Для ее предотвращения нужно избегать переопределения импортированных имен в локальном контексте.  Думаю, что это правило достаточно просто запомнить.  Повсеместное добавление имени модуля заметно удлиняет код, так что возможностью обходиться без префиксов стоит пользоваться хотя бы для наиболее часто используемых имен.</p>

<h2>except:</h2>

<p>Цитата:</p>

<blockquote>
  <p>Python has the <code>except:</code> clause, which catches all exceptions. Since every error in Python raises an exception, this makes many programming errors look like runtime problems, and hinders the debugging process.</p>
</blockquote>

<p>[<a href="http://docs.python.org/howto/doanddont.html#except">оригинал</a>]</p>

<p>Полностью согласен, ловля всех исключений без разбора неприемлема.  Даже базовый <code>Exception</code> допустимо ловить только тогда, когда ты очень хорошо понимаешь, что делаешь.  В подавляющем большинстве случаев задача решается отловом одного-двух более конкретных типов исключений.</p>

<h2>Exceptions</h2>

<p>Цитата:</p>

<blockquote>
  <p>Exceptions are a useful feature of Python. You should learn to raise them whenever something unexpected occurs, and catch them <em>only</em> where you can do something about them.</p>
  
  <p>&#8230;</p>
  
  <p>So, try to make as few except clauses in your code — those will usually be a catch-all in the main(), or inside calls which should always succeed.</p>
</blockquote>

<p>[<a href="http://docs.python.org/howto/doanddont.html#exceptions">оригинал</a>]</p>

<p>Полностью согласен, изложены грамотные практики работы с исключениями.</p>

<p>Единственное замечание к этой главе в том, что показана устаревшая практика утилизации ресурсов:</p>

<pre><code>def get_status(file):
    fp = open(file)
    try:
        return fp.readline()
    finally:
        fp.close()
</code></pre>

<p>Современный подход предполагает использование <code>with</code>:</p>

<pre><code>def get_status(file):
    with open(file) as fp:
        return fp.readline()
</code></pre>

<h2>Using the Batteries</h2>

<p>Цитата:</p>

<blockquote>
  <p>Every so often, people seem to be writing stuff in the Python library again, usually poorly. While the occasional module has a poor interface, it is usually much better to use the rich standard library and data types that come with Python then inventing your own.</p>
</blockquote>

<p>[<a href="http://docs.python.org/howto/doanddont.html#using-the-batteries">оригинал</a>]</p>

<p>Да, изобретение велосипеда никогда не было хорошей идеей.</p>

<p>Между тем, все приведенные в оригинальной статье примеры не очень удачны:</p>

<ol>
<li><p>Формирование путей через сложение строк вполне допустимо, если всегда использовать прямой слэш, который вполне нормально работает и в Windows.</p></li>
<li><p>Использование <code>reduce</code> сейчас считается плохой практикой, да и стандартная функция <code>sum</code> давно существует.</p></li>
</ol>

<h2>Using Backslash to Continue Statements</h2>

<p>Цитата:</p>

<blockquote>
  <p>Since Python treats a newline as a statement terminator, and since statements are often more then is comfortable to put in one line, many people do:</p>

<pre><code>if foo.bar()['first'][0] == baz.quux(1, 2)[5:9] and \
    calculate_number(10, 20) != forbulate(500, 360):
        pass
</code></pre>
  
  <p>You should realize that this is dangerous: a stray space after the <code>\</code> would make this line wrong, and stray spaces are notoriously hard to see in editors.</p>
  
  <p>&#8230;</p>
  
  <p>It is usually much better to use the implicit continuation inside parenthesis</p>
</blockquote>

<p>[<a href="http://docs.python.org/howto/doanddont.html#using-backslash-to-continue-statements">оригинал</a>]</p>

<p>Полностью согласен.</p>

<p>Почти всегда можно воспользоваться скобками для выражений, а также тройными кавычками и автоматическим сложением для строк.  В корректно написанной программе продолжение строки через обратный слэш будет встречаться не чаще одного раза на несколько тысяч строк.</p>

<p><strong>дополнение</strong></p>

<p>Вот несколько примеров переноса строк без обратного слэша:</p>

<ol>
<li><p>Длинный <code>if</code></p>

<pre><code>if request.host in self.lastRequestTime and \
    (time.time() - self.lastRequestTime[request.host] &lt; self.throttleDelay):
</code></pre>

<p>Меняем на:</p>

<pre><code>if (request.host in self.lastRequestTime and
    (time.time() - self.lastRequestTime[request.host] &lt; self.throttleDelay)):
</code></pre></li>
<li><p>Используем уже имеющиеся скобки</p>

<pre><code>if internal_id in [str(ch.settings_dict.get('id')) \
    for ch in self.campaign_obj.channel_set.filter(type=channel.type)]:
    raise forms.ValidationError(['You have already subscribed for this %s' % channel.type_name]) 
</code></pre>

<p>Меняем на:</p>

<pre><code>if internal_id in [
    str(ch.settings_dict.get('id'))
    for ch in self.campaign_obj.channel_set.filter(type=channel.type)
    ]:
    raise forms.ValidationError(['You have already subscribed for this %s' % channel.type_name])
</code></pre></li>
<li><p>Автоматическре склеиваение строковых переменных</p>

<pre><code>raw_input("== External authentication\n" +
          "Please open this URI in browser:\n%s\n" +
          "When page finishes loading press any key to continue" % api_client.get_login_uri())
</code></pre>

<p>Меняем на:</p>

<pre><code>raw_input("== External authentication\n"
          "Please open this URI in browser:\n%s\n"
          "When page finishes loading press any key to continue" % api_client.get_login_uri())
</code></pre></li>
</ol>

<hr />

<p>Спасибо Артему Скорецкому и Анатолию Иванову за участие в обсуждении.</p>
]]></content:encoded>
			<wfw:commentRss>http://alexlebedev.com/blog/python-idioms-and-anti-idioms/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Эффективное программирование: текстовое представление переменных в python</title>
		<link>http://alexlebedev.com/blog/effective-programming-python-variable-inspection/</link>
		<comments>http://alexlebedev.com/blog/effective-programming-python-variable-inspection/#comments</comments>
		<pubDate>Sun, 12 Aug 2007 11:25:54 +0000</pubDate>
		<dc:creator>Alex Lebedev</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[программирование]]></category>
		<category><![CDATA[эффективное программирование]]></category>

		<guid isPermaLink="false">http://alexlebedev.com/blog/effective-programming-python-variable-inspection/</guid>
		<description><![CDATA[Предположим, что вы хотите повысить свою эффективность как программиста.  Или, что более интересно, повысить эффективность разработчиков в своей команде.  Какое занятие отнимает больше всего времени после непроизводительной активности в составе: кофе, перекуры, <a href="http://bash.org.ru">bash.org</a>?  Наверняка это отладка.  Отладка обычно занимает вдвое-втрое больше времени, чем написание первой (не работающей) версии кода.

Сегодня я напишу о способе, который позволит сделать отладку более приятной и эффективной.

[...]]]></description>
			<content:encoded><![CDATA[<p>Я открываю новый цикл статей, &#8220;Эффективное программирование&#8221;.</p>

<p>Давно уже стало банальностью утверждение о том, что программисты могут отличаться в производительности в десять и более раз.  Среди важнейших причин, наряду с талантом и правильными условиями работы (сюда входит все от процессов разработки и тестирования до зарплаты, атмосферы в коллективе и удобства кресел), фигурирует и использование правильных практик программирования.  Кент Бек (Kent Beck), создатель методологии Extreme Programming, говорил о себе &#8220;Я не считаю себя замечательным программистом, я лишь неплохой программист с замечательными привычками&#8221;.  В этой и последующих статьях цикла будут рассмотрены некоторые практики, входящие в мой собственный  набор &#8220;замечательных привычек&#8221;, а также реально полезные инструменты и библиотеки.</p>

<p>В качестве основы взяты практические эпизоды наших текущих проектов.  Материал нового цикла будет в первую очередь полезен для разработчиков и, в особенности, для тех-лидов и руководителей, осуществляющих &#8220;тренерскую&#8221; работу.</p>

<hr />

<p>Предположим, что вы хотите повысить свою эффективность как программиста.  Или, что более интересно, повысить эффективность разработчиков в своей команде.  Какое занятие отнимает больше всего времени после непроизводительной активности в составе: кофе, перекуры, <a href="http://bash.org.ru">bash.org</a>?  Наверняка это отладка.  Отладка обычно занимает вдвое-втрое больше времени, чем написание первой (не работающей) версии кода.</p>

<p>Сегодня я напишу о способе, который позволит сделать отладку более приятной и эффективной.</p>

<p>Все, за исключением использования графических дебаггеров, способы отладки базируются на текстовом выводе значения переменных.  Логи, Exception&#8217;ы, консоль &#8212; все это требует представления данных программы в текстовом формате.  Каждый раз, когда это представление выбрано неудачно, вам придется менять его и проходить еще одну итерацию отладки.  Некоторые ошибки будет сложно или совсем невозможно воспроизвести.  Следовательно, очень желательно выбрать правильное представление с первого раза.</p>

<p>Сразу предупрежу, что стандартная реализация далеко не идеальна.</p>

<p>Что произойдет при попытке напечатать значение объекта в python?</p>

<pre><code>&gt;&gt;&gt; print 7
7
&gt;&gt;&gt; print range(5)
[0, 1, 2, 3, 4]
</code></pre>

<p>Пока все хорошо.  Попробуем отойти от базовых типов данных:</p>

<pre><code>class Run(object):
    pass

&gt;&gt;&gt; r = Run()
&gt;&gt;&gt; r.status = 'new'
&gt;&gt;&gt; print r
&lt;__main__.Run object at 0xd3f944c&gt;
&gt;&gt;&gt; print 'Shit happens...'
'Shit happens...'
</code></pre>

<p>Отвратительно.  Придется обращаться отдельно к каждому атрибуту, что не очень удобно.</p>

<p>Надо сказать, что в Ruby вывод подробного состояния объекта реализован очень правильно:</p>

<pre><code>&gt;&gt; Run.find :first
=&gt; #&lt;Run:0xb725f884 @attributes={"status"=&gt;"done",
"finished_at"=&gt;"1900-01-01 00:00:00", "id"=&gt;"1",
"started_at"=&gt;"1900-01-01 00:00:00"}&gt;
</code></pre>

<p>Python здесь явно проигрывает.  Впрочем, как я покажу дальше, не все так плохо.</p>

<p>Дело в том, что поведение объекта при вызове <code>print</code> можно настраивать.  Для этого служат два специальных метода:  <code>__str__</code> и <code>__repr__</code> &#8212; первый для описания объекта в свободной форме, второй &#8212; для максимально строго и полного описания соответственно (желательно, чтобы результат <code>__repr__</code> являлся валидным выражением на языке python, описывающем текущий объект).  Для отладки актуален, преимущественно, метод <code>__repr__</code>.</p>

<p>Классическое исполнение выглядит следующим образом:</p>

<pre><code>class Run(object):
    def __init__(self, status, started_at):
        self.status, self.started_at = status, started_at
    def get_name(self):
        return self.user_name
    def __repr__(self):
        return "Run: status=%s started_at=%s" % (
            self.status, self.started_at)

    &gt;&gt;&gt; from datetime import datetime
    &gt;&gt;&gt; r = Run('new', datetime.today())
    &gt;&gt;&gt; r
    Run: status=new started_at=2007-08-12 03:16:26.468553
</code></pre>

<p>В этом коде нарушается принцип DRY (don&#8217;t repeat yourself): при добавлении в класс нового атрибута вам придется менять код текстового представления объекта.</p>

<p>В один прекрасный день обнаруживаешь, что у тебя накопилось с десяток классов с 5-15 значимыми атрибутами каждый, а редактирование методов <code>__repr__</code> достало до печенок.  Как быстро выяснилось, есть способ реализовать <code>__repr__</code> раз и навсегда.  Для этого достаточно немного покопаться во внутренней механике объектов, а именно &#8212; использовать свойство <code>__dict__</code>, дающее доступ ко всем атрибутам данного объекта в виде dictionary формата &#8220;имя: значение&#8221;.  Такая реализация даже более компактна, чем явная печать каждого атрибута:</p>

<pre><code>class Run(object):    
    # ...
    def __repr__(self):
        return 'Run: %s' % dict([
            (k,v) for k,v in self.__dict__.items() if not k.startswith('_')])

&gt;&gt;&gt; session = create_session()
&gt;&gt;&gt; Run.get_latest(session)
Run: {'status': 'done', 'finished_at': datetime.datetime(2007, 8, 8, 20, 26, 48),
'resume_after': None, 'started_at': datetime.datetime(2007, 8, 8, 20, 26, 42),
'id': 4L, 'errors_in_a_row': 0}
</code></pre>

<p><strong>Примечание: здесь и далее в примерах работа с БД организована с помощью библиотеки <a href="http://sqlalchemy.org">SQLAlchemy</a>.  Это, пожалуй, лучшее что есть сейчас в python для работы с базами данных.</strong></p>

<p>Обратите внимание на выражение <code>dict([(k,v) for k,v in self.__dict__ if not k.startswith('_')])</code>.  В pyhton принято начинать имена private-атрибутов с одного подчеркивания (&#8221;_&#8221;), следовательно, мы отфильтровываем все private-атрибуты и не показываем их.  По опыту могу сказать, что это очень удобно при работе с <strong>SQLAlchemy</strong>, которая добавляет к вашим объектам кучу служебных свойств.</p>

<p>Уже гораздо лучше: написанный один раз код можно просто копировать в каждый новый класс.  Для тех, кто работает под девизом &#8220;зачем объектное программирование, когда есть блочное копирование&#8221; этого вполне достаточно.  Мы же пойдем чуть дальше и создадим действительно универсальную реализацию:</p>

<pre><code>class Inspectable(object):
    """Provide an ability to print class instance and its attributes"""
    def __repr__(self):
        return '&lt;%s: %s&gt;' % (
            self.__class__.__name__,
            dict([(x,y) for (x,y) in self.__dict__.items() if not x.startswith('_')])
            )

class Run(Inspectable, object):
    #...

&gt;&gt;&gt; session = create_session()
&gt;&gt;&gt; Run.get_latest(session)
&lt;Run: {'status': 'done', 'finished_at': datetime.datetime(2007, 8, 8, 20, 26, 48),
'resume_after': None, 'started_at': datetime.datetime(2007, 8, 8, 20, 26, 42),
'id': 4L, 'errors_in_a_row': 0}&gt;
</code></pre>

<p>Обратите внимание на порядок наследование в <code>Run</code>: <code>Inspectable, object</code>.  В python наследование от списка классов резолвится слева направо, поэтому первыми должны быть указаны классы, располагающиеся ниже в иерархии наследования.  В противном случае получите ошибку наследования:</p>

<pre><code>&gt;&gt;&gt; class Run(object, Inspectable):
...   pass
... 
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution
order (MRO) for bases object, Inspectable
</code></pre>

<p>Кстати, явно наследовать от <code>object</code> совсем необязательно &#8212; ведь <code>Inspectable</code> тоже является потомком <code>object</code>.  Я, однако, предпочитаю указывать object если перед этим наследую только от абстрактных классов &#8212; по идеологии это близко концепции mixin&#8217;ов в Ruby.</p>
]]></content:encoded>
			<wfw:commentRss>http://alexlebedev.com/blog/effective-programming-python-variable-inspection/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>О вреде изучения высокоуровневых языков</title>
		<link>http://alexlebedev.com/blog/do-not-learn-high-level-languages/</link>
		<comments>http://alexlebedev.com/blog/do-not-learn-high-level-languages/#comments</comments>
		<pubDate>Thu, 22 Mar 2007 00:46:01 +0000</pubDate>
		<dc:creator>Alex Lebedev</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[инструменты]]></category>
		<category><![CDATA[менеджмент]]></category>
		<category><![CDATA[программирование]]></category>

		<guid isPermaLink="false">http://alexlebedev.com/blog/do-not-learn-high-level-languages/</guid>
		<description><![CDATA[Где-то с месяц назад наткнулся на пост <a href="http://lukeplant.me.uk/blog.php?id=1107301645">Why learning Haskell/Python makes you a worse programmer</a>.  Люк поднимает крайне актуальную для многих из нас проблему -- изучение продвинутых языков не делает вас лучшим программистом на том, что приходится использовать, а, напротив, деморализует и снижает качество работы.]]></description>
			<content:encoded><![CDATA[<p>Где-то с месяц назад наткнулся на пост <a href="http://lukeplant.me.uk/blog.php?id=1107301645">Why learning Haskell/Python makes you a worse programmer</a>.  Люк Плант (Luke Plant) поднимает крайне актуальную для многих из нас проблему &#8212; изучение продвинутых языков не делает вас лучшим программистом на том, что приходится использовать, а, напротив, деморализует и снижает качество работы.</p>

<h2>Точка зрения сотрудника</h2>

<p>Очень рекомендую прочитать как статью, так и комментарии.  Если кратко, приводятся два основных аргумента:</p>

<ol>
<li><p>После того, как ты понял и полюбил Python, писать на C# неприятно.  Код громоздок и некрасив.  Процедурный подход вызывает кучу потенциальных глюков.  Кроме того, начав думать на Python, приходится работать &#8220;живым компилятором&#8221;, переводя эти мысли в C#.</p></li>
<li><p>Начав использовать функциональное программирование, пытаешься использовать его везде где только можно.  C# не поддерживает многие концепции функционального программирования, в результате чего попытки применять функциональный стиль выглядят уродливо и непонятно.  Многие задачи проще решать процедурно.</p></li>
</ol>

<p>Люк делает вывод, что нет смысла заниматься самосовершенствованием, если потом не сможешь использовать обретенные навыки.</p>

<p>С изменением среды, на самом деле, все плохо.  Шансы рядового программиста или проджект-менеджера изменить политику и стандарты компании, как правило, асимптотически приближаются к нулю.  Исключения, когда проектная команда обладает высокой степенью автономности и может использовать какие угодно инструменты, редки как девственность.</p>

<p>В свое время это было одним из факторов, побудивших меня <a href="http://alexlebedev.com/blog/%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d1%8f-%d1%83%d1%85%d0%be%d0%b6%d1%83-%d0%b2-%d1%81%d0%be%d0%b1%d1%81%d1%82%d0%b2%d0%b5%d0%bd%d0%bd%d1%8b%d0%b9-%d0%b1%d0%b8%d0%b7%d0%bd%d0%b5%d1%81/">покинуть моего предыдущего работодателя и начать работать на себя</a>.</p>

<p>Добавлю еще, что все относительно.  Можно заменить языки в обсуждаемом примере на другие, но проблема останется той же.  Есть языки лучше, чем Python и Haskell, есть и <em>множество</em> языков, куда хуже C#.  В реальной жизни все еще сложнее, потому что важно не столько качество языка, сколько пригодность его и связанной с ним платформы для решения конкретной задачи.</p>

<h2>Точка зрения компании</h2>

<p>С другой стороны, компания существует не для того, чтобы развлекать сотрудников.  Компания существует для того, чтобы получать прибыль.  Соответственно, инструменты выбираются в первую очередь по влиянию их использования на прибыль.</p>

<p>Ряд задач требуют использования неоптимальных с точки зрения эстетики и уровня абстракции языков.  Драйвера и embedded-приложения пишутся на C.  Windows-приложения &#8212; на том, что поставляет в данный момент Microsoft (сейчас это C#).  SAP кастомизируется с помощью ABAP, недалеко ушедшего в своем развитии от COBOL.  Можете продолжать до бесконечности.</p>

<p>Кроме того, есть ниши, где выбор языка может повлиять на маркетинговую привлекательность продукта.  Java продается.  C# продается.  Hashkell&#8230;  &#8212; а что это такое?  Думаю, логика выбора языка в таком случае будет очевидна.</p>

<p>Что думаете по этому поводу, коллеги?</p>
]]></content:encoded>
			<wfw:commentRss>http://alexlebedev.com/blog/do-not-learn-high-level-languages/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
	</channel>
</rss>

