在Python类声明中调用方法的最佳方式是什么?

4 投票
6 回答
4271 浏览
提问于 2025-04-11 18:34

假设我在声明一个类 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 包裹的函数。

肯定有更好的方法来做到这一点。我该如何在类中干净利落地声明一个函数,在声明时使用它,并且在类内部之后也能使用它?我真的应该这么做吗?

6 个回答

2

我觉得你想要做的是这个:

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

也许还有更好的、更符合Python风格的解决办法……

“在一个类里声明一个函数,在声明的时候使用它,然后在类里面再使用它”

抱歉,这个做不到。

“做不到”这个说法似乎和Python不太搭。

3

在扩展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是内部的,通常就足够了。

[编辑] 还有一个选项是,在你想使用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

(不过我觉得闭包的方法可能更好)

14

简单来说,解决办法是 f 不需要是这个类的成员。我猜你的思维方式可能受到了类似 Java 的语言影响,导致了思维上的障碍。大概是这样的:

def f(n):
    return '<' + str(num) + '>'

class C(object):

    v = f(9)
    w = f(42)

然后当你想再次使用 f 时,只需直接使用它就可以了。

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

我觉得这个故事的道理是“在 Python 中,你不需要把所有东西都强行放进一个类里”。

撰写回答