1. Можно ли с помощью декораторов добавить новые методы к классу?
    Например так:
    @add_method
    class Clazz:
    def method1(self, msg):
    print msg

    >>>c = Clazz()
    >>>c.method1('ok')
    ok
    >>>c.method2('ok')
    ok
    Не знаю как написать такой декоратор? (добавляет в класс method2)
    Спасибо.
  2. Maximbo

    21.12.2009

    0 ↑
    0 ↓
    В версии питона 2.6 можно сделать так:
    def add_method2(cls):
    def method2(self):
    print 'method 2'
    print self.var
    cls.method2 = method2
    return cls

    @add_method2
    class Test(object):
    def __init__(self):
    self.var = 'instance var'

    def method1(self):
    print 'method 1'

    >>> obj = Test()
    >>> obj.method1()
    >>> obj.method2()
    Питон 2.5 не поддерживает синтаксис декорирования классов, поэтому придётся писать так:
    def add_method2(cls):
    def method2(self):
    print 'method 2'
    print self.var
    cls.method2 = method2
    return cls

    class Test(object):
    def __init__(self):
    self.var = 'instance var'

    def method1(self):
    print 'method 1'

    Test = add_method2(Test)

    >>> obj = Test()
    >>> obj.method1()
    >>> obj.method2()
    P.S.
    По-моему, ваш вопрос, всё же, скорее по питону в целом. А это форум о Django.
  3. Спасибо. Спросил в этой верке наверно по привычке.
    С помощью декоратора я хочу сделать возможность вести историю по любой модели. Пока экспериментирую.
  4. astur.net.ru

    21.12.2009

    0 ↑
    0 ↓

    По-моему, ваш вопрос, всё же, скорее по питону в целом. А это форум о Django.

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

  5. Maximbo

    21.12.2009

    0 ↑
    0 ↓

    Вроде Иван говорил про питон тоже сюда писать, чтобы отдельную ветку в форуме не заводить?..

    Может и так...

    http://softwaremaniacs.org/forum/python/17329/#96051

  6. Иван Сагалаев

    21.12.2009

    0 ↑
    0 ↓

    Я, честно говоря, сам пока не определился :-)

  7. hardtop

    22.12.2009

    0 ↑
    0 ↓
    Питон 2.5 не поддерживает синтаксис декорирования классов...
    Вроде, это 2.3 не поддерживает короткую запись декораторов. В 2.4 и 2.5 точно есть. Есть классический пример из Learning Python - только что проверил на 2.5.1:
    class tracer:
    def __init__(self, func):
    self.calls = 0
    self.func = func
    def __call__(self, *args):
    self.calls += 1
    print 'call %s to %s ' % (self.calls, self.func.__name__)
    self.func(*args)

    @tracer
    def spam(a, b, c):
    print a, b, c

    if __name__ == '__main__':
    spam(1, 2, 3)
    spam('a', 'b', 'c')
    Собственно, выведет:
    call 1 to spam
    1 2 3
    call 2 to spam
    a b c
    Я, честно говоря, сам пока не определился :-)
    Иван, а может стоит завести отдельную ветку по Питону? Ну, пусть будет некоторое пересечение с веткой Django, зато удобнее модерировать. Да и найти что-то будет легче.
  8. Maximbo

    22.12.2009

    0 ↑
    0 ↓

    Вроде, это 2.3 не поддерживает короткую запись декораторов. В 2.4 и 2.5 точно есть.

    Вы говорите про декораторы функций, а не классов.

    Есть классический пример из Learning Python - только что проверил на 2.5.1

    В вашем примере создаётся экземпляр класса tracer, вызывающий запомненную функцию в своём методе call, в то время как в исходной задаче требовалось добавить методы к самому классу.

  9. В исходной задаче выявилась пакость. Если у меня в декорируемом классе уже есть method2, то он будет переопределен.
    Для ведения истории я хотел переопределять save() и после его работы записывать историю, но так не прокатит.
    Похоже остается только использование сигналов.
  10. Maximbo

    22.12.2009

    0 ↑
    0 ↓
    Можно попробовать сделать вот так (для python 2.5):
    def change_method1(cls):
    def method1(self, possible, msg=None):
    print u"Это возможно?", possible
    print msg
    original_method(self, possible, msg)
    original_method = cls.method1
    cls.method1 = method1
    return cls

    class Test(object):
    def method1(self, possible, msg=None):
    print u'Вызов старой версии метода'

    Test = change_method1(Test)

    if __name__ == '__main__':
    obj = Test()
    obj.method1(True, msg=u"Оно вам надо?")
  11. Дмитрий

    22.12.2009

    0 ↑
    0 ↓
    Вадим, а Вы не смотрели в сторону сигналов?
  12. django-reversion работает как раз на сигналах

  13. hardtop

    23.12.2009

    0 ↑
    0 ↓
    Вы говорите про декораторы функций, а не классов.
    Да, виноват — поспешил... (вокруг смех, веселье)
  14. Дмитрий, я проводил испытания и выяснил что сигналы в 4-5 раз медленнее прямого выхова + каждый новый подписчик увеличивает стоимость вызова в 1.5 раза. Причем если использовать слабые ссылки connect(weak = True), то стоимость еще возрастает в 2 раза.
  15. sorgoz.ya.ru

    25.12.2009

    0 ↑
    0 ↓
    Если нужно переопределить save() для модели, то задайте в классе модели этот метод, пусть он выполняет нужные вам операции, после чего вызовите из него super(..).save(..):

    super(ModelClass, self).save(force_insert, force_update)

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.