2014-02-11 2 views
2

Я надеялся, что это будет работать:призывающие Python статические методы объектов

class A(object): 

    @classmethod 
    def do_it(cls, spam, eggs): 
     if spam in A.ways_to_do_it: 
      A.ways_to_do_it[spam](eggs) 
     super(A, cls).do_it(spam, eggs) 

    @staticmethod 
    def do_it_somehow(eggs): 
     ... 

    @staticmethod 
    def do_it_another_way(eggs): 
     ... 

    ways_to_do_it = { 
     'somehow': do_it_somehow, 
     'another_way': do_it_another_way, 
    } 

Но это поднимает TypeError: 'staticmethod' object is not callable. Я хотел проверить staticmethod, чтобы узнать что-то, но он встроен. Надеюсь, что я понял, чего я хочу достичь здесь.

Есть ли у вас идеи, как это сделать? Я знаю, что создание этих @staticmethod s глобально решит проблему, но это будет беспорядок в моем модуле.

P. S. do_it_somehow и do_it_another_way будет называться только от A.do_it.

+1

Что касается тех, кто пытается отметить этот вопрос как дубликат: я думаю, что этот вопрос является более общим случаем, чем упоминаемый, - это угловой случай, изображающий ту же проблему, что и у ОП. – jsbueno

+0

@jsbueno: более общий случай также рассматривается в дубликатах. –

ответ

4

Python имеет концепцию дескриптор объекты, являющиеся объектами, имеющими как минимум метод __get__. Эти объекты ведут себя по-разному при извлечении из класса, или, например, в качестве атрибутов (метод их __get__ называется.)

@staticmethod декоратор преобразует последующее объявление функции в дескрипторе, который имеет статическое поведение метода - но сказали, что поведение будет доступны только при извлечении объекта в качестве атрибута класса. Приведенный выше код прямо ссылается на объект, как он есть.

Поскольку у вас также есть другие методы (класс) для словаря работать, вы бы лучше получить yoiur нужных метод после создания класса, так что каждый метод извлекается с помощью протокола дескриптора:

class A(object): 

    @classmethod 
    def do_it(cls, spam, eggs): 
     if spam in A.ways_to_do_it: 
      A.ways_to_do_it[spam](eggs) 
     super(A, cls).do_it(spam, eggs) 

    @staticmetod 
    def do_it_somehow(eggs): 
     ... 

    @staticmetod 
    def do_it_another_way(eggs): 
     ... 

A.ways_to_do_it = { 
     'somehow': A.do_it_somehow, 
     'another_way': A.do_it_another_way, 
    } 

Вы можете получить свой статический метод до создания класса, вызвав do_it_another_way.__get__(object, None) - так как ему не нужна ссылка на класс (но тем не менее он должен иметь действительный класс как первый параметр). Но если вы хотите, чтобы ваш словарь также указывал на определенные классы, они обязательно должны быть извлечены после создания класса: нет способа, которым Python может создать вам метод «связанного класса» до создания класса.

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

class A(object): 
    @staticmethod 
    def foo(): ... 

    bar = foo 

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

Для получения дополнительной информации отметьте http://docs.python.org/2/reference/datamodel.html#implementing-descriptors. (Это то, что делают classmethod, декораторы статических методов, так как вы также хотели узнать).

+1

И в Python 3 функции _all_ (не встроенные) являются дескрипторами. 'class A: pass; a = lambda self: id (self); A.a = a' приведет к тому, что 'a' является функцией объекта класса и связанного метода для любых экземпляров (даже те, которые были созданы до того, как функция была установлена ​​как атрибут класса, предполагая, что они уже не скрывали функцию) , и вы даже можете получить эквивалент JavaScript 'bind' с явным использованием' __get__': 'anA = A(); aa = a .__ get __ (anA); аа() '. – JAB

2

Попробуйте так:

class A(object): 

    @classmethod 
    def do_it(cls, spam, eggs): 
     if spam in A.ways_to_do_it: 
      A.ways_to_do_it[spam](eggs) 
     super(A, cls).do_it(spam, eggs) 

    @staticmethod 
    def do_it_somehow(eggs): 
     ... 

    @staticmethod 
    def do_it_another_way(eggs): 
     ... 

A.ways_to_do_it = { 
    'somehow': A.do_it_somehow, 
    'another_way': A.do_it_another_way, 
} 

Это становится сложно ссылаться на класс, прежде чем вы завершили строительство класса, так что проще добавить материал к нему сразу после окончания определения класса.

+0

Ваш ответ заставляет его работать и описывает высокий уровень того, почему он работает для classmethods. Но staticmethods не нуждался бы в ссылке на класс, поэтому на них можно было бы ссылаться, прежде чем класс будет готов. Проверьте мой ответ, чтобы узнать, что происходит. – jsbueno

Смежные вопросы