在Python类中使用对象变量作为递归方法参数
我正在尝试在一个类里面写一个递归函数,但在用对象变量作为方法参数时遇到了一些麻烦:
class nonsense(object):
def __init__(self, val):
self.val = val
def factorial(self, n=self.val):
if n<=1: return 1
return n*self.factorial(n=n-1)
上面的代码会产生以下错误:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in nonsense
NameError: name 'self' is not defined
但是如果我不提到 self.val,这个错误就消失了,虽然这样指定 n 是多余的:
class nonsense(object):
def __init__(self, val):
self.val = val
def factorial(self, n):
if n<=1: return 1
return n*self.factorial(n=n-1)
那正确的做法是什么呢?
4 个回答
1
这个奇怪行为的原因是,def
语句只在函数定义的时候执行一次。所以,初始化的值只会在那时创建,而那时还没有 self
的引用。
作为一种替代方案,可以试试这个:
class nonsense(object):
def __init__(self, val):
self.val = val
def factorial(self, n=None):
return self.factorial_aux(n if n is not None else self.val)
def factorial_aux(self, n):
if n <= 1:
return 1
return n * self.factorial(n-1)
上面的解决方案只会检查一次 n
参数是否是默认值(None
),然后它会返回调用 factorial_aux
(实际执行工作的函数)时传入的合适参数的结果。
6
默认参数是在定义方法的时候就被计算出来的。因此,实际上在这个时候使用在 __init__
里定义的成员值已经“太晚”了。你应该做的是把默认值设置为 None
,然后在函数内部检查这个值:
class nonsense(object):
def __init__(self, val):
self.val = val
def factorial(self, n=None):
if n is None:
n = self.val
elif n <= 1:
return 1
return n*self.factorial(n-1)
3
正如你发现的那样,在类的头部不能使用 self.xxx
,而是要用 None,然后在主体部分进行修正:
def factorial(self, n=None):
if n is None: n = self.val
if n<=1: return 1
return n*self.factorial(n=n-1)
原因是,当类对象正在被创建时,self
还不存在;除了 globals()
,在 Python 到达 factorial
时,唯一定义的名字是 __module__
和 __init__
。
为了证明这一点,你可以尝试做一个实验:
class TestClassCreation(object):
print("Started creating class")
print("names so far: %s" % vars())
def __init__(self):
pass
print("now we have %s" % vars())
def noop(self, default=None):
print("this gets run when noop is called")
print("and now have %s" % vars())
print()
print("and now we'll fail...")
def failure(self, some_arg=self.noop):
pass
print("we never get here...")