Not possible to hide local variables

Steven D'Aprano steve+comp.lang.python at pearwood.info
Tue Apr 28 04:37:36 EDT 2015


On Tuesday 28 April 2015 17:33, Cecil Westerhof wrote:

> If I remember correctly you can not hide variables of a class or make
> them read-only?

In Python circles, the preferred terminology for class and instance members 
is "attributes" rather than variables. "Variable" is reserved for module- 
and function-level name bindings.

But other than that, you are correct. Python practices attribute hiding by 
convention. Names beginning with a single underscore like _spam or obj._eggs 
are treated as private.

Names beginning with two underscores, but not trailing with underscores, are 
also name-mangled: obj.__eggs will be mangled to obj._TheClass__eggs. You 
should avoid name-mangling unless you really need it, don't use it "just in 
case".

The convention is, if the caller messes with your private attributes or 
variables, and their code breaks, they have nobody to blame but themselves, 
and we are allowed to laugh at them. We're consenting adults here.

(The only exception is C extension objects and built-ins, where messing with 
private data could cause a segfault.)


> I want to rewrite my moving average to python. The init is:
>     def __init__(self, length):
>         if type(length) != int:
>             raise ParameterError, 'Parameter has to be an int'
>         if n < 0:
>             raise ValueError, 'Parameter should be greater or equal 2'
>         self.length             = length
>         self.old_values         = []
>         self.current_total      = 0
> 
> But when someone changes length, old_values, or current_total that
> would wreck havoc with my class instance. What is the best way to
> handle this?

Don't use a class at all. Moving average is best handled as a generator. We 
can use a regular generator:


import collections, itertools

def moving_average(data, window=3):
    """Iterate over data, yielding the simple moving average with a fixed
    window size.

    With a window size of N (defaulting to three), the simple moving average
    yields the average of items data[0:N], data[1:N+1], data[2:N+2], ...

    >>> list(moving_average([40, 30, 50, 46, 39, 44]))
    [40.0, 42.0, 45.0, 43.0]

    """
    it = iter(data)
    d = collections.deque(itertools.islice(it, window))
    if len(d) != window:
        raise ValueError('too few data points for given window size')
    s = sum(d)
    yield s/window
    for x in it:
        s += x - d.popleft()
        d.append(x)
        yield s/window



Being an iterator, it can consume data points one at a time and return 
results as needed, or here is an example of consuming all the data greedily:

py> data = [1, 2, 3, 2, 3, 5, 4, 7, 3]
py> list(moving_average(data))
[2.0, 2.3333333333333335, 2.6666666666666665, 3.3333333333333335, 4.0, 
5.333333333333333, 4.666666666666667]



If you must use a class, flag the internal attributes as private with a 
leading underscore. Your callers will respect that, and if they don't, all 
promises are null and void.


-- 
Steve




More information about the Python-list mailing list