Если коротко, то типы в Python делятся либо на встроенные и пользовательские, либо на mutable и immutable (сразу так и не подобрал подходящего перевода на русский язык). Ну а если немного подробнее, то как-то так:

Рассмотрим простой пример кода (здесь и далее примеры кода будут писаться и выполняться в ipython’е):

In [1]: a = 1000
In [2]: a
Out[2]: 1000
In [3]: type(a)
Out[3]: int

Здесь мы объявляем переменую создаем объект (object) a, со значением(value) 1000 типа(type) int. Объекты в python - это абстрация над данными. Поэтому любой объект содержит в себе идентификатор(identity), тип и значение. Идентификатор - это адрес объекта в памяти, который никогда не меняется. Но мы можем присвоить объекту новое значение, написав, следующий код:

In [4]: a = 'hello'
In [5]: a
Out[5]: 'hello'
In [6]: type(a)
Out[6]: str

Теперь мы присвоили объекту a новое значение типа string. За кулисами этого всего, интерпритатор сделал, примерно, такое: создал новый объект ‘hello’, и поменялл ссылку переменной a на новый объект в памяти. При этом старое значение 1000 все еще хранится в памяти и будет удалено сборщиком мусора, т.к. на него нет ни одной ссылки. Тоже самое происходит и присвоении переменной нового значений такого же типа:

In [7]: b = 1000
In [8]: id(b)
Out[8]: 40235856
In [9]: b = 1001
In [10]: id(b)
Out[10]: 39189680

Интересный эффект будет при использовании целых чисел в диапазоне от -5, до 256 - их id всегда будет одинаковый, т.к. для улучшения быстродействия интерпритатор при старте создает объекты с этими значениями.

Стандартные типы в CPython делятся на два типа: mutable и immutable. Mutable — это обекты, значения которых могут быть изменены(например, list), а immutable — это обекты, значения которых не может меняться (например, string). Но это не значит, что если у нас есть переменная immutable типа, то ее нельзя изменить. Например, с типом string все привыкли работать так:

In [11]: text = 'Hello'
In [12]: text
Out[12]: 'Hello'
In [13]: text = 'Hello, World'
In [14]: text
Out[14]: 'Hello, World'

Как мы видим, начение переменной text поменялось. На самом деле, при присвоении переменной text нового значения, в памяти создается новый объект, и переменная text начинает на него ссылаться. В этом легко убедится при помощи функции id - получить идентификатор до и после изменения переменной.

Mutable типы ведут себя так:
In [15]: l = [1, 2, 3]
In [16]: l
Out[16]: [1, 2, 3]
In [17]: id(l)
Out[17]: 139901299808088
In [18]: l.append(4)
In [19]: l
Out[19]: [1, 2, 3, 4]
In [20]: id(l)
Out[20]: 139901299808088

Основываясь на этом свойстве(и не только), ключами в словаре (dict) могут быть только immutable обекты:

In [21]: d = {'one': 1}
In [22]: type(d)
Out[22]: dict
In [23]: d[1]=1
In [24]: d
Out[24]: {1: 1, 'one': 1}
In [25]: d[l] = 'list'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-25-4f49b18d7af7> in <module>() ----> 1 d[l] = 'list'
TypeError: unhashable type: 'list'

Immutable типы в Python — это числа(numbers), строки (strings) и кортежи (tuples).

Для облегчения привжу список некоторых всторенных типов (built-in types):

 

  • Numbers (числа) — int, long, float, complex(3+4j), Decimal, Fraction
  • Sequence Types (последовательности) — str, unicode, list, tuple, bytearray, buffer, xrange
  • Sets — set, frozenset
  • Maps (хеш-таблицы) — dict
  • Files, context managers и другие.

 

Ссылки по теме:

 

 

 


Безусловно, выбор языка программироания, текстового редактора, IDE, операционной системы и многого другого лежит на плечах каждого отдельно взятого разработчика. Даже если он работает в команде.

По сути, всем все-равно в какой IDE пишете код, если он работает так, как надо и написан вовремя. Унификация средст разработки внутри компании/команды лишь облегчает жизть менеджерам, ИТ, новым членам команды и упрощает коммуникацию между разработчиками. Ведь на много легче один раз написать инструкцию по установки всего нужного для запуска проекта ПО, например, для Ubuntu 13.10 x64 и запуск проекта в PyCharm, чем каждый раз сталкиваться с проблемой как запустить X в окружении Y.

Но каждый разработчик в праве решать сам чем он будет пользоваться. Вот только к выбору инструментов нужно подходить “с умом”. Главное - хорошо знать инструменты, которыми вы пользуетесь. Иначе будет ситуация, когда используется Photoshop только для изменения размера картинки. Досконально знать OS, IDE, текстовый редактор, которым пользуетесь каждый день не только приятно, но и полезно. Это может экономить массу времени. Всем известно, что использования горячих клавиш, в некоторых условиях, в разы быстрее, чем совершать нужные действия мышкой. Аналогичная ситуация с разнообразными инструментами автоматизации. Если мне нужно выполнить действиее более 2-3х раз - я пишу небольшой скрипт на bash или python. Иногда скрипты получаются не очень и маленькими, иногда в помощь приходят fabric/chef/puppet. Готовые настройки для используемых редактором и систем (профайлы bash, zsh) - упрощают жизь и экономят время, которое можно потратить на более полезные и интересные вещи.

Все вышесказанное всем известно и описано в множестве книг и постов. Захотелось повторить, т.к. я в очередной раз наступил на эти грабли: потратил почти час на написание простого скрипта на bash’е, аналог которого на python мне удалось написать за 10 минут. Да, я узнал кое-что новое о bash, но это не мой основной язык программирования, и скрипт писался исключительно для себя (и не важно, что любой может скачать его с github и пользоваться), но я потратил массу времени из-за того, что выбрал не тот инструмент. Нельзя путать процесс обучения и процесс разаботки. Между ними очень тонкая грань, которую легко перейти и потерять время напрасно.


Всегда недолюбливал Apache из-за формата его конфига. Конфиг Nginx’а мне вседа было порще читать и писать. Да и статику им раздавать хорошо и быстро, поэтому от Apache я, по возможности, отказываюсь. Но частая проблема с nginx в том, что приходится собирать необходимые модули из исходников самому, со всемы вытикаюющими плюсами и минусами. И, как полагается любому популярному и быстро развивающемуся проекту, документация по сборке этих самих модулей не всегда полная и актуальная.

Ниже (очень) короткое изложение того, что мне пришлось сделать для сборки mod_security. Надеюсь, это сэкономит кому-то время и нервы.

Оффициальная документация расположена тут и тут. При этом сложилось впечатление, что на github’е она более актуальная и полная. По ним можно собрать все, при условии, что у вас уже стоят нужные пакеты. Мне понадобились:

$ sudo apt-get install autoconf automake libtool libcurl4-openssl-dev

Тажке, не забыть выполнить “./autogen.sh” перед “./configure”. Почему-то, в секции “Installation for NGINX” документации написаны только общие сведения. Мне, например, не хотелось и не нужно было собирать mod_security для apache, устанавливать пакет apache2-dev и т.д. Поэтому выполнил configure со следующими параметами:

$ ./configure --prefix=/usr --disable-apache2-module --enable-standalone-module

Далее сборка, установка и настройка у меня прошла без проблем.



Как всегда, в рубрике “мысли в слух” звучит только исключительно мнение автора и может не соответствовать действительности.

Безусловно, всем, ну ладно, почти всем, хочется писать только на самых последний версиях фреймворков, использовать самые новые технологии и навсегда забыть о так называемом legacy code. Но мало кто из разработчиков думает о том, когда и зачем это нужно, а когда - невозможно. Разберем пример выбора фреймворка Х на примере нескольких случаев.

Случай #1. Работает - не трожь!

Очень распространенный и нелюбимый мною случай. Проекту N лет, заказчика он устраивает, клиенты довольны. Но проект еще развивается, нужно не только фиксить баги, но и улучшать текущую, добавлять нувою функциональность. Но что бы добавить одну простую новую функцию, нужно написать “много” кода, который уже есть в новой версии фрреймворка X, который используется. А новая версия может быть несовместима со старой. А если и совместима - нет гарантии что все быстро и хорошо заработает. Часто бывает что проще и дешевле переписать с нуля. И вот тут должен выключаться режим разработчика и включаться режим владельца бизнеса. На основе естимейтов по переходу на новый фреймворк, можно посчитать что дешевле: дописать старое или написать, частично или полностью, новое. Наверняка, в каких-то правильных книжках по менеджменту (возможно, не только для ИТ) и/или економики уже есть готовые формулы для этого, но я не встречал такого. Самое сложное здесь то, что формула будет не такой простой, как может показаться изначально.

Например:

Функция А будет сделана(разработанна, протестированна, задеплоенная - вообщем, готова для использования) за 30 часов на старом фреймворке и за 20 на новом. Умножая кол-во часов на n денег легко посчитать как дешевле. Но тут нужно учитывать всевозможные риски с новым фреймворком - его баги, баги после миграции, стоимость изучения и т.д.

Пример выше сильно упрощен и я не сталкивался с таким в работе. Обычно, все выглядит так (цифры придуманы):

Функция А будет сделана(разработанна, протестированна, задеплоенная - вообщем, готова для использования) за 10 часов на старом фреймворке и за 50 на новом. Разница по деньгам и времени - в 5 раз. Тажело будет уговорить менеджера и/или заказчика. Но тут на решение должны влиять другие факторы, а именно:

 

  • каждая новая фича будет стоить дешевле из-за того, что разработчики уже выучат матчасть, набьют новые шишшки с новым фреймворком и т.д., в последствии чего - в идеальных условиях, время разработки будет стремится к времени на разработку этой же фичи на старом фремйворке - n часов.
  • во время миграции на новый фреймворк, пользователи продукта не будут видеть новых версий, что сильно влияет на time to market (TTM), может пказаться, что продукт больше не развивается.
  • на протяжении этого всего времени, нужно будет поддерживать старую/текущую версию продукта, что тоже стоит денег.
  • переписанный продукт на новый фреймворк X - часто является скорее новым продуктом, чем новой версией старого, со всемы вытикающими последствиями.

 

 

Случай #2. “Теперь мы используем фреймворк X версии Y” или “у нас новый проект на фреймворке X версии Y”.

Казалось бы все хорошо, фреймворк X версии Y - самая последняя версия самого крутого фреймворка, использующего самые новые технологии. Все хотят это испльзовать и работать с этим. Но пройдет сколько-то времени (месяц, два, год), а мы пишем все тот же проект, на все тех же технологиях, которые были актуальны и популярны полгода-год-два назад. И все сводится к вышеописанному сценарию #1.

 

Случай #3. Идеальный процесс разработки продукта.

Раз это описание идеального процесса, пусть это будет что-то типа continuous development. Все лучшее от разнообразных Agile методологий, Kaizen и т.д. и заказчик готов платить достаточно денег при соблюдении таких условий:

 

  • продукт будет выпущен в оговоренные сроки
  • дена дальшейшей разработки будет снижаться
  • цена поддержки будет низкой

 

Допустим, дата релиза запланированна на весну (сейчас декабрь). К тому времени должна выйти новая версия хорошо известного фреймворка X, а сейчас доступна совсем не production ready alpha. Т.к. авторы этого фреймворка всегда выпускают релизы вовремя(у нас же идеальная ситуация:) ), то можно начать писать на нем. Но т.к. у нас в руках очень сырая и нестабильная alpha версия, то нам нужно обезопасить себя от всяческих рисков и проблем, связанных с багами и недоработками фреймворка. Я вижу только один действительно рабочий способ - внедрение continuous integration процесса в полной его мере: покрытие всяческими тестами и постоянный их запуск. Таким образом, особенно при хорошем покрытии кода unit-тестами, мы можем отделить ошибки которые связаны с нашим кодом, от тех, что зависят от фреймворка и/или сторонних библиотек.

Таким образом мы берем на себя технологический риски, связанные с “сырым” фреймворком и возможные потери денег как на разработку продукта, так и на возможную недополученную прибыль от его использования и/или продажи. Но если все будет хорошо, и проект успешно выйдет в production, то мы получим следующие плюсы от этого:

 

  • малое значение TTM - продукт выходит на рынок как только пояляется новая технология/фреймворк
  • вы становитесь early adopters выбранного вами фреймворка
  • качество продукта будет достаточно высоким (см. о continuous integration)
  • счастливые разработчики, использующие самые последние технологии и фрейморки
  • разработка следующей версии продукта стартует быстрее (есть наборы тестов, CI) и так же может быть на еще более новой версии фреймворка

 

Минусов значительно меньше, но они более существенные - цена этого всего выше. И в лучшем случае - это только время и деньги. Добавьте сюда еще всевозможные риски разработки продукта в таких условиях (мало или совсем нет опыта с технологиями, фреймворк может зарелизиться посже или вообще не станет production ready, и др.) - станет не все так радостно и просто. Вообщем, как говорится, думайте сами, решайте сами...

 

Ссылки по теме:



Вот так неожиданно для себя узнал что Selenium Webdriver работает без Java. До этого всегда был уверен, что такого не может быть. Возможно, это произошло с релизом Selenium 2, возможно нет. Но мой мир уже не будет таким как прежде. 

Не поверил, пока сам не убедился в этом. Пришлось ставить виртуалку без Java и проверять следующий код:


Build succeeded, 156 warnings

Published 11/18/2013 by e0ne in Offtopic

Запустил “make build” и получил 156 ворнингов:(... Хотя, достаточно быстро получил более радужное число - 69, что тоже не мало. Нужно фиксить дальше, а пока - немного очень IMHO на эту тему.

Отключать или нет параметр “mark warnings as errors” (название может отличаться, но суть остается той же) - часто решают для конкретного проекта и/или команды. Иногда это не мешает работы продукта. Я бы сказал что в 98% случаев это не мешает, зато остаются 2%. И баги, попавшие в те самые 2%, часто являются самыми трудновоспроизводимимы и затратными по времени на исправления.

Итак, из-за чего появляются warnings? Самые частиы причины - это:

  • использование deprecated и/или недокументированного API.
  • несоответствие кода гайдлайнам (guidelines) и/или принятым стандартам языка программирования, фреймворка и т.д.
  • проблемы с настройкой окружения (environment).

Что плохого в использовании deprecated API? Ничего, если приложение будет удалено не посже, чем сразу после перговго запуска. В противном случае, рано или поздно, после очередного обновления используемой платформы и/или фреймворка что-то перестанет работать, т.к. deprecated код будет удален. Таким образом, ваш код, который использует устаревший API фреймворка, автоматически становится deprecated :). Готовы ли вы писать заведомо legacy код?

С недокументированными возможностями дела обстоят похожим образом, за исключением того, что никто не гарантирует что этот API будет работать так же, как и сейчас.

Код, который не соответствует стандартным гайдлайнам, как минимум, - странный. IMHO. Нет, я понимаю, что при использовании StyleCop (для C#) практически невозможно писать код, соответствующий всем его требованиям, но для того же Python’а, держать код в соответствии с PEP-8 достаточно просто. Трудности бывают только с 80-ю символами в строке. С PyLint все немного сложнее. Но никто не заставляет выполнять все требования. Главное - здравый смысл и правильная настройка инструментов, которые используются.

Проблемы с настройкой окружения. Тут не буду много писать, т.к. эта тема тянет на отдельный пост в блоге, который уже давно хочется написать. Скажу только то, что один и тот же код (id коммита, tag, и т.д.) должен, по возможности, собираться одинаково на всех используемых окружениях. Это, как правило, рабочий ПК разработчиков и тестировщиков и билд-серверы. Иначе где-то что-то пойдет не так.

Не попал в мой классификацию еще один случай - содержимое stderr. Бывает, что только туда пишуться какие-либо предупреждения, которые остаются непрочитанными. Давно уже руки тянуться помечать все билды как failed, если что-то есть в stderr, но пока это сделать не получается, т.к. “хороший продукт !== работающий и/или безбажный продукт”, а “хороший продукт == радующий пользователя/заказчика продукт”. Как-то так.



Разберем ситуацию, ставшую достаточно распространенной в наше время. Есть распределенная команда, допустим в двух городах (А и Б), которая работает над одним крупным проектом. Сборка проекта происходит на Jenkins’е в городе А и длится, например 3 минут. Скачать готовый продукт из города А в город Б можно в среднем за 30 минут (в зависимости от времени суток, загрузки канала, фазы луны и т.д.). Итого: команде из города Б нужно ждать час(60 минут) чтобы запустить/протестировать свежий билд. Очевидно, это достаточно много. Нужно ускорять.

Рассматривать вопросы вынесения этого всего (CI) в облако и прочее я не буду по двум причинам:

 

  • это не всегда возможно
  • и все-равно скачивать готовый билд приходится долго, только уже двум командам.

 

Также я пропущу вопросы синхронизации репозиториев с исходниками и прочие инфраструктурные вещи, не относящиеся к данной теме.

Решать данную проблему буду так: в обоих офисах настраивается Jenkins-нода, на которой можно собирать свежий билд. Главная задача - 2 одновременных билда одного проекта в разных офисах.

Делается это, примерно, так:

 

  1. Заходим на наш Jenkins и устанавливаем нужные плагины (Jenkins => Manage Jenkins =>  Manage Plugins => Available). Нам нужен “Throttle Concurrent Builds Plugin” (https://wiki.jenkins-ci.org/display/JENKINS/Throttle+Concurrent+Builds+Plugin) и NodeLabel Parameter Plugin (https://wiki.jenkins-ci.org/display/JENKINS/NodeLabel+Parameter+Plugin).
  2. Настраиваем Slave node. Документация лежит тут: https://wiki.jenkins-ci.org/display/JENKINS/Step+by+step+guide+to+set+up+master+and+slave+machines.
  3. Настраиваем нашу Job’у для параллельного выполнения на двух нодах. Я для примера создал Fake-long-job, которая выполняет простой bash-скрипт “sleep 20;”.
    • Отмечаем флаг “This build is parameterized”
    • Добавляем параметр Node

    • Выбираем на каких нодах можно выполнять билд

    • Настраиваем одновременные билды. В моем случае их может быть 2, по одному на каждой ноде

      .
    • Нажимаем кнопку Save.
  4. Проверяем, что все работает, запустив два билда на разных нодах.

 

Выше я описал только базовые настройки, которые подошли мне. Вам, возможно, прийдется дополнительно настраивать что либо еще.


Скорее заметка для себя. Постоянно забываю об этом ключе.

Иногда нужно очень быстро поднять HTTP-сервер с минимальным функционалом. Например, нужно проверить HTML+JavaScript, который работает только через веб-сервер. Я, конечно понимаю, что у меня под рукой всегда есть, как минимум nginx, и поднять на нем сайт, который будет раздавать статические фалы, делов на 5-10 минут. Но, во-первых, это долго, а во-вторых, этот самый nginx у меня находится на виртуалке, пусть и включенной в 90% времени на ноутбуке. А тут прийдется не только веб-сервер настроить, но и расшаренные папки в VirtualBox-е, и так далее...

Единственное, что у меня действительно всегда есть под рукой, это Python (уже несколько лет не пользуюсь ОС Windows, а на всех *nix он есть из коробки). Поднять простой тестовый веб-сервер на питоне можно всего одной командой:

$ python -m SimpleHTTPServer 8100

После этой команды, у вас локально запустится веб-сервер на 8100-м порту, который покажет содержимое текущей папки.

Работает это благодаря ключу -m, который указывает интерпритатору о том, что необходимо выполнить модуль как скрипт. Это идентично команде "$ python /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SimpleHTTPServer.py", но писать значительно проще. Единственное ограничение, что этот модуль должен быть доступен в sys.path


Или мне так не везет, или для всех это совершенно очевидные вещи, но я, при выполнении простой fabric task'и копирования содержимого директорий, столкнулся на ряд проблем, которые хоть и казались простыми и распростаренными, но были описаны в найденных мной блогах и мануалах в плной мере.

Проблема #1 - неправильный пользователь

Не всегда бывает что имя локального пользователя совпадает с удаленным. Ситуация распространенная и описанная всеми, кому не лень. Решение простое - указывем нужного пользователя в переменной env.user:

from fabric.api import *
env.user = 'e0ne'

Проблема #2 - аутентификация

Самый простой способ аутентификации по SSH - имя пользователя и пароль. Самая распространенная проблема - нужно как-то безопасно хранить пароль. А так, как я предпочитаю все скрипты для развертывания хранить в VCS (читать как в git’е), то возникает проблема: нехорошо хранить пароль в репозитории, особенно в открытом на GitHub’е. Решение простое - использование пары public/private RSA ключей. Процесс генерации и настройки (ключевые слова: ssh-keygen, ~/.ssh/known_hosts) описывать сейчас не буду, только то, что касается Jenkins’а.

В моем случае, Jenkins был установлен на Ubuntu server 13.04 командой  “apt-get install jenkins”, поэтому пользователь jenkins в системе создался таким образом, что без подоплнительной настройки под ним не залогинишься. Поэтому использем sudo и генерируем ключи для подключения по SSH, примерно, так:

$ sudo -u jenkins -i ssh-keyget -t rsa

После чего в домашнем каталоге jenkins’а будет лежать пара ключей, публичный из которых нужно положить на наш сервер, на который fabric будет копировать файлы. Достатьid_rsa.pub можно так:

$ sudo -u jenkins -i cat ~/.ssh/id_rsa.pub

И положить этот ключ на сервер, в моем случае в файл /home/e0ne/.ssh/known_hosts.

Этот способ так же решает проблему того, что не нужно передавать в fabric путь к ключу, он будет браться из пути по умолчанию.

Проблема # 3 - не загружаются файлы

Ошибка тут у меня была совсем непонятной (привет paramiko и протоколу ssh), откатывать настройки или делать это с другим сервером сейчас нет возможности. Да и точно не помню как я пришел к решению. Наверное, после медитации на вывод команды “ls -al”. Задача jenkins job’ы была в том, что нужно было апдейтить простое веб-приложение, а у пользователя, под которым jenkins/fabric подключался к серверу не было прав на запись в нужную директорию. Решилось добавлением пользователя в группу www-data, в которой у меня находится пользователь nginx’а.

Пока все. Эти три проблемы были при попытке “простого копирования” файлов. Дальше будет должно быть интереснее. Или проще. Как повезет:).



Иногда приходится делать удивительные и неожиданные для себя вещи. Например, писать что-то на PHP. Но написать мало - надо еще проверить что написанный код работает. В моем случае приложение было немного сложнее классического "hello, world", но все-равно требовалось проверить его работоспособность.

Т.к. это всего-лишь тестовая версия/PoC или что-то такое, интересовала самая простая схема его запуска. Apache не виртуалке c Ubuntu не стоял, под руку попал Nginx. Далее все происходило так:

  • установка необходимого пакета (PHP уже стоял):
    $ sudo apt-get install php5-fpm
    Не знаю насколько FastCGI хорош и применим в мире PHP, но мне подошел
  • выключаем работу с unix socket и вешаем FastCGI процесс на локальный порт 9000; для этого правим конфигурационный файл "/etc/php5/fpm/pool.d/www.conf" следующим образом:
    ;listen = /var/run/php5-fpm.sock
    listen = 127.0.0.1:9000
  • настраиваем Nginx ("/etc/nginx/sites-available/default"):
    location ~ \.php$ {
                    try_files $uri =404;
                    fastcgi_split_path_info ^(.+\.php)(/.+)$;
                    fastcgi_pass 127.0.0.1:9000;
                    fastcgi_index index.php;
                    include fastcgi_params;
            }
  • копируем нужный PHP-скрипт в директорию, на которую смотрит веб-сервер
  • проверяем работу скрипта
Все. Решение не претендует на правильность и полноту. Просто иногда надо.