Optimizing methods away or not?

S

Steven D'Aprano

I have a class with a method meant to verify internal program logic (not
data supplied by the caller). Because it is time-consuming but optional,
I treat it as a complex assertion statement, and optimize it away if
__debug__ is false:

class Parrot:
def __init__(self, *args):
print "Initialising instance..."
if __debug__:
self.verify() # check internal program state, not args
if __debug__:
def verify(self):
print "Verifying..."


If I run Python normally, I can do this:
Initialising instance...
Verifying...Verifying...


and if I run Python with the -O flag, I get this:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Parrot instance has no attribute 'verify'


This is the behaviour I want, but I haven't seen it before in other code.
What do people think? Is it a good idea or a bad?

If you think it is a bad idea to have verify disappear under
optimization, would you change your mind if it were called __verify
instead?


One possible alternative is to do something like this:

class Parrot:
def __init__(self, *args):
print "Initialising instance..."
if __debug__:
self.verify()
def verify(self):
if __debug__:
print "Verifying..."
return None
# this is optional
else:
warnings.warn("verify() is a null op")


which now means that Parrot instances will always have a verify method,
even if it does nothing. I'm not sure I like that. What do others think?
Which do you consider better design?
 
J

James Stroud

Steven said:
class Parrot:
def __init__(self, *args):
print "Initialising instance..."
if __debug__:
self.verify() # check internal program state, not args
if __debug__:
def verify(self):
print "Verifying..."

+1 It looks good to me and the intent is much clearer than the other.
 
M

Marc 'BlackJack' Rintsch

I have a class with a method meant to verify internal program logic (not
data supplied by the caller). Because it is time-consuming but optional,
I treat it as a complex assertion statement, and optimize it away if
__debug__ is false:

class Parrot:
def __init__(self, *args):
print "Initialising instance..."
if __debug__:
self.verify() # check internal program state, not args
if __debug__:
def verify(self):
print "Verifying..."


If I run Python normally, I can do this:

Initialising instance...
Verifying...
Verifying...


and if I run Python with the -O flag, I get this:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Parrot instance has no attribute 'verify'


This is the behaviour I want, but I haven't seen it before in other
code. What do people think? Is it a good idea or a bad?

If you think it is a bad idea to have verify disappear under
optimization, would you change your mind if it were called __verify
instead?


One possible alternative is to do something like this:

class Parrot:
def __init__(self, *args):
print "Initialising instance..."
if __debug__:
self.verify()
def verify(self):
if __debug__:
print "Verifying..."
return None
# this is optional
else:
warnings.warn("verify() is a null op")


which now means that Parrot instances will always have a verify method,
even if it does nothing. I'm not sure I like that. What do others think?
Which do you consider better design?

None of it. Why not simply:

class Parrot:
def __init__(self, *args):
print "Initialising instance..."
assert self.verify()

def _verify(self):
print "Verifying..."
return None

If you compile with -O the ``assert`` is optimized away. But you still
can call `_verify()` at specific points even in optimized code if you
want or need.

Ciao,
Marc 'BlackJack' Rintsch
 
M

Marc 'BlackJack' Rintsch

class Parrot:
def __init__(self, *args):
print "Initialising instance..."
assert self.verify()

Here I meant ``assert self._verify()`` of course.
def _verify(self):
print "Verifying..."
return None

Ciao,
Marc 'BlackJack' Rintsch
 
S

Steven D'Aprano

None of it. Why not simply:

class Parrot:
def __init__(self, *args):
print "Initialising instance..."
assert self._verify()

def _verify(self):
print "Verifying..."
return None

For your method to work, I'd have to have _verify return a boolean flag
instead of None, because assert None always fails.

If you compile with -O the ``assert`` is optimized away. But you still
can call `_verify()` at specific points even in optimized code if you
want or need.

That's a reasonable approach, if my use-case was for the caller to call
the verify method. It's not: it's verifying my program logic rather than
the caller's data, and it's only meaningful to do that verification at
initialisation time.
 
A

Arnaud Delobelle

Steven D'Aprano said:
I have a class with a method meant to verify internal program logic (not
data supplied by the caller). Because it is time-consuming but optional,
I treat it as a complex assertion statement, and optimize it away if
__debug__ is false:

class Parrot:
def __init__(self, *args):
print "Initialising instance..."
if __debug__:
self.verify() # check internal program state, not args
if __debug__:
def verify(self):
print "Verifying..."


If I run Python normally, I can do this:

FWIW here is a way with a metaclass:

===== verify_init.py =====

from functools import wraps

def post_verify(method):
@wraps(method)
def decorated(self, *args):
result = method(self, *args)
self.verify()
return result
return decorated

class VerifyInit(type):
def __new__(meta, name, bases, attrs):
if __debug__:
attrs['__init__'] = post_verify(attrs['__init__'])
else:
del attrs['verify']
return type.__new__(meta, name, bases, attrs)

class Parrot:
__metaclass__ = VerifyInit
def __init__(self, *args):
print "Initialising instance..."
def verify(self):
print "Verifying..."

coco = Parrot()

==========

marigold:junk arno$ python verify_init.py
Initialising instance...
Verifying...
marigold:junk arno$ python -O verify_init.py
Initialising instance...

You could also not use the metaclass and use post_verify as a decorator:

class Parrot:
@post_verify
def __init__(self, *args):
print "Initialising instance..."
if __debug__:
def verify(self):
print "Verifying..."
 
A

Arnaud Delobelle

Steven D'Aprano said:
Except that self.verify doesn't exist if __debug__ is false.

OK I wrote this as an afterthought. I'm, sure it's not beyond your ability
to add

if not __debug__:
return method

at the start of the post_verify function :)
 
T

Terry Reedy

Steven said:
I have a class with a method meant to verify internal program logic (not
data supplied by the caller). Because it is time-consuming but optional,
I treat it as a complex assertion statement, and optimize it away if
__debug__ is false:

class Parrot:
def __init__(self, *args):
print "Initialising instance..."
if __debug__:
self.verify() # check internal program state, not args
if __debug__:
def verify(self):
print "Verifying..."

Given that verify is only called from within _init__, I would put
everything within one 'if __debug__' statement. Either inline

if __debug__:
<code from verify function>

or if for some reason you really don't like that, nested

if __debug__:
def verify():
print "Verifying..."
verify()

tjr
 

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,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top