Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #89486 > unrolled thread

Not possible to hide local variables

Started byCecil Westerhof <Cecil@decebal.nl>
First post2015-04-28 09:33 +0200
Last post2015-04-30 16:50 +0200
Articles 19 — 9 participants

Back to article view | Back to comp.lang.python


Contents

  Not possible to hide local variables Cecil Westerhof <Cecil@decebal.nl> - 2015-04-28 09:33 +0200
    Re: Not possible to hide local variables Ethan Furman <ethan@stoneleaf.us> - 2015-04-28 00:56 -0700
      Re: Not possible to hide local variables Cecil Westerhof <Cecil@decebal.nl> - 2015-04-29 08:16 +0200
        Re: Not possible to hide local variables Michael Torrie <torriem@gmail.com> - 2015-04-29 21:10 -0600
    Re: Not possible to hide local variables Chris Angelico <rosuav@gmail.com> - 2015-04-28 18:06 +1000
      Re: Not possible to hide local variables Cecil Westerhof <Cecil@decebal.nl> - 2015-04-29 08:25 +0200
        Re: Not possible to hide local variables Chris Angelico <rosuav@gmail.com> - 2015-04-29 17:36 +1000
    Re: Not possible to hide local variables Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-04-28 18:37 +1000
      Re: Not possible to hide local variables Chris Angelico <rosuav@gmail.com> - 2015-04-28 18:44 +1000
      Re: Not possible to hide local variables Marko Rauhamaa <marko@pacujo.net> - 2015-04-28 12:20 +0300
      Re: Not possible to hide local variables Cecil Westerhof <Cecil@decebal.nl> - 2015-04-29 08:32 +0200
        Re: Not possible to hide local variables Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-04-30 14:05 +1000
          Re: Not possible to hide local variables Marko Rauhamaa <marko@pacujo.net> - 2015-04-30 08:10 +0300
            Re: Not possible to hide local variables Chris Angelico <rosuav@gmail.com> - 2015-04-30 16:01 +1000
    Re: Not possible to hide local variables Cecil Westerhof <Cecil@decebal.nl> - 2015-04-29 08:14 +0200
    Re: Not possible to hide local variables Grant Edwards <invalid@invalid.invalid> - 2015-04-29 14:16 +0000
      Re: Not possible to hide local variables Dave Angel <davea@davea.name> - 2015-04-29 22:31 -0400
      Re: Not possible to hide local variables Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2015-04-30 08:53 -0400
        Re: Not possible to hide local variables Cecil Westerhof <Cecil@decebal.nl> - 2015-04-30 16:50 +0200

#89486 — Not possible to hide local variables

FromCecil Westerhof <Cecil@decebal.nl>
Date2015-04-28 09:33 +0200
SubjectNot possible to hide local variables
Message-ID<874mo0zoz2.fsf@Equus.decebal.nl>
If I remember correctly you can not hide variables of a class or make
them read-only?

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?

-- 
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof

[toc] | [next] | [standalone]


#89488

FromEthan Furman <ethan@stoneleaf.us>
Date2015-04-28 00:56 -0700
Message-ID<mailman.57.1430207777.3680.python-list@python.org>
In reply to#89486
On 04/28, Cecil Westerhof wrote:
> If I remember correctly you can not hide variables of a class or make
> them read-only?
> 
> 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?

Prefix those names with a single leading underscore, which is the convention
for private variables.

This way, if some user (maybe you! ;) has a good reason to change those
values in can be done, but it is quite clear that said user is mucking about
with internals and they are on their own.

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#89523

FromCecil Westerhof <Cecil@decebal.nl>
Date2015-04-29 08:16 +0200
Message-ID<87pp6nxxwe.fsf@Equus.decebal.nl>
In reply to#89488
Op Tuesday 28 Apr 2015 09:56 CEST schreef Ethan Furman:

> On 04/28, Cecil Westerhof wrote:
>> If I remember correctly you can not hide variables of a class or
>> make them read-only?
>>
>> 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?
>
> Prefix those names with a single leading underscore, which is the
> convention for private variables.

Done.


> This way, if some user (maybe you! ;) has a good reason to change
> those values in can be done, but it is quite clear that said user is
> mucking about with internals and they are on their own.

There is something to say for this way of working.

-- 
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof

[toc] | [prev] | [next] | [standalone]


#89597

FromMichael Torrie <torriem@gmail.com>
Date2015-04-29 21:10 -0600
Message-ID<mailman.112.1430363415.3680.python-list@python.org>
In reply to#89523
On 04/29/2015 12:16 AM, Cecil Westerhof wrote:
>> Prefix those names with a single leading underscore, which is the
>> convention for private variables.
> 
> Done.
> 
>> This way, if some user (maybe you! ;) has a good reason to change
>> those values in can be done, but it is quite clear that said user is
>> mucking about with internals and they are on their own.
> 
> There is something to say for this way of working.

I must say your posts are refreshing.  It's good to see someone come
into Python and learn the conventions of Python and roll with it instead
of trying to insist on Java- or C++-isms and idioms.
Makes life so much better and easier for everyone!  And helps make
Python enjoyable for you as well. Keep up the good work.

[toc] | [prev] | [next] | [standalone]


#89490

FromChris Angelico <rosuav@gmail.com>
Date2015-04-28 18:06 +1000
Message-ID<mailman.58.1430208384.3680.python-list@python.org>
In reply to#89486
On Tue, Apr 28, 2015 at 5:33 PM, Cecil Westerhof <Cecil@decebal.nl> wrote:
> If I remember correctly you can not hide variables of a class or make
> them read-only?
>
> 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?

If you simply want them to never change them, but they may look at
them, the best option is to do nothing. It's no different from
anything else they might do; for instance, someone might inject a new
'type' into your namespace, and completely break your check. (More on
that later.) If they aren't meant to look at _or_ change them, what
you could do is name them "_length" and so on - a single leading
underscore is a signal that this is a private implementation detail,
not to be changed or viewed. But you can't stop people from messing
you up. You can't stop people from playing around with ctypes and
changing the value of 0 (and yes, that is possible - it'll probably
crash Python sooner or later though!). None of that is your problem.

In fact, it's not really your problem if someone gives you a length
that isn't a simple integer. In the first place, they might give you a
subclass of int, so a better check would be this:

if not isinstance(length, int):
    raise ValueError("length should be integral")

(note that I'm avoiding the multiple-argument syntax which doesn't
work in Python 3; if ParameterError is a subclass of ValueError you
could use that instead, but ValueError works)

You don't even need to check that it's an integer. If you get
something that can't be compared against 0, they'll get an instant
exception; otherwise, the exception might come up later. But it's
still not your problem. You could skip the nonnegativity check too,
but that's good documentation. (I'm assuming that ought to be checking
'length', as there is no 'n' in your example.)

So here's how I'd write that function:

    def __init__(self, length):
        if length < 0:
            raise ValueError("Length should be non-negative")
        self.length = length
        self.old_values = []
        self.current_total = 0

If someone changes them... not your problem. Life's so easy when you
don't have to worry!

ChrisA

[toc] | [prev] | [next] | [standalone]


#89521

FromCecil Westerhof <Cecil@decebal.nl>
Date2015-04-29 08:25 +0200
Message-ID<87lhhbxxg1.fsf@Equus.decebal.nl>
In reply to#89490
Op Tuesday 28 Apr 2015 10:06 CEST schreef Chris Angelico:

> In fact, it's not really your problem if someone gives you a length
> that isn't a simple integer. In the first place, they might give you
> a subclass of int, so a better check would be this:
>
> if not isinstance(length, int):
> raise ValueError("length should be integral")

Good tip: I implemented it.


> (note that I'm avoiding the multiple-argument syntax which doesn't
> work in Python 3;

I already did this with print. Are there other statements I have to
take care for the possible problems with Python 3? I do not use Python
3, but that does not mean it is not a good idea to make my modules
work with it.


> if ParameterError is a subclass of ValueError you
> could use that instead, but ValueError works)

Was the wrong error: should have been TypeError. I find this strange:
Python is compiled, but I get only an error when this statement is
reached.


> You don't even need to check that it's an integer. If you get
> something that can't be compared against 0, they'll get an instant
> exception; otherwise, the exception might come up later. But it's
> still not your problem. You could skip the nonnegativity check too,
> but that's good documentation. (I'm assuming that ought to be
> checking 'length', as there is no 'n' in your example.)

Yep, silly mistake. I now check for greater or equal two. A moving
average of length one is not very useful.

I am of the school that it is good to check as much as possible.
(Without going over the top.)

-- 
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof

[toc] | [prev] | [next] | [standalone]


#89527

FromChris Angelico <rosuav@gmail.com>
Date2015-04-29 17:36 +1000
Message-ID<mailman.75.1430292996.3680.python-list@python.org>
In reply to#89521
On Wed, Apr 29, 2015 at 4:25 PM, Cecil Westerhof <Cecil@decebal.nl> wrote:
> Op Tuesday 28 Apr 2015 10:06 CEST schreef Chris Angelico:
>> (note that I'm avoiding the multiple-argument syntax which doesn't
>> work in Python 3;
>
> I already did this with print. Are there other statements I have to
> take care for the possible problems with Python 3? I do not use Python
> 3, but that does not mean it is not a good idea to make my modules
> work with it.

As long as you can require a minimum Python version of 2.7 (usually
not hard these days; older Pythons are still around, but
diminishingly, and you can simply choose not to support them), it's
not too hard to make your code basically compatible. The biggest
change will be the bytes/unicode distinction; you can adorn your byte
strings with b"..." and your Unicode strings with u"...", but the hard
part is keeping track in your head of which one is which. For a lot of
programs, it's not difficult; ASCII strings fit happily into either
bytes or Unicode, so the bulk of your strings can be left unadorned.

>> if ParameterError is a subclass of ValueError you
>> could use that instead, but ValueError works)
>
> Was the wrong error: should have been TypeError. I find this strange:
> Python is compiled, but I get only an error when this statement is
> reached.

As Python code gets compiled, names get turned into either locals or
external lookups. You can inspect a function interactively:

>>> def checkme(x):
...     if x < 0: raise BotchedError("Oops")
...     return x + 1
...
>>> checkme.__code__.co_names
('BotchedError',)
>>> checkme.__code__.co_varnames
('x',)

Local variable are turned into special slots, matching co_varnames;
global name lookups are done at run time. This allows mutual
recursion:

def func1(x):
    if x % 2: return x - 1
    return func2(x + 2)

def func2(x):
    if x % 3: return x
    return func1(x - 4)

At compilation time, func1 has a global name reference that can't be
resolved... but by the time anyone gets to calling it, the module has
gained a func2, and all is well. If you want to check your code for
name mistakes, you could do something like this:

# checker.py
import sys
def check_module(module):
    mod = sys.modules[module]
    for attr in dir(mod):
        thing = getattr(mod, attr)
        try: names = thing.__code__.co_names
        except AttributeError: continue # Not a function
        for name in names:
            if name not in dir(mod) and name not in dir(mod.__builtins__):
                raise NameError("Function %s refers to %s which
doesn't exist" % (attr, name))

Then you just put this line at the bottom of your code to see if there
are any potential problems:

import checker; checker.check_module(__name__)

Alternatively, you could build functionality like this into a linter
or other code quality confirmation. For most code, this will be a safe
check; it is theoretically possible for a module to grow a new
attribute between the end of module execution and the actual calling
of a function (which is why the interpreter itself doesn't check like
this), but it's sufficiently unusual that you could safely disallow it
in your style guide or source control policy.

> I am of the school that it is good to check as much as possible.
> (Without going over the top.)

One of the key differences between the C++/Java mentality and the
Python mentality is that, while both schools of thought would broadly
agree with what you say here, we draw the line of "over the top" at
different places. In Java code, you're expected to type-check
everything; in Python code, just use it, and if the caller gives you
the wrong data type, that's his problem, not yours. In Java code,
exceptions are there to be carefully documented - "this function might
raise this/these exception(s)", and its callers have to decide at
compilation time whether they're going to handle them or let them
bubble. In Python code, unexpected exceptions are generally expected
to bubble all the way up to a generic handler - for most applications,
that means printing the exception to the console and terminating. You
deal with things you know how to deal with, and any weird things that
happen, again, not your problem. Neither philosophy is fundamentally
wrong, but other Java programmers will get very annoyed at you if you
adopt a Python style in Java code (using RuntimeError for everything,
and checking nothing), and your Python code will get unnecessarily
verbose and heavy if you adopt a Java style in Python code. It's up to
you to decide how worthwhile it is, but I recommend at least giving
the Python style a try (pun not intended); you may well like it, or
you may find that you want at least a bit more pre-checking, but
either way, you'll know why.

All the best!

ChrisA

[toc] | [prev] | [next] | [standalone]


#89492

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2015-04-28 18:37 +1000
Message-ID<553f46d2$0$11124$c3e8da3@news.astraweb.com>
In reply to#89486
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

[toc] | [prev] | [next] | [standalone]


#89493

FromChris Angelico <rosuav@gmail.com>
Date2015-04-28 18:44 +1000
Message-ID<mailman.60.1430210687.3680.python-list@python.org>
In reply to#89492
On Tue, Apr 28, 2015 at 6:37 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> 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.)

Hey. If you cause a segfault, we're still allowed to laugh at you.

ChrisA

[toc] | [prev] | [next] | [standalone]


#89494

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-04-28 12:20 +0300
Message-ID<87pp6oip7x.fsf@elektro.pacujo.net>
In reply to#89492
Steven D'Aprano <steve+comp.lang.python@pearwood.info>:

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

I would take it further: as a rule, user code should not think of
modifying the contents of an object unless it is clearly documented as a
supported operation. IOW, you should consider all attributes private, or
at least read-only.

BTW, the principle is actually enforced for some objects:

   >>> "hello".join = lambda *x: "world"
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   AttributeError: 'str' object attribute 'join' is read-only
   >>> "hello".joinn = lambda *x: "world"
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   AttributeError: 'str' object has no attribute 'joinn'
   >>> "hello".__setattr__ = lambda *x: None
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   AttributeError: 'str' object attribute '__setattr__' is read-only


A tougher issue is with encapsulation. A derived class might
accidentally step on the toes of the base class by redefining an
attribute:

========================================================================
class Base:
    def __init__(self):
        self._secret = 17

    def divulge_secret(self):
        return self._secret

class Derived(Base):
    def __init__(self):
        self._secret = 1

print(Derived().divulge_secret())
========================================================================

which outputs 1.


Marko

[toc] | [prev] | [next] | [standalone]


#89522

FromCecil Westerhof <Cecil@decebal.nl>
Date2015-04-29 08:32 +0200
Message-ID<87h9rzxx5h.fsf@Equus.decebal.nl>
In reply to#89492
Op Tuesday 28 Apr 2015 10:37 CEST schreef Steven D'Aprano:

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

I will try to remember that.


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

Using that now.


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

What is a good reason?


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

Coming from C/C++ and Java I have to get used to it, but I will manage
I think.


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

Not in this case. The idea is that you do not know the elements at the
moment, but feed them to the MovingAverage instance as they come in.
See:
    https://github.com/CecilWesterhof/PythonLibrary/blob/master/utilDecebal.py


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

Done.

-- 
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof

[toc] | [prev] | [next] | [standalone]


#89601

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2015-04-30 14:05 +1000
Message-ID<5541a9fc$0$13014$c3e8da3$5496439d@news.astraweb.com>
In reply to#89522
On Wednesday 29 April 2015 16:32, Cecil Westerhof wrote:

>> 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.
> 
> Coming from C/C++ and Java I have to get used to it, but I will manage
> I think.


If you have a Java background, you might find this useful:

http://dirtsimple.org/2004/12/python-is-not-java.html

http://dirtsimple.org/2004/12/java-is-not-python-either.html


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#89603

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-04-30 08:10 +0300
Message-ID<87383inqut.fsf@elektro.pacujo.net>
In reply to#89601
Steven D'Aprano <steve+comp.lang.python@pearwood.info>:

> If you have a Java background, you might find this useful:
>
> http://dirtsimple.org/2004/12/python-is-not-java.html
>
> http://dirtsimple.org/2004/12/java-is-not-python-either.html

Unfortunately, I didn't find those articles all that insightful.

The one big difference between Java and Python is interfaces vs
ducktyping. In Java, you do everything through formal interfaces. In
Python, you do nothing through formal interfaces.

I occasionally see a Java class that doesn't implement an interface, and
there are abstract Python base classes that emulate interfaces. However,
I think both practices are nonidiomatic and should be avoided in their
respective domains.

Anonymous nested inner classes are syntactically cumbersome. However,
conceptually they are a fabulous device. They were popularized in Java
and I use them quite frequently in Python.


Marko

[toc] | [prev] | [next] | [standalone]


#89605

FromChris Angelico <rosuav@gmail.com>
Date2015-04-30 16:01 +1000
Message-ID<mailman.117.1430373680.3680.python-list@python.org>
In reply to#89603
On Thu, Apr 30, 2015 at 3:10 PM, Marko Rauhamaa <marko@pacujo.net> wrote:
> Steven D'Aprano <steve+comp.lang.python@pearwood.info>:
>
>> If you have a Java background, you might find this useful:
>>
>> http://dirtsimple.org/2004/12/python-is-not-java.html
>>
>> http://dirtsimple.org/2004/12/java-is-not-python-either.html
>
> Unfortunately, I didn't find those articles all that insightful.
>
> The one big difference between Java and Python is interfaces vs
> ducktyping. In Java, you do everything through formal interfaces. In
> Python, you do nothing through formal interfaces.

The other big difference between C++/Java and Python is that the
former have everything hidden by default, and the latter has
everything public by default. Compare:

class Point {
    int x, y;
public:
    Point(int _x, int _y): x(_x), y(_y) {}
    int get_x() {return x;}
    void set_x(int newx) {x = newx;}
    int get_y() {return y;}
    void set_y(int newy) {x = newy;}
};

void interpolate_points(Point from, Point to, Point **target, int count)
{
    int delta_x = to.get_x() - from.get_x();
    int delta_y = to.get_y() - from.get_y();
    for (int i=0;i<count;++i)
        target[i] = new Point(from.get_x() + delta_x*i/count,
from.get_y() + delta_y*i/count);
}


class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

def interpolate_points(from, to, count):
    delta_x = to.x - from.x
    delta_y = to.y - from.y
    return [Point(from.x + delta_y*i/count, from.y + delta_y*i/count)
for i in range(count)]

Okay, part of the improvement is the list comp rather than C-style
array return, and you could improve on that some in C++ even; but the
rigmarole of getters and setters is a completely unnecessary bug
magnet. (There's one in the above code. I was going to fix it, but
it's more indicative to leave it there.) Most classes do not need that
overhead. In the rare cases where they do, Python lets you smoothly
shift from a simple member to a property, so you can add logging or
whatever. (Though... don't abuse that. I've seen code that puts
@property on functions that do *network operations*, I kid you not.
This completely violates the expectation that attribute lookup is
simple and cheap.) As a small side benefit, there's no hassles about
naming ("get_x" vs "_x" vs "x"); you could avoid _x by using a
Python-style "this->x" everywhere in the C++ code, but that won't fix
get_x.

So clearly Python has the advantage, right? Well, not so clear. The
philosophical abhorrence of remote mutation in C++ makes it a lot
easier to reason about the code - you can look at the class definition
itself and guarantee you can see every change to a private member.
(Aside from naughty pointer manipulation and stuff. But in Python, we
have to say "Aside from fiddling with ctypes", so this is like the
Mistform Ultimus disclaimer[1].) In Python, you have to balance that
in application code; is it safe to change some object's member? Can
you just reach in and change the "read_callback" member to be a new
function reference, or might it have been referenced somewhere else?
Will you cause problems for yourself by replacing this actual file
with a StringIO file-like object? How can you know?

So the freedom is there, but it has to be used intelligently.

ChrisA

[1] eg http://archive.wizards.com/Magic/Magazine/Article.aspx?x=mtg/daily/arcana/1370
and http://archive.wizards.com/Magic/magazine/article.aspx?x=mtg/daily/arcana/1300

[toc] | [prev] | [next] | [standalone]


#89520

FromCecil Westerhof <Cecil@decebal.nl>
Date2015-04-29 08:14 +0200
Message-ID<87twvzxxyu.fsf@Equus.decebal.nl>
In reply to#89486
Op Tuesday 28 Apr 2015 09:33 CEST schreef Cecil Westerhof:

> If I remember correctly you can not hide variables of a class or
> make them read-only?
>
> 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?

I have posted it on:
    https://github.com/CecilWesterhof/PythonLibrary/blob/master/utilDecebal.py

-- 
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof

[toc] | [prev] | [next] | [standalone]


#89552

FromGrant Edwards <invalid@invalid.invalid>
Date2015-04-29 14:16 +0000
Message-ID<mhqp33$dpj$2@reader1.panix.com>
In reply to#89486
On 2015-04-28, Cecil Westerhof <Cecil@decebal.nl> wrote:
> If I remember correctly you can not hide variables of a class or make
> them read-only?
>
> 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?

It's like the punchline to the old doctor joke: if it hurts when you
do that, then don't _do_ that:

  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

The convention is that properties that start with underscores are
private.  They're not hidden, but if people touch them and it breaks
something, it's their fault.  Whether you go all Torvalds on their ass
for doing so is left as an exercise for the reader.

-- 
Grant Edwards               grant.b.edwards        Yow! If Robert Di Niro
                                  at               assassinates Walter Slezak,
                              gmail.com            will Jodie Foster marry
                                                   Bonzo??

[toc] | [prev] | [next] | [standalone]


#89594

FromDave Angel <davea@davea.name>
Date2015-04-29 22:31 -0400
Message-ID<mailman.109.1430361094.3680.python-list@python.org>
In reply to#89552
On 04/29/2015 10:16 AM, Grant Edwards wrote:
> On 2015-04-28, Cecil Westerhof <Cecil@decebal.nl> wrote:
>> If I remember correctly you can not hide variables of a class or make
>> them read-only?
>>
>> 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?
>
> It's like the punchline to the old doctor joke: if it hurts when you
> do that, then don't _do_ that:
>
>    def __init__(self, length):
>        if type(length) != int:

Better:  if isinstance(length, int):

>            raise ParameterError, 'Parameter has to be an int'
>        if n < 0:

Better:   if length < 0:          (since n is undefined)

>            raise ValueError, 'Parameter should be greater or equal 2'
>        self._length             = length
>        self._old_values         = []
>        self._current_total      = 0
>
> The convention is that properties that start with underscores are
> private.  They're not hidden, but if people touch them and it breaks
> something, it's their fault.  Whether you go all Torvalds on their ass
> for doing so is left as an exercise for the reader.
>


-- 
DaveA

[toc] | [prev] | [next] | [standalone]


#89631

FromDennis Lee Bieber <wlfraed@ix.netcom.com>
Date2015-04-30 08:53 -0400
Message-ID<mailman.129.1430398447.3680.python-list@python.org>
In reply to#89552
On Wed, 29 Apr 2015 22:31:13 -0400, Dave Angel <davea@davea.name> declaimed
the following:

>On 04/29/2015 10:16 AM, Grant Edwards wrote:

>
>>            raise ParameterError, 'Parameter has to be an int'
>>        if n < 0:
>
>Better:   if length < 0:          (since n is undefined)
>
>>            raise ValueError, 'Parameter should be greater or equal 2'

	Given the text in the exception, shouldn't that be

	if length < 2:
-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
    wlfraed@ix.netcom.com    HTTP://wlfraed.home.netcom.com/

[toc] | [prev] | [next] | [standalone]


#89634

FromCecil Westerhof <Cecil@decebal.nl>
Date2015-04-30 16:50 +0200
Message-ID<87wq0tvffn.fsf@Equus.decebal.nl>
In reply to#89631
Op Thursday 30 Apr 2015 14:53 CEST schreef Dennis Lee Bieber:

> On Wed, 29 Apr 2015 22:31:13 -0400, Dave Angel <davea@davea.name>
> declaimed the following:
>
>> On 04/29/2015 10:16 AM, Grant Edwards wrote:
>
>>
>>> raise ParameterError, 'Parameter has to be an int'
>>> if n < 0:
>>
>> Better:   if length < 0:          (since n is undefined)
>>
>>> raise ValueError, 'Parameter should be greater or equal 2'
>
> 	Given the text in the exception, shouldn't that be
>
> 	if length < 2:

Yeah: it already is. No points for punctuality on my side. :-(

-- 
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web