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

Я знаю, что есть похожий вопрос, но мой сценарий несколько отличается: обратитесь к кодам:

class MyClass(object):
    def __init__(self, log_location)
        self.logs = logging(log_location) # create log object by the log_location, this object should be used by the decorator fucntion

    def record_log(log_object): 
        """ this is the decorator function
        """
        def deco(func):
            def wrap(*args, **kwargs):
                rs = func()

                # use log object to record log
                if rs:
                    log_object.record('success')
                else:
                    log_object.record('fail')

            return wrap
        return deco

   @record_log(self.logs) 
   def test(self):
       rs = do_some_thing
       if rs:
            return True
       return False

def main():
    my_class = MyClass()
    my_class.test()   

Но есть такая ошибка:

@record_log(self.logs)
NameError: name 'self' is not defined

Должен ли я использовать атрибут экземпляра self.logs в функции декоратора в таком сценарии?

Большое спасибо!


person Spybdai    schedule 22.04.2015    source источник
comment
затем он скажет мне, что имя «журналы» не определено.   -  person Spybdai    schedule 22.04.2015
comment
тогда как мне передать ему self.logs?   -  person Spybdai    schedule 22.04.2015
comment
использование @decorator отсутствует   -  person Zaaferani    schedule 22.04.2015


Ответы (2)


Есть несколько возражений по поводу вашего кода:

  1. deco() является избыточным. Вы можете напрямую вернуть wrap из record_log().

  2. Если вы планируете украшать только методы MyClass, то нет смысла передавать декоратору log_object, так как всегда будет использоваться self.logs. В противном случае рассмотрите возможность перемещения декоратора на уровень модуля, как уже предлагалось другими.

  3. Возвращаемое значение декорированного метода в настоящее время потеряно.

  4. Вызов украшенной функции не передает self ей.

Таким образом, правильный код будет таким:

class MyClass(object):
    def __init__(self, log_location):
        self.logs = logging(log_location)

    def record_log(func):
        """ this is the decorator function
        """
        def wrap(self):
            rs = func(self)
            # use log object to record log
            if rs:
                print 1
                self.logs.record('success')
            else:
                print 2
                self.logs.record('fail')
            return rs
        return wrap

    @record_log
    def test(self):
       rs = do_some_thing
       if rs:
            return True
       return False
person Vadim Landa    schedule 22.04.2015
comment
Хорошо... но я думаю, что вы должны использовать *args, **kwargs в качестве параметров и получать self из args[0], чтобы декоратор был применим к другим методам класса. - person tobias_k; 22.04.2015
comment
Спасибо Вадим за подробные комментарии по поводу кода и спасибо за решение. Большое спасибо @tobias_k, но это решение лучше подходит для моего сценария. спасибо всем ребятам за помощь!!! - person Spybdai; 22.04.2015
comment
Не забывайте также учитывать комментарии @tobias_k :) - person Vadim Landa; 22.04.2015
comment
Привет @VadimLanda, еще 2 вопроса: 1. нет параметра self для функции rocord_log, это статический метод? Если нет, то в чем разница между ним и статическим методом, обернутым 'staticmethod'? 2. как вы упомянули в 3-м пункте, возвращаемое значение декорированного метода в настоящее время потеряно, почему в декораторе должно быть возвращаемое значение? - person Spybdai; 23.04.2015
comment
1. Технически record_log является статическим методом, однако вы не можете использовать с ним @staticmethod - см. stackoverflow.com/questions/6412146/ для получения подробной информации. 2. Поскольку декораторы являются обертками функций, в большинстве случаев вам все равно нужно знать результат исходной функции. Конечно, вы можете отказаться от этого в декораторе, если вам это не нужно. - person Vadim Landa; 23.04.2015

На данный момент вы не можете передать ссылку на self или любой атрибут self. Строка @record_log выполняется (декорируется метод) до того, как будет выполнен код в main, то есть до того, как вообще будет создан какой-либо экземпляр MyClass — фактически, даже до того, как определение MyClass будет завершено! Но помните, что

@record_log(self.logs) 
def test(self, n):

на самом деле просто синтаксический сахар для

test = record_log(self.logs)(test)

Таким образом, одним из способов решения вашей проблемы было бы переопределить test в вашем __init__, т.е.

def __init__(self, log_location)
    self.logs = logging(log_location)
    self.test = record_log(self.logs)(self.test)

Также обратите внимание, что ваш декоратор не передает никаких параметров в func и не возвращает результаты. Кроме того, его, вероятно, следует определить на уровне модуля (перед классом).

def record_log(log_object): 
    def deco(func):
        def wrap(*args, **kwargs):
            rs = func(*args, **kwargs)   # pass parameters
            if rs:
                log_object.record('success')
            else:
                log_object.record('fail')
            return rs   # return result
        return wrap
    return deco
person tobias_k    schedule 22.04.2015
comment
Привет, tobias_k, спасибо за помощь. Теперь это работает, хотя и немного уродливо. И еще вопрос: поскольку @record_log(self.logs) def test(self, n): на самом деле просто синтаксический сахар for test = record_log(self.logs)(test) почему один работает, а другой нет?? ? - person Spybdai; 22.04.2015
comment
@Spybdai Потому что, как я уже сказал, оператор @record_log выполняется до еще до того, как класс будет полностью создан, а self.test = ... выполняется при создании экземпляра класса. - person tobias_k; 22.04.2015