【问题标题】:The best way to invoke methods in Python class declarations?在 Python 类声明中调用方法的最佳方式是什么?
【发布时间】:2008-11-20 08:40:40
【问题描述】:

假设我声明了一个类C,其中一些声明非常相似。我想使用函数f 来减少这些声明的代码重复。可以像往常一样声明和使用f

>>> class C(object):
...     def f(num):
...             return '<' + str(num) + '>'
...     v = f(9)
...     w = f(42)
... 
>>> C.v
'<9>'
>>> C.w
'<42>'
>>> C.f(4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f() must be called with C instance as first argument (got int instance instead)

哎呀!我无意中将f 暴露给了外界,但它不需要self 参数(并且不能出于明显的原因)。一种可能性是在我使用该功能后del

>>> class C(object):
...     def f(num):
...             return '<' + str(num) + '>'
...     v = f(9)
...     del f
... 
>>> C.v
'<9>'
>>> C.f
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'C' has no attribute 'f'

但是如果我想在声明之后再次使用f 怎么办?删除该功能是不行的。我可以将其设为“私有”(即在其名称前加上 __)并对其进行 @staticmethod 处理,但通过异常通道调用 staticmethod 对象会变得非常时髦:

>>> class C(object):
...     @staticmethod
...     def __f(num):
...             return '<' + str(num) + '>'
...     v = __f.__get__(1)(9)   # argument to __get__ is ignored...
... 
>>> C.v
'<9>'

我必须使用上面的疯狂,因为staticmethod 对象,它们是描述符,它们本身是不可调用的。我需要恢复被staticmethod 对象包裹的函数,然后才能调用它。

必须有更好的方法来做到这一点。如何在类中干净地声明一个函数,在其声明期间使用它,并稍后在类中使用它?我应该这样做吗?

【问题讨论】:

    标签: python class declaration static-methods invocation


    【解决方案1】:

    很简单,解决方案是 f 不需要是类的成员。我假设您的思维过程已经通过了 Java 语言过滤器,从而导致了心理障碍。它有点像这样:

    def f(n):
        return '<' + str(num) + '>'
    
    class C(object):
    
        v = f(9)
        w = f(42)
    

    那么当你想再次使用 f 时,就使用它

    >>> f(4)
    '<4>'
    

    我认为这个故事的寓意是“在 Python 中,您没有将所有内容强制归为一个类”。

    【讨论】:

    • 我同意,f 似乎与 C 无关,所以为什么要把它放在类中
    • 感谢现实检查;您可能是对的,最简单的解决方案就是将f 放在外面。我将它放在类中的本能是 f 与类及其声明的密切联系的结果——我不想用它来打扰模块命名空间。
    • 我认为这就是模块命名空间的用途。如果您想表明使用该模块的人不应该被 f 打扰,请在名称前加上下划线 (_f)。
    • 同意。模块中的 def _f 将是正常的简单 Python 方式。您可以跳过箍将其绑定到类中并隐藏它,但它并不能真正为您带来任何东西,所以除非对于简单的解决方案有特别不寻常的要求。
    【解决方案2】:

    扩展Ali A的回答, 如果你真的想避免在模块命名空间中使用 f(并且使用像 _f 这样的非导出名称,或者设置 __all__ 是不够的),那么 您可以通过在闭包中创建类来实现这一点。

    def create_C():
        def f(num):
            return '<' + str(num) + '>'
    
        class C(object):
            v = f(9)
            def method_using_f(self, x):  return f(x*2)
        return C
    
    C=create_C()
    del create_C
    

    这样,C 可以在其定义和方法中访问 f,但没有其他方法可以访问(除非相当复杂的内省 它的方法 (C.method_using_f.im_func.func_closure))

    这对于大多数用途来说可能是多余的 - 通过使用“_”前缀命名约定来记录 f 是内部的应该 一般就足够了。

    [编辑] 另一种选择是在您希望使用它的方法中保存对预包装函数对象的引用。例如,将其设置为默认参数:

    class C(object):
        def f(num):
            return '<' + str(num) + '>'
    
        v = f(9)
        def method_using_f(self, x, f=f):  return f(x*2)
    
        del f
    

    (虽然我认为关闭方法可能更好)

    【讨论】:

      【解决方案3】:

      我相信您正在尝试这样做:

      class C():
      ...     class F():
      ...         def __call__(self,num):
      ...             return "<"+str(num)+">"
      ...     f=F()
      ...     v=f(9)
      >>> C.v
      '<9>'
      >>> C.f(25)
      '<25>'
      >>> 
      

      也许有更好或更多的pythonic解决方案......

      “在一个类中声明一个函数,在它的声明过程中使用它,并且稍后在类中使用它”

      对不起。做不到。

      “做不到”似乎和 Python 不搭调

      【讨论】:

      • “无法完成”——如前所述。您的解决方案 - 巧妙地 - 达到了预期的结果,但它的方式与所说的不同。与您的聪明方法不同,OP中所述的方式无法完成。
      【解决方案4】:

      这是一种可能性:

      class _C:
          # Do most of the function definitions in here
          @classmethod
          def f(cls):
              return 'boo'
      
      class C(_C):
          # Do the subsequent decoration in here
          v = _C.f()
      

      【讨论】:

        【解决方案5】:

        一个选项:写一个更好的staticmethod

        class staticfunc(object):
            def __init__(self, func):
                self.func = func
            def __call__(self, *args, **kw):
                return self.func(*args, **kw)
            def __repr__(self):
                return 'staticfunc(%r)' % self.func
        

        【讨论】:

          【解决方案6】:

          让我们从头开始。

          “在一个类中声明一个函数,在它的声明过程中使用它,并且稍后在类中使用它”

          对不起。做不到。 “在类中”与“在声明期间使用”相矛盾。

          • 在类中表示作为声明的一部分创建。
          • 在声明期间使用意味着它存在于类之外。通常作为一个元类。但是,还有其他方法。

          不清楚 C.w 和 C.v 应该是什么。它们只是字符串吗?如果是这样,外部函数f 是最好的解决方案。 “不要使命名空间混乱”有点似是而非。毕竟,你想再次使用它。

          它与 C 在同一个模块中。这就是 Python 具有模块的原因。它将函数和类绑定在一起。

          import myCmod
          
          myCmod.C.w
          myCmod.C.v
          myCmod.f(42)
          

          如果 w 和 v 不是简单的字符串,那么有一个非常好的解决方案可以提供很大的灵活性。

          一般来说,对于像这样的类级别(“静态”)变量,我们可以使用其他类。完全实现所需的 API 是不可能的,但这已经很接近了。

          >>> class F(object):
              def __init__( self, num ):
                  self.value= num
                  self.format= "<%d>" % ( num, )
          
          >>> class C(object):
              w= F(42)
              v= F(9)
          
          >>> C.w
          <__main__.F object at 0x00C58C30>
          >>> C.w.format
          '<42>'
          >>> C.v.format
          '<9>'
          

          这样做的好处是 F 是一个适当的、可以扩展的一流的东西。不是我们试图避免暴露的“隐藏”事物。这是生活中的一个事实,所以我们不妨遵循 Open/Closed 原则,让它对扩展开放。

          【讨论】:

          • 实际上完全可以在类声明中使用类中声明的函数。它尚未附加到类,但确实存在于类定义命名空间中。这用于以编程方式生成方法(之后删除辅助函数)
          • @Brian:喜欢看一个例子。也许你可以用这个更新你的例子。
          • 一个例子是在一个类的for循环中创建setter和getter。例如 for name in ['foo','bar',...]:定义一个 getter 函数,使用它们创建属性,然后在最后删除临时 getter,只留下属性。
          • (已编辑)这是一个这样做的例子:scratchpad.cmlenz.net/4821326a0f68c52ba236bf7ea4b4af6b
          • 我很困惑。 Brian 的例子是 adrian 的例子 #2。然而,最初的问题说这是不可接受的。那我想我不明白这个问题。
          猜你喜欢
          • 2011-05-26
          • 2012-07-13
          • 1970-01-01
          • 2016-09-18
          • 2011-03-19
          • 2016-05-19
          • 2021-06-10
          • 2015-08-08
          • 1970-01-01
          相关资源
          最近更新 更多