Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Handling Property and internal ('__') attribute inheritance and creation

250 views
Skip to first unread message

Rafe

unread,
Aug 15, 2008, 2:02:36 PM8/15/08
to
Hi,

I've been thinking in circles about these aspects of Pythonic design
and I'm curious what everyone else is doing and thinks. There are 3
issues here:


1) 'Declaring' attributes - I always felt it was good code practice to
declare attributes in a section of the class namespace. I set anything
that is constant but anything variable is set again in __init__():

Class A(object):
name = "a name"
type = "a typee"
childobject = None

def __init__(self, obj):
self.childobject = object

This makes it easy to remember and figure out what is in the class.
Granted there is nothing to enforce this, but that is why I called it
'code practice'. Do you agree or is this just extra work?


2) Internal attributes (starting with 2x'_') aren't inherited. Do you
just switch to a single '_' when you want an "internal" attribute
inherited? These are attributes I want the classes to use but not the
user of these classes. Of course, like anything else in Python, these
aren't really private. It is just a convention, right? (The example
for #3 shows this.)


3) It isn't possible to override a piece of a Property Descriptor. To
get around this, I define the necessary functions in the class but I
define the descriptor in the __new__() method so the inherting class
can override the methods. Am I overlooking some basic design principle
here? This seems like a lot of work for a simple behavior. Example:

class Base(object):
def __new__(cls):
setattr(cls,
"state",
property(fget = cls._Get_state,
fset = cls._Set_state,
fdel = None,
doc = cls._doc_state))

obj = super(Base, cls).__new__(cls)
return obj

state = None # Set in __new__()
_state = True
_doc_state = "The state of this object"
def _Get_state(self): return self._state
def _Set_state(self, value): self._state = value

class Child(Base):
def _Get_state(self):
# Do some work before getting the state.
print "Getting the state using the child's method"
return self._state

print Child().state

Please share your thoughts,

- Rafe

Bruno Desthuilliers

unread,
Aug 15, 2008, 2:22:23 PM8/15/08
to
Rafe a écrit :

> Hi,
>
> I've been thinking in circles about these aspects of Pythonic design
> and I'm curious what everyone else is doing and thinks. There are 3
> issues here:
>
>
> 1) 'Declaring' attributes

There's nothing like "declaration" of variables/attributes/whatever in
Python.

> - I always felt it was good code practice to
> declare attributes in a section of the class namespace. I set anything
> that is constant but anything variable is set again in __init__():
>
> Class A(object):
> name = "a name"
> type = "a typee"
> childobject = None
>
> def __init__(self, obj):
> self.childobject = object
>
> This makes it easy to remember and figure out what is in the class.
> Granted there is nothing to enforce this, but that is why I called it
> 'code practice'. Do you agree or is this just extra work?

It's not only extra work, it's mostly a WTF. You create class attributes
for no other reasons than to mimic some other mainstream languages. If I
was to maintain such code, I'd loose valuable time wondering where these
class attributes are used.

>
> 2) Internal attributes (starting with 2x'_') aren't inherited.

Yes they are. But you need to manually mangle them when trying to access
them from a child class method. FWIW, that *is* the point of
__name_mangling : making sure these attributes won't be accidentally
overwritten in a child class.

> Do you
> just switch to a single '_' when you want an "internal" attribute
> inherited? These are attributes I want the classes to use but not the
> user of these classes. Of course, like anything else in Python, these
> aren't really private. It is just a convention, right? (The example
> for #3 shows this.)

Yes. The (*very* strong) convention is that
_names_with_simple_leading_underscore denote implementation attributes.

> 3) It isn't possible to override a piece of a Property Descriptor. To
> get around this, I define the necessary functions in the class but I
> define the descriptor in the __new__() method so the inherting class
> can override the methods. Am I overlooking some basic design principle
> here? This seems like a lot of work for a simple behavior. Example:
>
> class Base(object):
> def __new__(cls):
> setattr(cls,
> "state",
> property(fget = cls._Get_state,
> fset = cls._Set_state,
> fdel = None,
> doc = cls._doc_state))
>
> obj = super(Base, cls).__new__(cls)
> return obj
>
> state = None # Set in __new__()
> _state = True
> _doc_state = "The state of this object"
> def _Get_state(self): return self._state
> def _Set_state(self, value): self._state = value

pep08 : attribute names (including methods) should be all_lower.

> class Child(Base):
> def _Get_state(self):
> # Do some work before getting the state.
> print "Getting the state using the child's method"
> return self._state
>
> print Child().state

How often do you really need to override a property ? (hint : as far as
I'm concerned, it never happened so far). Now you have two solutions :
either redefine the whole property in the derived class, or, if you
really intend your property to be overriden, provide a "template method"
hook.

I'd say you're making things much more complicated than they need to be.

Christian Heimes

unread,
Aug 15, 2008, 5:08:19 PM8/15/08
to pytho...@python.org
Bruno Desthuilliers wrote:
> How often do you really need to override a property ? (hint : as far as
> I'm concerned, it never happened so far). Now you have two solutions :
> either redefine the whole property in the derived class, or, if you
> really intend your property to be overriden, provide a "template method"
> hook.

In Python 2.6 and 3.0 you can override the setter, getter or deleter of
a property in a subclass. You may be able to find a pure Python
implementation written by Guido. My C implementation in the core works
almost the same.

Christian

Eric Brunel

unread,
Aug 19, 2008, 5:30:52 AM8/19/08
to
On Fri, 15 Aug 2008 20:02:36 +0200, Rafe <rafe...@gmail.com> wrote:
[snip]

> 1) 'Declaring' attributes - I always felt it was good code practice to
> declare attributes in a section of the class namespace. I set anything
> that is constant but anything variable is set again in __init__():
>
> Class A(object):
> name = "a name"
> type = "a typee"
> childobject = None
>
> def __init__(self, obj):
> self.childobject = object
>
> This makes it easy to remember and figure out what is in the class.
> Granted there is nothing to enforce this, but that is why I called it
> 'code practice'. Do you agree or is this just extra work?

To add to what others have already said, it is not only 'just extra work',
it is also quite dangerous. See:

class A(object):
children = []

Now *all* A instances share the *same* list, as it was defined as a class
attribute. If you ever forget to set it to something else in __init__,
you're in for big surprises.

What I'm doing is set all instance attributes right at the beginning of
the __init__ with a huge comment before them, like:

class A(object):
def __init__(self):
## INSTANCE ATTRIBUTES:
## --------------------
self.children = []

This way, you still can 'figure out what is in the class' quite quickly,
without the drawbacks of declaraing class attributes.

HTH
--
python -c "print ''.join([chr(154 - ord(c)) for c in
'U(17zX(%,5.zmz5(17l8(%,5.Z*(93-965$l7+-'])"

Fredrik Lundh

unread,
Aug 19, 2008, 5:51:37 AM8/19/08
to pytho...@python.org
Eric Brunel wrote:

> To add to what others have already said, it is not only 'just extra
> work', it is also quite dangerous. See:
>
> class A(object):
> children = []

the OP is aware of that, of course:

> I set anything that is constant but anything variable is set again in
> __init__()

as long as if you're aware of the issues involved (which you should be
if you're using Python professionally), using class-level attributes is
a perfectly valid Python style.

</F>

Bruno Desthuilliers

unread,
Aug 19, 2008, 7:38:09 AM8/19/08
to
Fredrik Lundh a écrit :

Using class attributes is perfectly valid Python style, indeed. But in
this case - ie: using class attributes as a pseudo-declaration of
instance attributes -, I would not call this good style : it's a waste
of time, a violation of DRY, and a potential source of confusion for the
class's users / maintainers.

Rafe

unread,
Aug 19, 2008, 10:36:35 AM8/19/08
to
On Aug 16, 1:22 am, Bruno Desthuilliers
<bdesth.quelquech...@free.quelquepart.fr> wrote:
> Rafea écrit :


Thanks Bruno, and everyone ! These are exactly the type of hard
answers I was hoping for. I'm mostly converted but my brain still
needs a Pythonic push from time to time. Looks like have some some
clean up to perform...with confidence.

I'm interested to see the implementation of getter, etc overrides in
2.6/3.0. I have two classes that could be simplified with this. For
example, I have a class which does a lot of work and has about 5 key
properties. I want to inherit it, and just trigger an event (update
something only stored in the child) after 4 of these attributes are
finished being set. I was thinking about using a callback which is
empty in the parent or __setattr__() (but I hat using this unless I
have to, it is still troublesome to me).

- Rafe


Bruno Desthuilliers

unread,
Aug 19, 2008, 3:06:28 PM8/19/08
to
Rafe a écrit :

IOW : a template method. Probably one of the most known, most evident
and most useful design pattern. But...

> or __setattr__() (but I hat using this unless I
> have to, it is still troublesome to me).


Don't use __setattr__ if you can avoid it. It *is* troublesome - and
costly too.

Now, remember that the property class is just an handy shortcut for the
most common use of computed attributes. The more generic mechanism
properties are built upon - the descriptor protocol - is nothing
complicated, so when you hit the limits of properties (or when
property-based code becomes too tangled/messy/whatever), you can write
your own custom descriptor objects.

0 new messages