Warning when new attributes are added to classes at run time

Discussion in 'Python' started by Matthew Wilson, Jul 19, 2006.

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

    --
    A better way of running series of SAS programs:
    http://overlook.homelinux.net/wilsonwiki/SasAndMakefiles
     
    Matthew Wilson, Jul 19, 2006
    #1
    1. Advertising

  2. Matthew Wilson

    jay graves Guest

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

    <snippage>
    > 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
     
    jay graves, Jul 19, 2006
    #2
    1. Advertising

  3. Matthew Wilson schrieb:
    > 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
     
    Diez B. Roggisch, Jul 19, 2006
    #3
  4. Matthew Wilson wrote:
    > 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)


    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in ''.split('@')])"
     
    Bruno Desthuilliers, Jul 20, 2006
    #4
  5. On Thu 20 Jul 2006 04:32:28 AM EDT, Bruno Desthuilliers wrote:
    >> 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


    --
    A better way of running series of SAS programs:
    http://overlook.homelinux.net/wilsonwiki/SasAndMakefiles
     
    Matthew Wilson, Jul 20, 2006
    #5
  6. Matthew Wilson wrote:
    > On Thu 20 Jul 2006 04:32:28 AM EDT, Bruno Desthuilliers wrote:

    (snip)
    >
    >>> 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

    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in ''.split('@')])"
     
    Bruno Desthuilliers, Jul 20, 2006
    #6
  7. 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.



    Matthew Wilson wrote:
    > 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
    >
    > --
    > A better way of running series of SAS programs:
    > http://overlook.homelinux.net/wilsonwiki/SasAndMakefiles
     
    Nick Vatamaniuc, Jul 20, 2006
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Bennett Haselton
    Replies:
    1
    Views:
    516
    John M Deal
    Nov 8, 2004
  2. Razvan
    Replies:
    11
    Views:
    922
    Andrew Thompson
    Jul 17, 2004
  3. =?Utf-8?B?QXJuZSBHYXJ2YW5kZXI=?=

    Dynamic forms with input boxes added at run time.

    =?Utf-8?B?QXJuZSBHYXJ2YW5kZXI=?=, Oct 5, 2007, in forum: ASP .Net
    Replies:
    1
    Views:
    359
    =?Utf-8?B?UGV0ZXIgQnJvbWJlcmcgW0MjIE1WUF0=?=
    Oct 5, 2007
  4. Pierre Yves
    Replies:
    2
    Views:
    490
    Pierre Yves
    Jan 10, 2008
  5. Jim Mitchell
    Replies:
    1
    Views:
    260
    Jim Mitchell
    Aug 16, 2003
Loading...

Share This Page