Warning when new attributes are added to classes at run time

M

Matthew Wilson

I sometimes inadvertently create a new attribute on an object rather
update a value bound to an existing attribute. For example:

In [5]: class some_class(object):
...: def __init__(self, a=None):
...: self.a = a
...:

In [6]: c = some_class(a=1)

In [7]: c.a
Out[7]: 1

In [8]: c.A = 2

I meant to update c.a but I created a new c.A. I make this mistake
probably hourly.

I suspect adding attributes at run time can be a beautiful thing, but in
this particular instance, I'm only using this feature to hurt myself.

I wrote a simple class that will warn me when I make this mistake in the
future:

import warnings

class C(object):

warn_on_new_attributes = True

standard_attributes = []

def __setattr__(self, name, value):

if self.warn_on_new_attributes \
and name is not 'warn_on_new_attributes' \
and name not in self.standard_attributes:

warnings.warn("%s has no standard attribute %s."
% (self.__class__.__name__, name))


self.__dict__[name] = value


class C1(C):

standard_attributes = ['a1', 'a2']


class C2(C):

warn_on_new_attributes = False

# Do some simple testing.
c11 = C1()
c11.a1 = 1
c11.a2 = 2
c11.a3 = 3
c11.a4 = 4

# Disable warnings for this instance.
c12 = C1()
c12.warn_on_new_attributes = False
c12.a1 = 1
c12.a2 = 2
c12.a3 = 3
c12.a4 = 4

c11.a5 = 5

# Use an object that has warnings disabled by default.
c2 = C2()
c2.a1 = 1
c2.a2 = 2
c2.a3 = 3
c2.a4 = 4

# enable warnings for this object.
c2.warn_on_new_attributes = True
c2.a1 = 1
c2.a5 = 5


All comments are welcome. Is there a better way of implementing the
above class, OR, is this approach generally wrong-headed? Am I the only
one that makes this mistake?

TIA
 
J

jay graves

Matthew said:
I sometimes inadvertently create a new attribute on an object rather
update a value bound to an existing attribute. For example:
All comments are welcome. Is there a better way of implementing the
above class, OR, is this approach generally wrong-headed? Am I the only
one that makes this mistake?

It's never been much a problem for me but you could look into __slots__

http://docs.python.org/ref/slots.html

....
jay
 
D

Diez B. Roggisch

Matthew said:
I sometimes inadvertently create a new attribute on an object rather
update a value bound to an existing attribute. For example:

In [5]: class some_class(object):
...: def __init__(self, a=None):
...: self.a = a
...:

In [6]: c = some_class(a=1)

In [7]: c.a
Out[7]: 1

In [8]: c.A = 2

I meant to update c.a but I created a new c.A. I make this mistake
probably hourly.

Try pylint & pychecker.

Diez
 
B

Bruno Desthuilliers

Matthew said:
I sometimes inadvertently create a new attribute on an object rather
update a value bound to an existing attribute. For example:
(snip)

I meant to update c.a but I created a new c.A. I make this mistake
probably hourly.

I suspect adding attributes at run time can be a beautiful thing, but in
this particular instance, I'm only using this feature to hurt myself.

See other posts in this thread for some other possible solutions.
I wrote a simple class that will warn me when I make this mistake in the
future:

import warnings

class C(object):

warn_on_new_attributes = True

standard_attributes = []

def __setattr__(self, name, value):

if self.warn_on_new_attributes \
and name is not 'warn_on_new_attributes' \
and name not in self.standard_attributes:

warnings.warn("%s has no standard attribute %s."
% (self.__class__.__name__, name))


self.__dict__[name] = value
Make it:
object.__setattr__(self, name, value)

Your approach will lead to strange results if you mix it with properties
or other descriptors...

class C1(C):

standard_attributes = ['a1', 'a2']

DRY violation here. And a potential problem with inheritance (as always
with class attributes).

(snip)
 
M

Matthew Wilson

self.__dict__[name] = value
Make it:
object.__setattr__(self, name, value)

Your approach will lead to strange results if you mix it with properties
or other descriptors...
Thanks!
class C1(C):

standard_attributes = ['a1', 'a2']

DRY violation here. And a potential problem with inheritance (as always
with class attributes).

Considering I had to look up what DRY meant before replying to this
message, I may be missing your point. Is the repeat here that each
subclass has to define its own list of standard attributes? Or, is it
that the standard_attributes list holds strings, but I could build that
list up by looking at my existing attributes?

If you're feeling charitable, can you explain what you mean a little
more?

TIA
 
B

Bruno Desthuilliers

Matthew said:
class C1(C):

standard_attributes = ['a1', 'a2']

DRY violation here. And a potential problem with inheritance (as always
with class attributes).


Considering I had to look up what DRY meant before replying to this
message, I may be missing your point. Is the repeat here that each
subclass has to define its own list of standard attributes? Or, is it
that the standard_attributes list holds strings, but I could build that
list up by looking at my existing attributes?

Mostly the second. But now, how are you going to build that list from
"existing attributes" before these attributes exists ?-) (chicken and
egg problem here...)

FWIW, you could use custom descriptors to build the 'schema', then use a
metaclass to detect these descriptors and build the "allowed attributes"
list. But the whole thing seems overkill to me if it's only to solve
typo problems (can be interesting for other reasons...).

Re-read the three first answers to your post - unit-tests,
pylint/pychecker, eventually slots...

My 2 cents
 
N

Nick Vatamaniuc

Use __slots__ they will simply give you an error. But at the same time
I don't think they are inheritable and in general you should only use
slots for performance reasons (even then test before using).

Or you could also simulate a __slots__ mechanism the way you are doing
i.e. checking the attributes yourself. But I would suggest though that
you create a test case (unittest) and run testing _separately_ from
your code. Your code should do only what it does, too many "if i
messed: up warn me" or "need to make sure I don't have extra
attributes" checks would make the code itself incomprehnsible over time
as half of it would end up being just checks for exceptional
situations. So spend 5 minutes to look up
http://pyunit.sourceforge.net/ and then in the long run your effort to
implement testing will be rewarded.

Nick V.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top