Python декоратор как статический метод

Я пытаюсь написать класс Python, который использует функцию декоратора, которая нуждается в информации о состоянии экземпляра. Это работает как задумано, но если я явно сделаю декоратор staticmetod, я получу следующую ошибку:

Traceback (most recent call last):
  File "tford.py", line 1, in <module>
    class TFord(object):
  File "tford.py", line 14, in TFord
    @ensure_black
TypeError: 'staticmethod' object is not callable

Зачем?

Вот код:

class TFord(object):
    def __init__(self, color):
        self.color = color

    @staticmethod
    def ensure_black(func):
        def _aux(self, *args, **kwargs):
            if self.color == 'black':
                return func(*args, **kwargs)
            else:
                return None
        return _aux

    @ensure_black
    def get():
        return 'Here is your shiny new T-Ford'

if __name__ == '__main__':
    ford_red = TFord('red')
    ford_black = TFord('black')

    print ford_red.get()
    print ford_black.get()

И если я просто уберу строку @staticmethod, все работает, но я не понимаю почему. Разве это не нужно self в качестве первого аргумента?

3 ответа

Решение

Это не как staticmethod должен быть использован. staticmethod объекты - это дескрипторы, которые возвращают обернутый объект, поэтому они работают только при доступе как classname.staticmethodname, пример

class A(object):
    @staticmethod
    def f():
        pass
print A.f
print A.__dict__["f"]

печать

<function f at 0x8af45dc>
<staticmethod object at 0x8aa6a94>

Внутри сферы AВы всегда получите последний объект, который не вызывается.

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

Классы Python создаются во время выполнения после оценки содержимого объявления класса. Класс оценивается путем присвоения всех объявленных переменных и функций специальному словарю и использования этого словаря для вызова type.__new__ (см. настройку создания класса).

Так,

class A(B):
    c = 1

эквивалентно:

A = type.__new__("A", (B,), {"c": 1})

Когда вы аннотируете метод с помощью @staticmethod, после создания класса создается особая магия type.__new__, Внутри области объявления класса функция @staticmethod является просто экземпляром объекта staticmethod, который вы не можете вызвать. Декоратор, вероятно, должен быть объявлен выше определения класса в том же модуле ИЛИ в отдельном модуле "decorate" (зависит от того, сколько у вас декораторов). В общем случае декораторы должны быть объявлены вне класса. Одним заметным исключением является класс свойств (см. Свойства). В вашем случае наличие декоратора внутри объявления класса может иметь смысл, если у вас есть что-то вроде цветового класса:

class Color(object):

    def ___init__(self, color):
        self.color = color

     def ensure_same_color(f):
         ...

black = Color("black")

class TFord(object):
    def __init__(self, color):
        self.color = color

    @black.ensure_same_color
    def get():
        return 'Here is your shiny new T-Ford'

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

Проблема в том, что статический метод, который пытается использовать в качестве декоратора, на самом деле является объектом staticmethod и не вызывается.

Решение: объект staticmethod имеет метод__get__который принимает любой аргумент и возвращает реальный метод: документация Python Python 3.5 и выше:

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f

Минимальное решение, с которым я пришел:

class A():
    def __init__(self):
        self.n =  2

    @staticmethod
    def _addtoglobaltime(func):
        from functools import wraps
        @wraps(func)
        def wrapper(*args, **kwargs):
            self = args[0]
            response = func(*args, **kwargs)
            return self.n, response
        return wrapper

    @_addtoglobaltime.__get__('this can be anything')
    def get(self):
        return self.n**2

if __name__ == '__main__':
    a = A()
    print(a.get())

Напечатает (2, 4)

ensure_black возвращает _aux метод, который не украшен @staticmethod

Вы можете вернуть нестатический метод в static_method

http://docs.python.org/library/functions.html

Другие вопросы по тегам