Записи по тегу “python”.

Дайте гостю печеньку, он тоже ее достоин!

Допустим, у вас есть модель, которая хранит состояние в базе данных, данная модель связана с пользователями. Но как быть в случае неавторизованного пользователя? Хранить состояние в кукисах браузера. Для этого пришлось бы пробежаться по модулю и ставить проверки, добавлять новые методы, переменные. В итоге код превратится в сплошной if… else, а оно нам надо? «А что делать?» — спросите вы. (Надеюсь что не спросите =) Без паники! Я, как-а-вот, ваш спаситель, расскажу и покажу на выход.


Выход — сделать фейковую модель и орудовать ею, как и с обычной джанговской моделью.

Пример


class UserBasket(models.Model):
user = models.ForeignKey(User)
shakedtimes = models.IntegerField(default = 0)

Тогда нашей фейковой моделью будет


class AnonymousBasket(object):

def __init__(self, request, response):
self.shakedtimes = request.COOKIES.get('sk_times', '0')
self.response = response

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


def save(self):
self.response.set_cookie('sk_times', self.shakedtimes)

Красяво? Думаю да. Минусом выступает то что в фейковую модель необходимо передать объект запроса и ответа, но как вы собрались считывать и сохранять куки, а? А? То-то же.

Разбиение объектов на “равные” части

Предлагаю вашему вниманию небольшой снипет, который мы написали, когда понадобилось сделать вывод объектов по колонкам.

  1. from django import template
  2.  
  3.  
  4. register = template.Library()
  5.  
  6.  
  7. class SplitObjectsNode(template.Node):
  8.     def __init__(self, objects, list):
  9.         self._objects = template.Variable(objects)
  10.         self._list = list
  11.  
  12.     def render(self, context):
  13.         import math
  14.         columns_count = len(self._list)
  15.         objects = self._objects.resolve(context)
  16.  
  17.         for item in self._list:
  18.             context[item] = []
  19.  
  20.         if len(objects) == 0:
  21.             return
  22.  
  23.         dec = float(len(list(objects))) / columns_count
  24.         mid = int(math.ceil(dec))
  25.         pass_part = len(list(objects)) % columns_count
  26.         count_full_cols = columns_count – pass_part
  27.  
  28.         idx = 0
  29.         index = 0
  30.         for item in self._list:
  31.             step = mid
  32.             if idx >= pass_part and pass_part != 0:
  33.                 step = mid – 1
  34.             context[item] = objects[index: step + index]
  35.             index += step
  36.             idx += 1
  37.         return
  38.  
  39.  
  40. @register.tag
  41. def split_objects(parser, token):
  42.     """                                                                                                                                                                                                      
  43.    Split content of the objects to equal parts. Number of parts is a count of passed arguments to tag.                                                                                                      
  44.                                                                                                                                                                                                            
  45.    Example:                                                                                                                                                                                                
  46.    {% split_objects objects col1 col2 col3 %}                                                                                                                                                              
  47.    <table><tr><td>                                                                                                                                                                                          
  48.    {% for item in col1 %} {{ item.title }} {% endfor %}                                                                                                                                                    
  49.    </td><td>                                                                                                                                                                                                
  50.    {% for item in col2 %} {{ item.title }} {% endfor %}                                                                                                                                                    
  51.    </td><td>                                                                                                                                                                                                
  52.    {% for item in col3 %} {{ item.title }} {% endfor %}                                                                                                                                                    
  53.    </td></tr></table>                                                                                                                                                                                      
  54.    """
  55.     parts = token.contents.split()
  56.     if len(parts) < 3:
  57.         raise template.TemplateSyntaxError, "Tag should get at the least 3 arguments. Example usage is {% split_objects objects col1 col2 %}"
  58.  
  59.     tagname = parts.pop(0)
  60.     objects = parts.pop(0)
  61.     return SplitObjectsNode(objects, parts)

Основы emacs (внешняя ссылка)

После поисков редактора питоновских скриптов я прошустил довольно много, спрашивал, читал, изучал. Мне не нужен был ни Eclipse ни IDEA. Для моего домашнего ноута честно признатся таких монстров вообще не нужно.

В итоге остановился на emacs. Сейчас читаю классную статью, которую, если вы так же как и я решили перейти на emacs, можете прочитать вот

Кстати под Mac OS X есть отличная обертка над emacs Aquamacs

Вызов конструктора родительского класса

Христос Воскрес!

Воистину все постигается в чтении умных книг. Буквально недавно сообразил, какую возможно грубейшую ошибку мы совершили в создании одного приложения на Python. Мы использовали для вызова методов родительского класса super.

Пример:

  1. class B:
  2.  
  3.     def __init__(x, y):
  4.         self.x = x
  5.         self.y = y
  6.  
  7.     def setX(x):
  8.         self.x = x
  9.  
  10.  
  11. class A(B):
  12.     def __init__(x, y, r):
  13.         super(A, self).__init__(x, y)
  14.         self.r = r
  15.  
  16.     def setX(x):
  17.         super(A, self).setX(x + self.r)

А теперь посмотрим как было бы правильнее

  1. class B:
  2.  
  3.     def __init__(self, x, y):
  4.         self.x = x
  5.         self.y = y
  6.  
  7.     def setX(self, x):
  8.         self.x = x
  9.  
  10.  
  11. class A(B):
  12.     def __init__(self, x, y, r):
  13.         B.__init__(self, x, y)
  14.         self.r = r
  15.  
  16.     def setX(self, x):
  17.         B.setX(self, x + self.r)

Переход с Python 2.5 на Python 2.6

Решили перевести наш сервер на версию Python 2.6. Замечу что до этого использовали 2.5, что как оказалось является довольно устаревшей. Ну решили – начал переводить.

Казалось бы что сложного, но Gentoo всячески отказывалась принимать версию из-за того что она была замаскирована. Что это означает я даже не подозреваю, поэтому раз через emerge не хотела, то решил просто собрать исходники.

Скачал необходимую версию питона, скомпилил – все отлично, осталось перевести ссылку python и python-config на новую версию.

# rm /usr/bin/python /usr/bin/python-config
# ln -s /usr/local/bin/python2.6 /usr/bin/python
# ln -s /usr/local/bin/python2.6-config /usr/bin/python-config

Все отличненько и

# python --version

вернула 2.6.2, осталось пересобрать mod_python, чтобы он использовал обновленный питон. Сделал через обычный emerge.

В итоге все довольны, и осталось только переустановить все необходимые пакеты (дада, именно переустановить, обычное линкование не очень хороший метод), чтобы все проекты поднялись.

hasattr и __dict__

Заметил что hasattr в коей мере некорректно работает на определение наличия аттрибута в классе, пришлось отказаться от него и использовать такую проверку

  1. if "value" not in MyClass.__dict__:
  2.     pass

Какие минусы этого подхода полностью не знаю, но знаю что может привести в определенных ситуациях к зацикливанию, это в случае если использовать __setattr__ в паре с ним. Может у кого-то другие подходы? Или просвятит на предмет использования __dict__ :-)

Динамическое создание объектов

Простой трюк если вам необходимо создать объект из его названия.

Для себя я нашел простой набор функций

  1. def _get_mod(modulePath):
  2.     return __import__(modulePath, globals(), locals(), [‘*’])
  3.  
  4.  
  5. def _get_func(fullFuncName):
  6.     """Retrieve a function object from a full dotted-package name."""
  7.    
  8.     # Parse out the path, module, and function
  9.     lastDot = fullFuncName.rfind(u".")
  10.     funcName = fullFuncName[lastDot + 1:]
  11.     modPath = fullFuncName[:lastDot]
  12.    
  13.     aMod = _get_mod(modPath)
  14.     aFunc = getattr(aMod, funcName)
  15.    
  16.     # Assert that the function is a *callable* attribute.
  17.     assert callable(aFunc), u"%s is not callable." % fullFuncName
  18.    
  19.     # Return a reference to the function itself,
  20.     # not the results of the function.
  21.     return aFunc
  22.  
  23.  
  24. def _get_class(fullClassName, parentClass=None):
  25.     """Load a module and retrieve a class (NOT an instance).
  26.    
  27.    If the parentClass is supplied, className must be of parentClass
  28.    or a subclass of parentClass (or None is returned).
  29.    """
  30.     aClass = _get_func(fullClassName)
  31.    
  32.     # Assert that the class is a subclass of parentClass.
  33.     if parentClass is not None:
  34.         if not issubclass(aClass, parentClass):
  35.             raise TypeError(u"%s is not a subclass of %s" %
  36.                             (fullClassName, parentClass))
  37.    
  38.     # Return a reference to the class itself, not an instantiated object.
  39.     return aClass

После можно это использовать таким образом

  1. model_type = _get_class("example.MyView")

Далее просто создаем экземпляр нашего класса

  1. model_object = model_type()

Если надо вызвать определенный метод то достаточно воспользоваться getattr

  1. getattr(model_object, ‘mymethod’)()

кваргиарги

Иногда приходится выполнять запросы по нескольким условиям из одной таблицы. Как пример выборка какой нибудь статьи сначала по названию, потом по дате. В принципе такое будет выглядеть таким образом

  1. try:
  2.     if not objects:
  3.     objects = search_for(h_date__day = param)
  4. except:
  5.     objects = []
  6.        
  7. try:
  8.     if not objects:
  9.         objects = search_for(h_date__month = 11)
  10. except:
  11.     objects = []

Попробуем с этим что то сделать =) Для этого вспомним что такое kwargs и args и насколько полезными они часто бывают.

  1. objects = search_for(title__icontains = param)
  2.  
  3. if not objects:
  4.     objects = search_for(h_date__day = param)
  5.    
  6. if not objects:
  7.     objects = search_for(h_date__month = month)

Как видно я вынес одинаковую часть в отдельный метод. Посмотрим что делает этот метод:

  1. def search_for(*args, **kwargs):
  2.     try:
  3.         objects = MyModelObject.objects.filter(*args, **kwargs)
  4.     except:
  5.         objects = []
  6.     return objects

Ничего сверхъестественного, верно? А можно унифицировать метод и использовать его где захочется для простой фильтрации.

  1. def filter_for(model, *args, **kwargs):
  2.     try:
  3.         objects = model.objects.filter(*args, **kwargs)
  4.     except:
  5.         objects = []
  6.     return objects
  7.  
  8.  
  9. def func(request, param):
  10.     filter_for(MyModel, title_icontains = param)

Декораторы в Python

Декоратор в питоне это не одно и тоже что и декоратор в шаблонах проектирования как бы это показалось на первый взгляд. Декоратор в понимании шаблонов проектирования добавляет функциональность в runtime, а python декораторы делают это только во время определения функции. В этом и есть огромная разница между ними.
Далее под словом «декоратор» имеем ввиду python декоратор.
Декоратор определяется знаком @ и названием в начале функции. Пример (взят с вики):

  1. @viking_chorus
  2. def menu_item():
  3.     print "spam"

Данное определение эквивалентно

  1. def menu_item():
  2.     print "spam"
  3. menu_item = viking_chorus(menu_item)

Здравствуй аспектно ориентированное программирование. Теперь сам декоратор.

  1. def viking_chorus(myfunc):
  2.     def inner_func(*args, **kwargs):
  3.         for i in range(8):
  4.             myfunc(*args, **kwargs)
  5.     return inner_func

В итоге мы получим не что иное как вывод слова spam целых восемь раз. Декораторы как и любые функции могут принимать параметры.

Например

  1. def console(text_to_print):
  2.     def dec(func, *args, **kwargs):
  3.         print text_to_print
  4.         func(*args, **kwargs)
  5.     return dec
  6.  
  7. @console("ahtung")
  8. def index():
  9.     print "test"

Выведет сначала ahtung и только потом test.
Пример, если декорируемая функция получает дополнительные аргументы. Взят из пакета pybb, надеюсь автор не против =).

  1. def ajax(func):
  2.     def wrapper(request, *args, **kwargs):
  3.         if request.method == ‘POST’:
  4.             try:
  5.                 response = func(request, *args, **kwargs)
  6.             except Exception, ex:
  7.                 response = {‘error’: traceback.format_exc()}
  8.         else:
  9.             response = {‘error’: {‘type’: 403, ‘message’: ‘Accepts only POST request’}}
  10.         if isinstance(response, dict):
  11.             return JsonResponse(response)
  12.         else:
  13.             return response
  14.     return wrapper

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

  1. @ajax
  2. def setstatus(request):
  3.     return {‘newstate’: ‘updated’, ‘date’: ‘2008-08-21}

Функция может быть задекорирована не только одним декоратором, их может быть несколько.

  1. @accepts(int,int)
  2. @returns(float)
  3. def bar(low,high):
  4.     pass

Или тоже самое в виде списка

  1. [@accepts(int,int), @returns(float)]
  2. def bar(low,high):
  3.     pass

Как оформлять дело каждого, но скорее более читабельным все таки является последовательное декорирование, то есть первый вариант.

Использовал материал и код из

Трека pybb:

http://pybb.org/browser/pybb/util.py

Вики:

http://en.wikipedia.org/wiki/Python_syntax_and_semantics#Decorators
http://ru.wikipedia.org/wiki/Python#.D0.94.D0.B5.D0.BA.D0.BE.D1.80.D0.B0.D1.82.D0.BE.D1.80.D1.8B

Офсайта:

http://www.python.org/dev/peps/pep-0318/

Разработка через тестирование глазами новичка

Иногда программисты которые пишут приложения почему то игнорируют написание тестов, а то и вообще их избегают. Я не могу сказать, что я профи в разработке через тестирование, но разработав пару классов и применив их в проекте, уже могу точно сказать, что классы, написанные через тесты обладают наибольшей стабильностью. Конечно, есть еще у меня некоторые недоработки, которые я стараюсь заполнять сразу же.

Практически процесс довольно интересный, хоть и может показаться долгим и утомительным, но как я заметил, когда писал код я вижу слабые участки (а порой ленюсь их исправлять, но позже все таки исправляю), код становится более чистым, особенно, когда отрефакторишь и прогонишь функционал через тесты. А видеть при прогоне слово Ok просто блаженство.

Кстати заметил фишку в разработке тестов в unittest питона. Если в начало теста поставить описание docline то при проходе отобразится не скучный путь до пакета и модуля а полноценное описание теста. К примеру

  1. def testSetRatingTwice(self):
  2.     "Expect raising of exception when user set rating for object twice"
  3.     # functional of the test
  4.     self.assertRaises(….)

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

Очень не нравится пакет pymock, который очень часто вводит меня в ступор своими Inappropriate action или чем то подобным, поэтому стараюсь как можно чаще избегать подобных тестов, а то и вообще не прибегать к mock совсем.

Продолжаю процесс разработки через тестирование.