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