"Protected" property in Python?

Alex Martelli aleax at aleax.it
Tue Sep 23 08:41:21 CEST 2003


Jules Dubois wrote:

> I'm want to create a superclass with nothing but attributes and
> properties. Some of the subclasses will do nothing but provide values for
> the attributes.
> 
> (I'd also like to make sure (1) that the subclass provides actual values
> for the attributes and (2) that no "client" module adds or removes
> attributes or properties, but I don't know how to do those.)

You'll need a custom metaclass; you need to read up on this concept,
but these days you can find explanations on the net.  Any class is an
instance of a metaclass -- the class statement implies a call to the
metaclass, which in turn means the MC's __new__ then __init__ as
in any other class-call (instantiation) -- and that call must provide and
initialize the class object.

Not sure what you mean by "client" module, but in general Python is
not about stopping Python programers from performing tasks -- so if
you see your task as one of placing inhibitions on other Pythonistas
who want to use your code, you're in for a fight.  Still, you can do a good
job of ensuring that things don't happen _accidentally_, and that is
generally good enough -- a "malicious" other programmer is quite a
different kettle of fish, though.


Anyway, your problem has nothing to do with these difficult issues
(it's more of an issue of running before one can walk):

> I don't understand what I'm doing wrong, or maybe what I want to do is
> impossible.  Here's a stripped down version of the code:
> 
> ----------------------------------------------------------------
> #! /usr/bin/python
> class SuperClass(object):
>     def __init__(self):
>         self.__statusCode = "value bound in SUPERCLASS"
>     getStatusCode = property(lambda self: self.__statusCode)

This mangles the identifier __statusCode within class SuperClass,
giving _SuperClass__statusCode.

> class SubClass(SuperClass):
>     def __init__(self):
> #        SuperClass.__init__(self)
>         self.__statusCode = "value bound in SUBCLASS"

But this mangles it within class SubClass, giving _SubClass__statusCode, a 
different identifier.  That's what leading __ is all about: giving an 
identifier that strictly depends on the class where it's LEXICALLY found.
If you don't want that, don't use two leading underscores: use just one
(advisory indicator of privacy) and be happy.  Or, you *CAN* simulate by
hand the mangling, though there's little point in so doing -- e.g., in 
SuperClass, you can code:

    def getStatusCode(self):
        attr_name = '_%s__statusCode' % self.__class__.__name__
        return getattr(self, attr_name)
    statusCode = property(getStatusCode)

note that the leading get normally denotes an accessor method -- the
point of properties is having something that doesn't LOOK like a getter,
so naming one with a leading 'get' is quite peculiar.

> Why is the lambda function attempting to access the superclass' attribute
> and not the subclass' attribute?  Can I make it not do that?

Sure.

> If I replace "__statusCode" with "_statusCode", the output is
> 
>   value bound in SUBCLASS
> 
> as I want.  However, "_statusCode" is then visible from the outside, as I
> don't want.

There is no way to make the value NOT "visible from the outside" -- WITH
the leading underscores, it's STILL visible, as "_SubClass__statusCode",
anyway.  If you stashed the value away in a remote dict and encoded it
with strong cryptography, it would STILL be visible -- nothing stops a
halfway determined attacker from duplicating whatever way your superclass
uses to get at it.  Treating "client code programmers" as enemies and your
task as one of fighting against them to stop them from "abusing" your
pristine design is not Python's strength -- indeed the tools you used to 
have for this fight, rexec and Bastion, were recently removed as they did
not prove strong enough for this thankless task.  In Python, you had better
think of all these mechanisms as ADVISORY "security" -- and then the
simple convention of the one leading underscore should be ample: anybody
who deliberately uses an identifier starting with a leading underscore is
knowingly going beyond the interface to the implementation, anyway.  The
TWO leading underscores serve the specific purpose of allowing programmers
who code subclasses to blissfully ignore whatever private implementation
names the superclass has used for attributes, as it ensures against any
accidental name clashes -- using them for *communication* between base
and derived classes is weird, since they're mainly for *isolating* base from
derived classes.


> Am I missing something about how Python works?
> 
> Is my problem more fundamental, like not understanding OO programming?
> 
> I'm able to RTFM if someone would provide a pointer.

Googling for:
    Python metaclass
gives you plenty of material to chew on.  I also suggest my presentation on
the subject, PDF slides at http://www.strakt.com/docs/ep03_meta.pdf .


Alex





More information about the Python-list mailing list