在Python类声明中调用方法的最佳方式是什么?
假设我在声明一个类 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 个回答
我觉得你想要做的是这个:
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不太搭。
在扩展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
(不过我觉得闭包的方法可能更好)
简单来说,解决办法是 f 不需要是这个类的成员。我猜你的思维方式可能受到了类似 Java 的语言影响,导致了思维上的障碍。大概是这样的:
def f(n):
return '<' + str(num) + '>'
class C(object):
v = f(9)
w = f(42)
然后当你想再次使用 f 时,只需直接使用它就可以了。
>>> f(4)
'<4>'
我觉得这个故事的道理是“在 Python 中,你不需要把所有东西都强行放进一个类里”。