too dynamic - how to avoid?

M

mic

I've spent last hour trying to debug my code, when I found out that instead
of executing object's method I accidentaly overridden it with variable (see
below example). My stupid! But as the system becomes more complex, I begun
to wonder how to avoid such situations in the future. Does anybody have some
experiences about that?

Simple example:

class test:
def a(self, value):
print value
t = test()
t.a('my test') <= returns obviously 'my test'
t.a = 'my test' <= creates object's attribute a, so I'm loosing my method

Regards,

Michal
 
A

Alex Martelli

mic said:
I've spent last hour trying to debug my code, when I found out that
instead of executing object's method I accidentaly overridden it with
variable (see below example). My stupid! But as the system becomes more
complex, I begun to wonder how to avoid such situations in the future.
Does anybody have some experiences about that?

Yes, and the experience is: it's not a problem in practice. Still,
if you disagree, Python gives you the tools to protect yourself -- read
on.

Simple example:

class test:
def a(self, value):
print value
t = test()
t.a('my test') <= returns obviously 'my test'
t.a = 'my test' <= creates object's attribute a, so I'm loosing my method

First of all, be sure to make your classes newstyle, e.g.
class test(object):
otherwise, by default, you get oldstyle classes, where compatibility
constraints mean most important new features just can't work right.

Now, limiting discussion to newstyle classes only:

in a class's dictionary there are two kinds of descriptors --
somewhat arbitrarily names 'data' and 'non-data' kinds of descriptors.

The 'non-data' kind, used for example for methods, are SPECIFICALLY
designed to be overridable per-instance. The 'data' kind aren't.

(Names are rather artificial; the real distinction is whether a
__set__ method is present in the descriptor -- if yes, it's data,
otherwise it's non-data -- calling it "setting-intercept" might
be clearer than calling it 'data'...!). Check it out...:

class c(object):
def a(self): pass
b = property(a)

descriptor 'a' is non-data, so it can be overridden in instances;
descriptor 'b' is data, so it cannot. E.g.:
Traceback (most recent call last):

The 'property' type of descriptors isn't quite suitable for what
you want, of course, because even just ACCESSING x.b makes a CALL
to x.a. Rather, you want a new and different "nonrebindablemethod"
type of descriptor whose __get__ is just like the function, while
its __set__ raises the exception. So, make such a type:

class norebind(object):
def __init__(self, func): self.func = func
def __get__(self, *args): return self.func.__get__(*args)
def __set__(self, *args): raise TypeError

class c(object):
def a(self): pass
b = norebind(a)

There -- now, after x=c(), attempts to rebind x.b will raise a
TypeError, as you apparently wish. Note that you can code the
class as:

class c(object):
def a(self): pass
a = norebind(a)

so that there's no x.b -- just a non-rebindable x.a.


You won't need it in practice, but it's still a cool hack:).

Alex
 
A

Alexander Schmolck

mic said:
I've spent last hour trying to debug my code, when I found out that instead
of executing object's method I accidentaly overridden it with variable (see
below example). My stupid! But as the system becomes more complex, I begun
to wonder how to avoid such situations in the future. Does anybody have some
experiences about that?

- use of decent naming schemes presumably helps (and if your class gets so big
that you can't remember what are attributes and what are methods refactoring
would probably be a good idea)

- if that's not enough you can always do something like override __setattr__,
to check that you don't overwrite instance methods, e.g. (UNTESTED):

import types
class bar:
...
def __setattr__(self, attr, value):
if isinstance(getattr(self, attr, None), types.MethodType):
raise RuntimeError("Trying to overwrite method %s" % attr)
else setattr(self, attr, value)


- if you need this often, you could write a function or metaclass to avoid
duplicating the above in all classes where you feel you need it.


'as
 
A

Anton Vredegoor

mic said:
I've spent last hour trying to debug my code, when I found out that instead
of executing object's method I accidentaly overridden it with variable (see
below example). My stupid! But as the system becomes more complex, I begun
to wonder how to avoid such situations in the future. Does anybody have some
experiences about that?

Simple example:

class test:
def a(self, value):
print value
t = test()
t.a('my test') <= returns obviously 'my test'
t.a = 'my test' <= creates object's attribute a, so I'm loosing my method

From this I can't discern whether you're asking a simple question or
want some sophisticated, nifty programming advice. I'm assuming you
just want some help using the interpreter and the overall programming
environment.

If something -whatever- is not quite as expected it's very important
to read the traceback (if there is one) from the interpreter output,
for example in this case trying to call t.a('my test') after t.a has
been bound to a string will result in some complaint from the
interpreter that type 'str' has no call method. It's also very a good
idea to include the traceback in usenet posts since that makes it
possible to receive more specific comments.

Another thing is that it's very handy to use the interactive
interpreter to test pieces of code that behave in unexpected ways.
Just try it out and check the output of your functions. It's not like
indexing an array out of bounds will make your complete system crash
or make it instable, Python is reasonably well defended against simple
mistakes.

The dir command is handy too, for example dir(t.a) will give some info
about what the interpreter thinks t.a is. Next, sprinkling your code
with print statements -turning it into a poor mans debugger system-
works very well for most of the mistakes of this calibre.

Please note that given the dynamic nature of Python the above code
snippet might be exactly what you want and there's no way for the
interpreter yet to do what you intend rather than what you code ;-)

Then there's the debugger inside Idle, but the strategies above are
sufficient in most cases, so that -at least in my case- it's almost
never necessary to use it. I mostly use Scite as an editor and have
pychecker* a single keypress away, but I only use it to doublecheck my
code after it runs as expected.

HTH,

Anton

* http://pychecker.sourceforge.net/
 
T

Tim Ottinger

My stupid! But as the system becomes more complex, I begun
to wonder how to avoid such situations in the future. Does anybody have some
experiences about that?

1) Don't do it.
2) You can use a naming convention
a) Methods have verbs, variables don't: set_temperature, temperature
b) Prefix members with warts: x.temperature() x._temperature or
x.its_temperature
c) Your choice here
3) See #1, above

Pychecker doesn't seem to pick this up. I don't know about pylint.
I guess it might be something possible if you are talking methods
and attributes.

Tim
 
J

John J. Lee

mic said:
I've spent last hour trying to debug my code, when I found out that instead
of executing object's method I accidentaly overridden it with variable (see
below example). My stupid! But as the system becomes more complex, I begun
to wonder how to avoid such situations in the future. Does anybody have some
experiences about that?

I don't understand how that could cause a problem. Why didn't you
immediately get a traceback with a message something like 'a is not
callable'?


John
 

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
474,164
Messages
2,570,898
Members
47,440
Latest member
YoungBorel

Latest Threads

Top