Seems like I want a pre-processor, but...

R

Russell Warren

After some digging it seems that python does not have any equivalent to
C's #if directives, and I don't get it...

For example, I've got a bit of python 2.3 code that uses
collections.deque.pop(0) in order to pop the leftmost item. In python
2.4 this is no longer valid - there is no argument on pop (rightmost
only now) and you call .popleft() instead.

I would like my code to work in both versions for now and simply want
to add code like:

if sys.version[:3] == "2.3":
return self.myDeque.pop(0)
else:
return self.myDeque.popleft()

but am recoiling a bit at the unnecessary conditional in there that I
think will be run on every execution - unless the compiler has some
magic detection of things like sys.version to compile out the
conditional as if it were a preprocessor directive (seems highly
unlikely!)?.

What is the pythonic thing to do? This is in a generally usable file,
not a package, so I don't want to make a version for each (which seems
to be what every package out there does). It seems to be begging for a
pre-processor directive set.

Thanks,
Russ
 
F

Fredrik Lundh

Russell said:
For example, I've got a bit of python 2.3 code that uses
collections.deque.pop(0) in order to pop the leftmost item. In python
2.4 this is no longer valid - there is no argument on pop (rightmost
only now) and you call .popleft() instead.

the collections module was added in 2.4, so it's not clear what code you're
really using under 2.3. but since it's not a standard module, maybe you could
add the missing method yourself ?
I would like my code to work in both versions for now and simply want
to add code like:

if sys.version[:3] == "2.3":
return self.myDeque.pop(0)
else:
return self.myDeque.popleft()

but am recoiling a bit at the unnecessary conditional in there that I
think will be run on every execution - unless the compiler has some
magic detection of things like sys.version to compile out the
conditional as if it were a preprocessor directive (seems highly
unlikely!)?.

What is the pythonic thing to do?

fork the function/method:

if sys.version_info < (2, 4):
def myfunc(self):
# using deque emulation
return self.myDeque.pop(0)
else:
def myfunc(self):
return self.myDeque.popleft()

or wrap the non-standard deque class in a compatibility wrapper:

if sys.version_info < (2, 4):
class mydequeclass(deque):
def popleft(self):
return self.pop(0)

...

return self.myDeque.popleft()

</F>
 
P

Peter Otten

Russell said:
After some digging it seems that python does not have any equivalent to
C's #if directives, and I don't get it...

For example, I've got a bit of python 2.3 code that uses
collections.deque.pop(0) in order to pop the leftmost item. In python
2.4 this is no longer valid - there is no argument on pop (rightmost
only now) and you call .popleft() instead.

collections is new in 2.4 -- I assume you mean list.pop(0) versus
collections.deque.popleft().
I would like my code to work in both versions for now and simply want
to add code like:

if sys.version[:3] == "2.3":
return self.myDeque.pop(0)
else:
return self.myDeque.popleft()

but am recoiling a bit at the unnecessary conditional in there that I
think will be run on every execution - unless the compiler has some
magic detection of things like sys.version to compile out the
conditional as if it were a preprocessor directive (seems highly
unlikely!)?.

What is the pythonic thing to do? This is in a generally usable file,
not a package, so I don't want to make a version for each (which seems
to be what every package out there does). It seems to be begging for a
pre-processor directive set.

Here's how I would rewrite your example:

try:
from collections import deque
except ImportError:
class deque(list):
def popleft(self):
return self.pop(0)

# in your method:
return self.myDeque.popleft()

That way the performance impact almost vanishes for the newer python and
your code is less cluttered with if ... else statements that are only there
to pick the version specific code.

Peter
 
K

Kent Johnson

Russell said:
After some digging it seems that python does not have any equivalent to
C's #if directives, and I don't get it...

For example, I've got a bit of python 2.3 code that uses
collections.deque.pop(0) in order to pop the leftmost item. In python
2.4 this is no longer valid - there is no argument on pop (rightmost
only now) and you call .popleft() instead.

I would like my code to work in both versions for now and simply want
to add code like:

if sys.version[:3] == "2.3":
return self.myDeque.pop(0)
else:
return self.myDeque.popleft()

Often you can make these tests one-time by defining an appropriate
object whose value depends on the test, then using that object instead
of repeatedly making the test. In this case, I assume the code above is
inside a class method. You could have the class conditionally define a
myPopLeft() method like this (not tested):

class MyClass(object):
...
if sys.version[:3] == "2.3":
def myPopLeft(self):
return self.myDeque.pop(0)
else:
def myPopLeft(self):
return self.myDeque.popleft()

Now the class has a myPopLeft() method that does the right thing, and
sys.version is only tested once, at class declaration time.

You might want to make the condition explicitly on the deque class,
also, using has_attr(deque, 'popleft').

Another example is the common code used to define 'set' portably across
Python 2.3 and 2.4:

try:
set
except NameError:
from sets import Set as set


In general the idea is to move the test from 'every time I need to do
something' to 'once when some name is defined'.

Kent
 
R

Russell Warren

the collections module was added in 2.4

Ah... sorry about that. I should have checked my example more closely.
What I'm actually doing is rebinding some Queue.Queue behaviour in a
"safe" location like this:

def _get(self):
ret = self.queue.popleft()
DoSomethingSimple()
return ret

And self.queue.popleft() used to be self.queue.pop(0). self.queue is a
deque object in 2.4 so I just used transferred that unluckily to the
post in a poor attempt to simplify it - I had no clue that it was new.
Should have made one up from scratch.

Anyway - it worked... you've answered my question perfectly, thanks. I
hadn't considered that the module loading phase could basically used
for preprocessing. You even helped by subtly providing a better
version checker... I didn't even know you could use < with a tuple like
that. I'll have to look into the logic rules there.

Russ
 
R

Russell Warren

Thanks guys - all great responses that answered my question in a few
different ways with the addition of some other useful tidbits!

This is a nice summary:
In general the idea is to move the test from 'every time I need to do
something' to 'once when some name is defined'.

Gotta love the response time of this newsgroup...

Russ
 
C

Carl Banks

Russell said:
Ah... sorry about that. I should have checked my example more closely.
What I'm actually doing is rebinding some Queue.Queue behaviour in a
"safe" location like this:

def _get(self):
ret = self.queue.popleft()
DoSomethingSimple()
return ret

What you should have done is call the base class's _get method, like
this:

def _get(self):
ret = Queue._get(self)
DoSomethingSimple()
return ret

This'll work in 2.3, 2.4, and if they were to change the type of the
queue again in 2.5, it would still work without any changes. Don't
redo the work the base class does if you don't have to.


Carl Banks
 
C

Cameron Laird

.
.
.
Anyway - it worked... you've answered my question perfectly, thanks. I
hadn't considered that the module loading phase could basically used
for preprocessing. You even helped by subtly providing a better
version checker... I didn't even know you could use < with a tuple like
that. I'll have to look into the logic rules there.
.
.
.
I'm working on an article on this topic, as it happens.
Yes, in general pre-processors are strictly inferior for
all realistic use-cases. Any time you think you need a
pre-processor in Python, you should ask for help to learn
of the better way that Python affords.

Even while writing this, I realize that some people have
implemented pre-processors for their Python, and expressed
satisfaction with the result. The cases familiar to me
had to do syntactic style--folks who don't like significant
white space, and so on.
 
R

Russell Warren

Yes, I definitely should have done that for that case. I'm not
entirely sure why I didn't. If I had, though, I may not have been
prompted to ask the question and get all the other great little
tidbits!
 
M

Michael Tobis

Well, Bill Mill and I simultaneously and independently decided to write
a preprocessor to strip out the unfortunate "@" decorator syntax. I
think we were both aiming at a polemic purpose rather than a practical
one, but as time fades it seems less clear what we were simultaneously
inspired to achieve. Each of us did a crude pass at it and then on
encountering each other's approach realized a truly satisfactory
implementation would probably be harder than it was worth...

In any case isn't ipython an instance of a useful preprocessor?

mt
 

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,994
Messages
2,570,222
Members
46,810
Latest member
Kassie0918

Latest Threads

Top