2013-08-06 3 views
7

иногда мне нужно написать класс со статическими методами, однако с возможностью инициализации его и сохранить состояние (объект)питон: гибрид между обычным методом и classmethod

STH как:

class A: 
    @classmethod 
    def method(cls_or_self): 
    # get reference to object when A().method() or to class when A.method() 
    code 

, что У меня есть сейчас:

class A: 
    def method(self = None, *params): code 
    # or 
    def method2(self = None, **params): code 
    # but what I need is rather normal parameters, not optional and named args: 
    def method3(self_or_cls, a, b=1, c=2, *p, **kw): code 

Пожалуйста, не пишите о различиях между staticmethod и classmethod. Меня интересует, существует ли такой декоратор (в более или менее стандартных библиотеках) и, кроме того, если выше подходит для PEP.

+2

Вы имеете в виду гибрид между classmethod и * регулярный метод *. –

ответ

17

Функции и classmethod Объекты действуют как descriptors; оба возвращают обертку, которая при вызове в свою очередь вызывает базовую функцию с дополнительным параметром. Единственное различие между функциями функции и объектами classmethod заключается в том, что этот дополнительный параметр равен.

Чтобы создать гибрид этих двух подходов, построить свой собственный дескриптор декоратора:

from functools import wraps 

class hybridmethod(object): 
    def __init__(self, func): 
     self.func = func 

    def __get__(self, obj, cls): 
     context = obj if obj is not None else cls 

     @wraps(self.func) 
     def hybrid(*args, **kw): 
      return self.func(context, *args, **kw) 

     # optional, mimic methods some more 
     hybrid.__func__ = hybrid.im_func = self.func 
     hybrid.__self__ = hybrid.im_self = context 

     return hybrid 

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

Демо:

>>> class Foo(object): 
...  @hybridmethod 
...  def bar(cls_or_self): 
...   print 'Called with cls_or_self={!r}'.format(cls_or_self) 
... 
>>> Foo.bar() 
Called with cls_or_self=<class '__main__.Foo'> 
>>> Foo().bar() 
Called with cls_or_self=<__main__.Foo object at 0x1043a4390> 
+0

... такой быстрый и удивительный, что я должен ждать, чтобы принять ответ (stackoverflow reqs), но как насчет classmethod? - для меня выше декоратор выглядит очень функционально, даже предлагая переписать клазметод или добавить третий стандартный декоратор для методов. Я знаю, что это требует сильной критики. –

+0

Декоратор 'classmethod' служит другой цели; методы, которые неоднозначны по отношению к их поведению (действуют так или иначе на основе контекста), не слишком питоничны. –