Python dumps core with this.. how come?

Michael Hudson mwh21 at cam.ac.uk
Sat Jan 22 11:09:19 EST 2000


Pre-point: Python is Python, not some other programming language you
may have used in the past. When you are writing Python code, try to
write Python, not brutally translate C (or whatever) to Python. This
can be tricky, depending on how ingrained your habits are, but it's
the only path to happiness.

katz at glue.umd.edu (Roey Katz) writes:

> On Sat, 22 Jan 2000 09:50:58 +0100, "Fredrik Lundh"
> <fredrik at pythonware.com> wrote:
> 
> >looks like a recursive call in getattr (if "res" is
> >not already available in the instance, it asks
> >__getattr__ to look it up).
> 
> Thanks, I got it right after I posted :)
> this brings up another concern. Given
> the following definition:
> 
>    class A:
> 
>       def __getattr__( self, name ):
>         return -1    
>       def __setattr__( self, name, val ):
>          self.__dict__[ name ] = val
> 
> I see that self.__dict__ must be a special variable
> in that setting it somehow avoids a recursive call 
> to __setattr__()? 

Yup. Look in Objects/classobject.c around about line 580 if you want
the gory details.

> 
> 
> >> Also, I have another question (very old, and I can't find this in the
> >> FAQ):  I want to explicitly take a reference to an integer.
> >
> >1. reset your brain ;-)

This is the best advice in /F's post.

> >2. variables are named references, not small boxes
> >   that hold data values
> >3. *all* variables in python are references.  there
> >   is no other thing.
> >4. integers cannot be modified in place.
> 
> Ok, OK:  this is what I always get when I ask this question,
> but then why does this happen:
> 
>      >>> a = 3 
>      >>> b = a 
>      >>> b
>      3 
>      >>> a = 5
>      >>> b
>      3

Lets rewrite your example a bit:

d = {}
d['a'] = 3
d['b'] = d['a']
d['b'] ==> 3
d['a'] = 5
d['b'] ==> 3

That's more-or-less what's going on under the hood. The line 'a=5'
changes "what 'a' points at" and does *not* change "what" 'a' points
at. Is this helping?

> I mean, what happens if I have a complicated path 
> to a variable and I want to alias it:
> 
>     alias = long_name.hard_to_type[ 1 ].integer_value
> 
>     # arbitrary calculations that would be 
>     # torturous with any label longer than 'alias'
>     if alias > someValue:
>        result = (alias * 5 ) / (alias + 4 )
>     else:
>        result = (alias * 3 ) / (alias + 2 )

This is all fine so long as you don't need to change the value
referred to. You can alias up to the last '.', eg:

s = package.module.sub_module.object[2].sub_field
s.attr = s.attr + 1

> >in other words, Python always copies references, but
> >you cannot modify objects in place unless their inter-
> >face allows you to (numbers, tuples and strings cannot
> >be modified in place, while lists and dictionaries can --
> >the only difference is that the former doesn't provide
> >any methods that let you modify them...)
> 
> so where does this definition fit in the context I mentioned?
> I understand that if I have a function and pass to it a parameter,
> I can modify it in-place (although that I cannot modify numbers,
> strings or tuples in-place seems to me inconsistent).  
> So for example, these are roughly equivalent:
>  
>     void setValue(  int& x ) { x = 3  }    // C++
>     void setValue(  int *x  ) { *x = 3 }    // C
>     def  setValue( x ):    x = 3             # Python
> 
> whoops, we cannot modfiy integers in-place, how invonvenient.  Though
> we could always wrap them in a class and overload its = operator
> (wait! no = operator to overload! double inconvenience!) or have a
> specially defined set() function:
> 
>     class A:
>        def set( self, val ):
>             self.value = val
> 
>     def setValue( a ):
>        a.setValue( 5 ) 
> 
> so that works, but do you see how it is sort of polluting? I mean, the
> interface is not so simple anymore. 

None of the above are as simple as

def getValue(): return 3

I dislike this style you use in C, too. As Python has reference
semantics, you don't have the C gotcha of trying to return a four
megabyte structure on the stack, and a sensible exception system means
you don't need to have *every* function return a HRESULT <wink>.

To return more than one result, return a tuple. Or pass in some
instance object that more expresses what you are trying to acheive.

> >Q: when does Python copy a reference and not a value?
> >A: always!
> ayeee! missed it! terribly sorry :)
> 
> 
> ok, here's a third question, and it builds on the above inquiry
> regarding a lack of an assignment operator.  What happens when I want
> to do convert this for loop:
> 
>    for i,j in dest, src:
>         dest[ i ] = src[ j ]

I don't think you mean this. This is (loosely) equivalent to

dest[ dest[0] ] = src[ dest[1] ]
dest[ dest[0] ] = src[ dest[1] ]

I think you mean

for i in range(len(src)):
    dest[i] = src[i]

> into a fast map() expression:
> 
>    map( dest.assign_operator, src )

map(operator.__setitem__,[dest]*len(src),range(len(src)),src)

should do what you want;

map(dest.append,src)

or

map(dest.__setitem__,range(len(src)),src)

*may* do what you what, depending on circumtances.

Bizarrely, 

filter(dest.append,src)

is probably more memory efficient (the map form conses up a list of
return values, but as append always returns None - which is false -
filter `rejects' every element) - but if you write code like this, you
deserve to lose.

> like I mentioned -- there is no assignment operator to overload! 
> the trouble here is that there may be more situations in which 
> I cannot use  filter() to narrow down the list to only the elements
> that I want and then copy it into another list with src.copy().  

In general, don't be too obsessive about turning loops into maps and
filters. You're probably not saving as much as you think. for is far
clearer, and if every cycle is *that* precious, why Python?

(maphash #'(lambda (k v) (setf (gethash dest k) v)) src)-ly y'rs
Michael



More information about the Python-list mailing list