Need help with Python scoping rules

Dave Angel davea at ieee.org
Wed Aug 26 11:13:14 EDT 2009


kj wrote:
> In <7figv3F2m3p0dU1 at mid.uni-berlin.de> "Diez B. Roggisch" <deets at nospam.web.de> writes:
>
>   
>> Classes are not scopes.
>>     
>
> This looks to me like a major wart, on two counts.
>
> First, one of the goals of OO is encapsulation, not only at the
> level of instances, but also at the level of classes.  Your comment
> suggests that Python does not fully support class-level encapsulation.
>
> Second, my example shows that Python puts some peculiar restrictions
> on recursion.  Recursion!  One of the central concepts in the theory
> of functions!  This is shown most clearly by the following elaboration
> of my original example:
>
> class Demo(object):
>     def fact_rec(n):
>         if n < 2:
>             return 1
>         else:
>             return n * fact_rec(n - 1)
>
>   
Why not fix the call?  The correct way to call a method is with either 
the class name or an instance object, depending on whether it's a 
instance method or not.  By default, a method is unbound, and its first 
argument is by convention called self.  If you want to recurse, then add 
self in the appropriate places.

As you have it, neither of the methods can be called outside the class:

class Demo(object):
    def fact_rec(n):
        if n < 2:
            return 1
        else:
            return n * fact_rec(n - 1)
     ....

obj = Demo()
print obj.fact_rec(5)

gives error:

Traceback (most recent call last):
  File "M:\Programming\Python\sources\dummy\stuff2.py", line 20, in <module>
    print obj.fact_rec(5)
TypeError: fact_rec() takes exactly 1 argument (2 given)

To fix it, you need to either change the signature (add in 'self' 
argument before the n argument) or do some form of decorator to the 
function.  If I assume you never intended this method to use 'self'  
(ie. it's a static method), then declare it so.  And call it accordingly.


class Demo(object):
    @staticmethod
    def fact(n):
        if n < 2:
            return 1
        else:
            return n * Demo.fact(n - 1)


print Demo.fact(6)

On the other hand, if I assume you're just giving a simple example of 
what's really intended to be a normal method (called with an instance, 
that it uses), then you'd change it this way.

class Demo2(object):
    def fact(self, n):
        if n<2:
            return 1
        else:
            return n * self.fact(n-1)

obj = Demo2()
print obj.fact(5)

Now the only real restriction, as opposed to all these red-herrings, is 
that the class may not be used before it's complete.  That only comes to 
play when class attributes (non-instance "variables") are defined in 
terms of class methods.  So if you have such attributes to define, move 
them outside of the class, perhaps immediately after it.


class Demo(object):
    @staticmethod
    def fact(n):
        if n < 2:
            return 1
        else:
            return n * Demo.fact(n - 1)
Demo._classvar = Demo.fact(5)

>     def fact_iter(n):
>         ret = 1
>         for i in range(1, n + 1):
>             ret *= i
>         return ret
>
>     classvar1 = fact_iter(5)
>     classvar2 = fact_rec(5)
>
> This code won't compile as shown, 
Sure it will compile.  It just won't run.  You get the error after 
compiling the function when *calling* it from the classvar* line.  But 
at that time the class is not defined, and not everything is ready for use.

You can probably work around this by replacing the staticmethod 
decorator with an equivalent function call:.

class Demo9(object):
    def fact(n):
        if n < 2:
            return 1
        else:
            return n * Demo.fact(n - 1)

    _classvar = fact(5)
    fact = staticmethod(fact)

print Demo9._classvar
xx = Demo9()
print xx.fact(6)
print Demo9.fact(8)
> but it does compile if the last
> line (the call to the recursive fact_rec) is commented out.  There
> is no justification for discriminating against recursive functions
> in this context.  Recursive functions should be OK wherever functions
> are OK.  I can't think of any other language that allows recursion
> but not anywhere.
>
> Is there any good reason (from the point of view of Python's overall
> design) for not fixing this?
>
> kynn
>
>
>   



More information about the Python-list mailing list