3

I am trying to write a recursive function within a class, but have some trouble using an object var as a method argument:

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)

The code above generates the following error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in nonsense
NameError: name 'self' is not defined

But if I don't refer to self.val, the error disappears, although having to specify n is redundant:

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)

What is the right way of doing this?

4 Answers 4

6

Default parameters are evaluated when the method is defined. Thus, it's essentially "too late" to use a member value defined in __init__. What you should do is set the default value to None and test for this in the body of the function:

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)
Sign up to request clarification or add additional context in comments.

Comments

3

As you have discovered, you cannot have self.xxx in the header -- rather have None and then correct in the body:

def factorial(self, n=None):
    if n is None: n = self.val
    if n<=1: return 1
    return n*self.factorial(n=n-1)

The reason is that when the class object is being created there is no self; besides globals(), the only names defined when Python gets to factorial are __module__, and __init__.

As an experiment to prove this to yourself, try this:

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...")

Comments

1

The reason for the strange behavior is that the def statement only gets executed once, when the function is defined. Thus the initializer value only gets created once, at a time when there isn't yet a self reference.

As an alternative, try this:

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)

The above solution only tests once if the n parameter has the default value (None) and after that, it returns the result of calling factorial_aux (which does the actual work) with the appropriate argument.

Comments

0

why not write is simply as this?

class fact1():  
    def fact2(self, num):
        if num==1:
            return 1
        ss=num*self.fact2(num-1)
        return ss

exx=fact1()
print exx.fact2(5)

Output 120

1 Comment

Because this isn't simple and clear. Compare fact(5) vs fact1().fact2(5).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.