【问题标题】:Using class/static methods as default parameter values within methods of the same class在同一类的方法中使用类/静态方法作为默认参数值
【发布时间】:2011-03-06 06:00:37
【问题描述】:

我想做这样的事情:

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 函数。这意味着它仅在作为对象属性引用时以“正常”方式工作。有问题的对象是由装饰器在SillyWalk 的is_silly_enough 属性和最初使用该名称定义的函数之间放置的staticmethod() 函数创建的。

这意味着为了在 either SillyWalk.walk 或它的调用者中使用 appraisal_method 的默认值,我们必须要么

  • 调用appraisal_method.__get__(instance, owner)(...) 而不是仅仅调用appraisal_method(...)
  • 或将其分配为某个对象的属性,然后将该对象属性作为我们调用的方法引用,就像我们将调用 appraisal_method 一样。

鉴于这两种解决方案似乎都不是 Pythonic™,我想知道是否有更好的方法来获得这种功能。我本质上想要一种方法来指定一个方法默认情况下应该使用在同一类范围内定义的特定类或静态方法来执行其日常工作的某些部分。

我不想使用None,因为我想让None 传达不应调用该特定函数的信息。我想我可以使用一些其他值,例如False 或NotImplemented,但看起来 a) 很麻烦 b) 不得不编写额外的几行代码以及其他冗余文档,这看起来很烦人似乎可以非常简洁地表示为默认参数。

最好的方法是什么?

【问题讨论】:

    标签: python decorator default-value class-method static-methods


    【解决方案1】:

    不确定我是否能得到你想要的,但使用 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
    

    【讨论】:

    • 可能是一种解决方法,我猜。我希望能够通过传递一个可调用对象来覆盖默认值,并且我还希望默认情况下最快。同样,“ise”意味着静态方法的名称也不是很明显。我想我正在寻找一个名为undecorate_static_method 的函数。我想我可以写,并包含在我想在其中使用此模式的每个模块中......以便能够制作 def 行 def walk(self, am=undecorate_static_method(ise)):
    【解决方案2】:

    我最终编写了一个 (un)wrapper 函数,用于函数定义标头中,例如

    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 函数本身编写了文档测试;他们也通过了。我仍然不能完全确定这是否是一件真正明智的事情,但它似乎确实有效。

    【讨论】:

      【解决方案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)
      

      请注意,appearance_function 现在将是一个函数而不是一个方法,即使 is_silly_enough 将在创建类后绑定为类方法(在代码末尾)。

      这意味着

      >>> 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>
      

      您可以使用 walk 参数调用 is_silly_enough,或者使用 .is_silly_enough() 调用 walk 实例。

      如果你真的想让 is_silly_enough 成为一个静态方法,你可以随时添加

          is_silly_enough = staticmethod(is_silly_enough)
      

      walk 定义之后的任何地方。

      【讨论】:

      • +1 这与我的想法非常接近。不过,我更多地考虑了一般情况,is_silly_walk 就像典型的静态方法一样,不接受实例类型的参数。我想我可以在声明walk 之后做类似is_silly_enough = staticmethod(functools.partial(is_silly_enough, None)) 的事情来提供无关参数,这样外部调用者就不必这样做了。我想缺点是函数签名将不再作为准确的文档。
      • 抱歉,最后的评论没有意义。将staticmethod(...) 调用放置在使用静态待定作为默认参数值的函数声明之后,这可以完美地工作。谢谢。
      • 有人可以评论这个答案和 OP 的答案(下)之间的优缺点吗?我发布了一个关于它的问题,但没有答案...stackoverflow.com/questions/3971490/… 谢谢!
      • 我认为OP使用的是英文意义上的“method”,而不是OOP意义上的,即“the method of evaluation”变成了“appraisal_method”,所以不用称它为appraisal_function。
      猜你喜欢
      • 1970-01-01
      • 2019-05-20
      • 2012-03-13
      • 1970-01-01
      • 1970-01-01
      • 2019-03-22
      • 2013-09-06
      • 2013-02-17
      • 1970-01-01
      相关资源
      最近更新 更多