在 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 参数(出于明显的原因不能接受)。一种可能性是在使用 f 之后将其删除:
>>> 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 对象包装的函数。
- 解决方案
答案1:
最简单的解决方案是,f 不需要成为类的成员。我们可以直接使用函数 f:
def f(n):
return '<' + str(num) + '>'
class C(object):
v = f(9)
w = f(42)
当我们想要再次使用 f 时,直接使用即可:
>>> f(4)
'<4>'
答案2:
如果我们真的想避免在模块命名空间中使用 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,但其他任何东西都不能访问 f(除非相当复杂地内省其方法 (C.method_using_f.im_func.func_closure))。不过,这对于大多数目的来说可能有点过分——通常使用 “_” 前缀命名约定来记录 f 是内部的应该是足够的。
答案3:
我们可以尝试以下方法:
class C():
class F():
def __call__(self,num):
return "<"+str(num)+">"
f=F()
v=f(9)
然后我们就可以使用 C.v 和 C.f(25) 了。
答案4:
另一种可能性是编写一个更好的 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
现在我们可以像这样使用它:
class C(object):
@staticfunc
def f(cls):
return 'boo'
C.f() # 'boo'
答案5:
我们还可以直接使用函数 f:
def f(num):
return '<' + str(num) + '>'
class C(object):
v = f(9)
然后我们就可以使用 C.v 了。