Установка Python в режиме portable под Windows

Установка Python

  1. Скачать инсталлятор в формате MSI отсюда: https://python.org/downloads/
  2. Установить полученный дистрибутив <python.msi> в папку <INSTALLDIR> командой:
    msiexec /a <python.msi> /qn TARGETDIR="<INSTALLDIR>"

    , где /a — команда на установку с админскими правами, /qn — не задавать лишних вопросов, TARGETDIR — конечная папка

Установка Python Package Index

  1. Скачать файл getpip.py: https://bootstrap.pypa.io/get-pip.py
  2. Установить pip командой:
    <INSTALLDIR>\python.exe getpip.py
  3. Теперь любые пакеты можно устанавливать командой:
    <INSTALLDIR>\Scripts\pip.exe install <package name>

В итоге у вас есть обособленный Python интерпретатор любой версии с любыми пакетами в комплекте.

Также у вас есть возможность запускать *.py файлы с помощью установленного дистрибутива по клику мыши. Для этого вам понадобится PyLauncher.

Установка и настройка PyLauncher

  1. Скачать и установить pylauncer отсюда: https://bitbucket.org/vinay.sajip/pylauncher/downloads
  2. Открыть/создать файл %LOCALAPPDATA%\py.ini и добавить строку:
    [commands]
    mypython=<INSTALLDIR>\python.exe
  3. Добавить/поменять первую строку запускаемых *.py файлов на такую:
    #!mypython

    Теперь при запуске этих файлов их будет обрабатывать ваш интерпретатор (<INSTALLDIR>\python.exe)

Как это работает?

Первая строчка в формате #!<program name> используется в *nix подобных системах для автоматического выбора интерпретатора во время запуска и называется она, кстати, «shebang». В Windows системах эта строка игнорируется. PyLauncher просто перехватывает обработку *.py файлов и вызывает правильный Python интерпретатор.

Ускорение работы Python

Я рекомендую отключать создание и обновление *.pyc и *.pyo файлов в процессе работы Python. Аргументы могу привести следующие: оперативной памяти сейчас у всех хватает, процессор достаточно быстрый. Но устройство хранения данных (в народе флешка, жёсткий диск, SSD) по-прежнему слабовато.

Поэтому не выпендривайтесь и запускайте python с параметром -B или добавьте параметр PYTHONDONTWRITEBYTECODE в переменную окружения с любым непустым значением. Например, PYTHONDONTWRITEBYTECODE=Non_empty_string.

Формат файлов в папке .git/objects

- А не запилить ли мне свой .git с преферансом и блудницами, — подумал я. И запилил! По крайней мере, малюсенькую часть, а именно папку .git/objects. Формат хранения обычных файлов простой — берем sha1 от данных этого файла, создаем папку из первых двух символов этого хеша, а внутрь заливаем пакованные данные с именем оставшегося хеша.

Вот такой скрипт получился на питоне. Зачем делал — не спрашивайте, не знаю. :)

import os
import hashlib
import zlib
 
# Какие папки сканируем
directories = ( "D:\\resources", )
# Какие файлы учитываем
extensions = ( '.png', '.wav', '.jpg', '.dds' )
 
# Количество повторов
numDuplicates = 0
# Общее количество обработанных файлов
numFiles = 0
 
for directory in directories:
    for root, dirs, files in os.walk(directory):
        for filename in files:
            basename, ext = os.path.splitext(filename)
            full = os.path.join(root, filename)
            # Пропустим неизвестный файл
            if ext not in extensions:
                print 'Skip %s' % full
                continue
 
            # Понеслась!
            print 'Processing %s' % full,
 
            # Возьмем хеш от содержимого файлов
            data = open(full, 'rb').read()
            hash = hashlib.sha1(data).hexdigest()
            print '.. done'
 
            # Название папки - первые два символа от хеша
            prefix = hash[:2]
            # Название файла - остальные символы хеша
            postfix = hash[2:]
 
            # Создадим подпапку в папочке objects
            dirname = os.path.join('objects', prefix)
            if not os.path.isdir(dirname):
                os.makedirs(dirname)
 
            # Выходной файл с данными
            filename = os.path.join(dirname, postfix)
            if not os.path.isfile(filename):
                # Пишем данные, предварительно запаковав их с помощью zlib
                open(filename, 'wb').write(zlib.compress(data))
            else:
                # Такой файл уже есть, найден дубликат, куда смотрел дизайнер?!
                numDuplicates += 1
            numFiles += 1
 
# Краткий отчет
print 'Total: %d, duplicates: %d' % (numFiles, numDuplicates)
Python модуль для создания древовидной структуры даных на лету

Хочу представить на суд общественности модуль, который позволяет создавать древовидные структуры на лету. Сначала пример, потом сам модуль. Вопросы по модулю смело задавайте в комментариях к посту.

Пример использования

# -*- coding: utf-8 -*-
import cPickle as pickle
from Dictionary import Dictionary
 
# Создаем пустой объект "animalsDict"
animalsDict = Dictionary()
 
# Добавляем ветку "cats" и ноду "wild" со значением "Tiger"
animalsDict.cats.wild = 'Tiger'
# К ветке "cats" добавляем ноду "home" со значение "House"
animalsDict.cats.home = 'House'
# Добавляем ветку "dogs" одной строкой с нодами "wild" и "home"
animalsDict.dogs = Dictionary(wild='wolf', home='House')
# Выводим данные с префиксом "animals"
print "before dump:"
animalsDict.printf('animals')
 
# Сохраняем объект "animalsDict" в строку
data = pickle.dumps(animalsDict)
# Восстанавливаем данные в объект "animalsRestored"
animalsRestored = pickle.loads(data)
 
# Выводим данные с префиксом "animals2"
print
print "after dump:"
animalsRestored.printf("animals2")
 
# Удаляем ветку cats
del animalsRestored.cats
 
# Повторно выводим данные
print
print "after remove:"
animalsRestored.printf("animals2")
 
# Создаем объект только для чтения
p = Dictionary(read_only=True, x=2, y=3.5, z=1.0)
 
# Выводим объект "p" с префиксом "point"
print
print "read only:"
p.printf("point")
 
# Если это раскомментировать, будет исключение типа KeyError
# del p.x

Вывод

before dump:
animals.cats.home = House
animals.cats.wild = Tiger
animals.dogs.home = House
animals.dogs.wild = wolf
 
after dump:
animals2.cats.home = House
animals2.cats.wild = Tiger
animals2.dogs.home = House
animals2.dogs.wild = wolf
 
after remove:
animals2.dogs.home = House
animals2.dogs.wild = wolf
 
read only:
point.x = 2
point.y = 3.5
point.z = 1.0

Модуль Dictionary.py

Исходники программы на Python

import cPickle as pickle
 
 
class Dictionary:
    def __init__(self, read_only=False, **params):
        self.__dict__['__children__'] = {}
        self.__dict__['__readonly__'] = read_only
        for key, value in params.iteritems():
            self.__dict__['__children__'][key] = value
 
 
    def clear(self):
        for key, value in self.__dict__['__children__'].iteritems():
            if isinstance(value, Dictionary):
                value.clear()
        self.__dict__['__children__'].clear()
 
 
    def printf(self, prefix=None):
        for key in sorted(self.__dict__['__children__'].iterkeys()):
            value = self.__dict__['__children__'][key]
            if value is None:
                continue
            if isinstance(value, Dictionary):
                if prefix:
                    value.printf('%s.%s' % (prefix, key))
                else:
                    value.printf('%s' % key)
            else:
                if prefix:
                    print '%s.%s = %s' % (prefix, key, str(value))
                else:
                    print '%s = %s' % (key, str(value))
 
 
    def __getattr__(self, name):
        try:
            return self.__dict__['__children__'][name]
        except KeyError:
            if not self.__dict__['__readonly__']:
                value = self.__dict__['__children__'][name] = Dictionary()
                return value
            raise
 
 
    def __delattr__(self, name):
        if self.__dict__['__readonly__']:
            raise KeyError, "Read only structure."
        del self.__dict__['__children__'][name]
 
 
    def __setattr__(self, name, value):
        children = self.__dict__['__children__']
        old_value = children.get(name)
 
        if name not in children:
            if self.__dict__['__readonly__']:
                raise KeyError, "Read only structure."
        elif isinstance(old_value, Dictionary):
            if old_value:
                raise AttributeError, "Attribute %s has children: %s" % (repr(name), old_value)
 
        children[name] = value
 
 
    def __getitem__(self, name):
        return self.__getattr__(name)
 
 
    def __setitem__(self, name, value):
        self.__setattr__(name, value)
 
 
    def __delitem__(self, name):
        self.__delattr__(name)
 
 
    def __eq__(self, value):
        if isinstance(value, Dictionary):
            return repr(self) == repr(value)
        if isinstance(value, dict):
            return self.__dict__['__children__'] == value
        return value is None
 
 
    def __ne__(self, value):
        return not self.__eq__(value)
 
 
    def __nonzero__(self):
        return bool(self.__dict__['__children__'])
 
 
    def __str__(self):
        return str(repr(self))
 
 
    def __repr__(self):
        params = [('read_only=%s' % self.__dict__['__readonly__'])]
        for key in sorted(self.__dict__['__children__'].iterkeys()):
            value = self.__dict__['__children__'][key]
            params.append('%s=%s' % (key, repr(value)))
        return 'Dictionary(%s)' % ', '.join(params)
 
 
    def __len__(self):
        return len(self.__dict__['__children__'])
 
 
    def iterkeys(self):
        return self.__dict__['__children__'].iterkeys()
 
 
    def itervalues(self):
        return self.__dict__['__children__'].itervalues()
 
 
    def iteritems(self):
        return self.__dict__['__children__'].iteritems()
 
 
    def __iter__(self):
        return self.__dict__['__children__'].__iter__()
 
 
    def __setstate__(self, state):
        for key, name, value in state:
            if key == 'param':
                self.__dict__[name] = value
            elif key == 'value':
                self.__dict__['__children__'][name] = pickle.loads(value)
 
 
    def __getstate__(self):
        result = []
        result.append(('param', '__readonly__', self.__dict__['__readonly__']))
 
        for name, value in self.__dict__['__children__'].iteritems():
            result.append(('value', name, pickle.dumps(value)))
        return tuple(result)
 
 
    def __getinitargs__(self):
        return ()
 
 
    @staticmethod
    def parse(kwargs):
        kwargs = dict(kwargs)
        params = {}
 
        while kwargs:
            key, value = kwargs.popitem()
            uri = key.split(".")
            params_ptr = params
 
            if len(uri) == 1:
                params[key] = value
                continue
 
            for i in xrange(len(uri)):
                k = uri[i]
                if i == len(uri) - 1:
                    params_ptr[k] = value
                elif k not in params_ptr:
                    params_ptr[k] = {}
                params_ptr = params_ptr[k]
 
        def create(dictionary):
            result = {}
            for key, value in dictionary.iteritems():
                if isinstance(value, dict):
                    result[key] = create(value)
                else:
                    result[key] = value
            return Dictionary(read_only=True, **result)
 
        return create(params)

Создание модуля расширения для Python на языке Си

Простой, а главное — рабочий пример модуля расширения на языке C для питона. Модуль называется example, а реализовывает одну функцию hello, которая вызывается с параметром who и возвращает «Hello %s» % who.

Листинг модуля example.c

Python 2.xx

#include <Python.h>
 
PyObject *hello( PyObject *self, PyObject *args, PyObject *kwargs )
{
    char *who = 0;
    static char *keywords[] = {"who", NULL};
    PyObject *result = 0;
 
    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", keywords, &who))
    {
        return NULL;
    }
 
    result = PyString_FromString("Hello ");
    PyString_Concat(&result, PyString_FromString(who));
    return result;
}
 
 
static PyMethodDef example_methods[] = 
{
    { "hello", (PyCFunction) hello, METH_KEYWORDS, "hello(who) -- return \"Hello who\"" },
    { NULL, 0, 0, NULL }
};
 
 
PyMODINIT_FUNC initexample()
{
    (void) Py_InitModule("example", example_methods);
 
    if (PyErr_Occurred())
    {
        PyErr_SetString(PyExc_ImportError, "example module init failed");
    }
}

Python 3.xx

#include <Python.h>
 
PyObject *hello( PyObject *self, PyObject *args, PyObject *kwargs )
{
    char *who = 0;
    static char *keywords[] = {"who", NULL};
 
    if (PyArg_ParseTupleAndKeywords(args, kwargs, "s", keywords, &who))
    {
        return PyUnicode_FromFormat("Hello %s", who);
    }
    return NULL;
}
 
 
static PyMethodDef example_methods[] = 
{
    { "hello", (PyCFunction) hello, METH_KEYWORDS, "hello(who) -- return \"Hello who\"" },
    { NULL, 0, 0, NULL }
};
 
 
static struct PyModuleDef example_module = 
{
    PyModuleDef_HEAD_INIT,
    "example",
    NULL,
    -1,
    example_methods
};
 
 
PyMODINIT_FUNC PyInit_example(void)
{
    return PyModule_Create(&example_module);
}

Листинг setup.py

setup.py

from distutils.core import setup
from distutils.extension import Extension
examplemodule = Extension(name="example", sources=['example.c', ])
setup(name="example", ext_modules=[examplemodule])

Компилируем:

python setup.py build

Инсталлируем:

python setup.py install

Выполняем простенький тест:

from example import hello
print hello(who="world!")

Радуемся :)

Hello world!

Полезные ссылки (на английском):

Цикл статей про Boost.Python на хабре:

P.S. По своему опыту могу сказать, что Boost.Python — редкостное барахло, возьмите лучше библиотеку PyCXX.

Утилита для создания текстурных атласов

Представляю на суд общественности утилиту для создания текстурных атласов из набора изображений.

Утилита написана на питоне мной и моим коллегой, работает исправно до сих пор.

Использование тривиально:

Usage: atlastool.py [options] directory
 
Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -v, --verbose         verbose output
 
  Atlas options:
    -W MAXWIDTH, --max-width=MAXWIDTH
                        max atlas width [default 2048]
    -H MAXHEIGHT, --max-height=MAXHEIGHT
                        max atlas height [default 2048]
    -p PADDING, --padding=PADDING
                        atlas image padding [default 1]
    -s SORTON, --sort-on=SORTON
                        sort parameter (width or height) [default "height"]
    --skip-dimension-sum=SKIPDIMENSIONSUM
                        skip images with dimension sum greater then [default
                        4294967296]
    -a ALPHATHRESHOLD, --alpha-threshold=ALPHATHRESHOLD
                        alpha threshold for image trimming [default 1]
    --no-optimize       disable atlas size optimization
 
  Output options:
    -f FORMAT, --format=FORMAT
                        output format (ogre, cocos2d, cocos2d-with-path)
    -o OUTPUTDIRECTORY, --output=OUTPUTDIRECTORY
                        output directory [default "./atlases"]
    -n OUTPUTNAME, --output-name=OUTPUTNAME
                        output filename [default "atlas"]

На данный момент поддерживает только png, но при желании можно добавить любой известный формат.

Блог Евгения Жирнова