Используя класс/статические методы как значения параметров по умолчанию в рамках методов того же класса

Я хотел бы сделать что-то вроде этого:

class SillyWalk(object):
    @staticmethod
    def is_silly_enough(walk):
        return (False, "It's never silly enough")
    def walk(self, appraisal_method=is_silly_enough):
        self.do_stuff()
        (was_good_enough, reason) = appraisal_method(self)
        if not was_good_enough:
            self.execute_self_modifying_code(reason)
        return appraisal_method
    def do_stuff(self):
        pass
    def execute_self_modifying_code(self, problem):
        from __future__ import deepjuju
        deepjuju.kiss_booboo_better(self, problem)

так как идея - это, кто-то может сделать

>>> silly_walk = SillyWalk()
>>> appraise = walk()
>>> is_good_walk = appraise(silly_walk)

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

Так или иначе это не работает, потому что is_silly_enough не на самом деле функция: это - объект чей __get__ метод возвратит оригинал is_silly_enough функция. Это означает, что только работает "нормальным" способом, когда на это ссылаются как атрибут объекта. Рассматриваемый объект создается staticmethod() функция, которую декоратор помещает промежуточный SillyWalk is_silly_enough атрибут и функция это первоначально определяется с тем именем.

Это означает это для использования значения по умолчанию appraisal_method из также SillyWalk.walk или его вызывающая сторона, мы имеем к также

  • звонить appraisal_method.__get__(instance, owner)(...) вместо просто вызова appraisal_method(...)
  • или назначьте его атрибутом некоторого объекта, затем ссылка, что свойство объекта как метод, который мы называем, как мы звонили бы appraisal_method.

Учитывая, что ни одно из этих решений не кажется особенно Pythonic™, я задаюсь вопросом, существует ли, возможно, лучший способ получить этот вид функциональности. Я по существу хочу способ указать, что метод должен, по умолчанию, использовать конкретный класс или статический метод, определенный в рамках того же класса для выполнения некоторой части его распорядка дня.

Я предпочел бы не использовать None, потому что я хотел бы позволить None передать сообщение, что та конкретная функция не должна быть вызвана. Я предполагаю, что мог использовать некоторое другое значение, как False или NotImplemented, но это кажется a) hackety b) раздражающий, чтобы должным быть записать дополнительным нескольким строки кода, а также иначе избыточную документацию, для чего-то, что кажется, что могло быть выражено вполне кратко как параметр по умолчанию.

Что лучший способ состоит в том, чтобы сделать это?

5
задан intuited 21 June 2010 в 10:35
поделиться

3 ответа

Может быть, все, что вам нужно, это в первую очередь использовать функцию (а не метод)?

class SillyWalk(object):
    def is_silly_enough(walk):
        return (False, "It's never silly enough")

    def walk(self, appraisal_function=is_silly_enough):
        self.do_stuff()
        (was_good_enough, reason) = appraisal_function(self)
        if not was_good_enough:
            self.execute_self_modifying_code(reason)
        return appraisal_function
    def do_stuff(self):
        pass
    def execute_self_modifying_code(self, problem):
        deepjuju.kiss_booboo_better(self, problem)

Обратите внимание, что по умолчанию для appraisal_function теперь будет функция, а не метод, даже если is_silly_enough будет привязан как class после создания класса (в конце кода).

Это означает, что

>>> SillyWalk.is_silly_enough
<unbound method SillyWalk.is_silly_enough>

но

>>> SillyWalk.walk.im_func.func_defaults[0] # the default argument to .walk
<function is_silly_enough at 0x0000000002212048>

И вы можете вызвать is_silly_enough с аргументом walk или вызвать экземпляр walk с помощью .is_silly_enough ().

Если вы действительно хотите, чтобы is_silly_enough был статическим методом, вы всегда можете добавить

    is_silly_enough = staticmethod(is_silly_enough)

в любом месте после определения walk.

2
ответ дан 14 December 2019 в 18:58
поделиться

Не уверен, получу ли я именно то, что вам нужно, но было бы чище использовать getattr?

>>> class SillyWalk(object):
    @staticmethod
    def ise(walk):
        return (False, "boo")
    def walk(self, am="ise"):
        wge, r = getattr(self, am)(self)
        print wge, r


>>> sw = SillyWalk()
>>> sw.walk("ise")
False boo
1
ответ дан 14 December 2019 в 18:58
поделиться

Я закончил тем, что написал (не) функцию-оболочку, которая будет использоваться в заголовках определения функции, например

def walk(self, appraisal_method=unstaticmethod(is_silly_enough)):

Это действительно работает, по крайней мере, это заставляет мои doctests, которые ломаются без этого, проходят .

Вот оно:

def unstaticmethod(static):
    """Retrieve the original function from a `staticmethod` object.

    This is intended for use in binding class method default values
      to static methods of the same class.

    For example:
        >>> class C(object):
        ...     @staticmethod
        ...     def s(*args, **kwargs):
        ...         return (args, kwargs)
        ...     def m(self, args=[], kwargs={}, f=unstaticmethod(s)):
        ...         return f(*args, **kwargs)
        >>> o = C()
        >>> o.s(1, 2, 3)
        ((1, 2, 3), {})
        >>> o.m((1, 2, 3))
        ((1, 2, 3), {})
    """
    # TODO: Technically we should be passing the actual class of the owner
    #         instead of `object`, but
    #         I don't know if there's a way to get that info dynamically,
    #         since the class is not actually declared
    #         when this function is called during class method definition.
    #       I need to figure out if passing `object` instead
    #         is going to be an issue.
    return static.__get__(None, object)

обновление:

Я написал тесты для самой функции unstaticmethod ; они тоже проходят. Я до сих пор не совсем уверен, что это действительно разумный поступок, но, похоже, он работает.

2
ответ дан 14 December 2019 в 18:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: