A bundle of questions from a Python newbie

Hans Nowak wurmy at earthlink.net
Thu Feb 21 05:42:30 CET 2002


"Gonçalo Rodrigues" wrote:

> 1. The way I thought that an assignment like
> 
> var += 1
> 
> worked was that the interpreter would go to the place pointed by var and
> add 1. In particular, id(var) should remain constant. But this is not
> what happens as a simple test shows it. So what is wrong in my
> perception of what += does? Is this an implemetation issue?

There is no "place pointed by var". Python is a bit different than most
languages in this respect. An "assignment" like

  x = 1

creates an integer object with value 1, then attaches the name
'x' to it in the current namespace. In, say, a Pascal tutorial,
you will learn that a variable refers to a reserved place in
memory, and various values can be written to that place. Not
so in Python. When you do

  x = 2

later, the same thing happens: integer object with value 2
is created, and the name 'x' is associated with it. Since
this is a new integer, id(x) will have a different value
than before. (The original integer with value 1 may, or
may not, still exist, but 'x' isn't associated with it
anymore.)

Now += is a bit of a different issue, because in the
case of mutable objects, it's capable of changing the
object in-place. Take a list, for example:

>>> a = [1, 2, 3]
>>> id(a)
9586800
>>> a += [4]
>>> a
[1, 2, 3, 4]
>>> id(a)
9586800

a has the same id before and after the +=. This is
different from the = operator, that always (re)binds
to a new name:

>>> a = a + [5]
>>> a
[1, 2, 3, 4, 5]
>>> id(a)
9902208

+= has the same behavior for immutable objects, like
integers and strings:

>>> x = 1
>>> id(x)
7626688
>>> x += 2
>>> x
3
>>> id(x)
7626496

By now you probably think, what the hell is the guy
talking about?! :-) See this document for a clear and
concise description of how Python's object and
name binding mechanism works:

  http://www.effbot.org/guides/python-objects.htm  

> 2. What is the idea of having in the loop
> 
> for var in range(10):
>     pass
> 
> the var variable still available after it? Wasn't it supposed to die (as
> in being out of scope) when the loop ends? 

This is not uncommon in other languages either, if I recall
correctly. Either way, no, it's not out of scope. It only 
goes out of scope if the current function/method/class body 
ends.

> 3. This is not a question just a small complaint. I really miss the
> do-until control flow in Python. I always have to waste some extra brain
> cycles to convert it to the while 1, etc. idiom. Hey, we all have a
> right to our complaints, right? ;-)

This one comes up every once in a while. The usual reply is that a
do-while construction can easily be mimicked using a while. Your
mileage may vary, but it's probably pointless to argue for this
feature on c.l.py, unless you like long flamewars. ;-)

> 6. Is there a reason why 3.__abs__() does not work? After all 3 is an
> object (in the wider sense of the word). And it does work if we assign
> it to a variable, eg,
> 
> var = 3
> var.__abs__()
> 
> I know that code like the above (methods applied to number literals) is
> probably rare, but consistence is also a good thing.

I believe this is something of a parsing issue. Not sure, though.
Either way, I don't think there are many people that want to write
3.__abs__...

> 9. What would be better in terms of speed?
> 
> for i in range(<whatever>):
>     <whatever>
> 
> Or to code an iterator like
> 
> from __future__ import generators
> 
> class Range:
>     def __init__(self, end, start = 0, step = 1):
>         self.end = end
>         self.start = start
>         self.step = step
> 
>     def __iter__(self):
>         i = self.start
>         while i < self.end:
>             yield i
>             i += self.step
> 
> and then use it in the for loop.

I'd say that normally it's fastest to loop over the
list. Iterators and generators are useful when you have
a possibly really long list that you don't want to have
in memory all at once. AFAIK, they're not faster than
looping over a sequence. I didn't test this though.
c.l.py has some incorrigible benchmarkers who could
prove me wrong... ;-)

> 10. I have a class that works like a list in the sense that supports the
> method __getitem__ and its relatives. But I'm at a loss as to what I
> have to do to support the slice notation.

__getitem__(self, x) will be called with a so-called slice
object if you call your instance with the slice notation.
Example:

>>> import types
>>> class Foo:
	def __init__(self, x):
		self.data = x
	def __getitem__(self, x):
		if type(x) == types.IntType:
			return self.data[x]
		elif type(x) == types.SliceType:
			return self.data[x.start:x.stop]
		else:
			raise IndexError

		
>>> f = Foo(range(10))
>>> f[1]
1
>>> f[:2]
[0, 1]

HTH,

-- 
Hans (base64.decodestring('d3VybXlAZWFydGhsaW5rLm5ldA==')) 
# decode for email address ;-)
The Pythonic Quarter:: http://www.awaretek.com/nowak/



More information about the Python-list mailing list