Re: Lots o' Questions

Guido.van.Rossum@cwi.nl
Mon, 25 Jul 1994 16:29:29 +0200

Lots o' Good Questions! After reading Jim Roskind's excellent reply,
I was inspired to add some answers to the FAQ (sometimes borrowing
Jim's words).

Here is an itemized reply, with quotes from the new FAQ when applicable:

> 1) Why can't some functions be applied to immutable types?
> Example: Applying list methods "index" or "count" to strings.
> Even more to the point, definging "remove", "reverse" or "sort" for
> strings such that they return a new strings.

6.6. Q. Why don't strings have methods like index() or sort(), like
lists?

A. Good question. Strings currently don't have methods at all
(likewise tuples and numbers). Long ago, it seemed unnecessary to
implement any of these functions in C, so a standard library module
"string" written in Python was created that performs string related
operations. Since then, the cry for performance has moved most of
them into the built-in module strop (this is imported by module
string, which is still the perferred interface, without loss of
performance except during initialization). Some of these functions
(e.g. index()) could easily be implemented as string methods instead,
but others (e.g. sort()) can't, since their interface prescribes that
they modify the object, while strings are immutable (see the previous
question).

> 2) Why make strings immutable in the first place? I understand why
> from an implementation perspective (moving pointers), but what are
> some other reasons?

6.6. Q. Why don't strings have methods like index() or sort(), like
lists?

A. Good question. Strings currently don't have methods at all
(likewise tuples and numbers). Long ago, it seemed unnecessary to
implement any of these functions in C, so a standard library module
"string" written in Python was created that performs string related
operations. Since then, the cry for performance has moved most of
them into the built-in module strop (this is imported by module
string, which is still the perferred interface, without loss of
performance except during initialization). Some of these functions
(e.g. index()) could easily be implemented as string methods instead,
but others (e.g. sort()) can't, since their interface prescribes that
they modify the object, while strings are immutable (see the previous
question).

> 3) Why aren't all types treated as objects, (i.e. strings)?
> Why can't I write: "a string".length()

6.6. Q. Why don't strings have methods like index() or sort(), like
lists?

A. Good question. Strings currently don't have methods at all
(likewise tuples and numbers). Long ago, it seemed unnecessary to
implement any of these functions in C, so a standard library module
"string" written in Python was created that performs string related
operations. Since then, the cry for performance has moved most of
them into the built-in module strop (this is imported by module
string, which is still the perferred interface, without loss of
performance except during initialization). Some of these functions
(e.g. index()) could easily be implemented as string methods instead,
but others (e.g. sort()) can't, since their interface prescribes that
they modify the object, while strings are immutable (see the previous
question).

> 4) Why can't I descend from built-in classes? Why can't I derive a
> class from the built-in "file" class? It would save me the trouble of
> creating a wrapper class to add functionality (like a "getchar()"
> type function.

6.8. Q. Why can't I derive a class from built-in types (e.g. lists or
files)?

A. This is caused by the relatively late addition of (user-defined)
classes to the language -- the implementation framework doesn't easily
allow it. See the answer to question 4.2 for a work-around. This
*may* be fixed in the (distant) future.

4.2. Q. Can I create an object class with some methods implemented in
C and others in Python (e.g. through inheritance)? (Also phrased as:
Can I use a built-in type as base class?)

A. No, but you can easily create a Python class which serves as a
wrapper around a built-in object, e.g. (for dictionaries):

# A user-defined class behaving almost identical
# to a built-in dictionary.
class UserDict:
def __init__(self): self.data = {}
def __repr__(self): return repr(self.data)
def __cmp__(self, dict):
if type(dict) == type(self.data):
return cmp(self.data, dict)
else:
return cmp(self.data, dict.data)
def __len__(self): return len(self.data)
def __getitem__(self, key): return self.data[key]
def __setitem__(self, key, item): self.data[key] = item
def __delitem__(self, key): del self.data[key]
def keys(self): return self.data.keys()
def items(self): return self.data.items()
def values(self): return self.data.values()
def has_key(self, key): return self.data.has_key(key)

> 5) Why do scoping rules require an explicit scope specification for
> everything except: self.<class_var> ? Example:
> class my_class:
> i = 1
> def f(self):
> self.j = self.i + 1
>
> Here, "j" is an instance variable and "i" is a class variable,
> shared among all instances of the class. They are accessed with
> the same syntax. Why?
> 6) Why make "self" (or whatever) a mandatory formal parameter in
> methods? Why not make "self" a predefined identifier within methods?

6.9. Q. Why must 'self' be declared and used explicitly in method
definitions and calls?

A. By asking this question you reveal your C++ background. :-)
When I added classes, this was (again) the simplest way of
implementing methods without too many changes to the interpreter. I
borrowed the idea from Modula-3. It turns out to be very useful, for
a variety of reasons.

First, it makes it more obvious that you are using a method or
instance attribute instead of a local variable. Reading "self.x" or
"self.meth()" makes it absolutely clear that an instance variable or
method is used even if you don't know the class definition by heart.
In C++, you can sort of tell by the lack of a local variable
declaration (assuming globals are rare or reasily recognizable) -- but
in Python, there are no local variable declarations, so you'd have to
look up the class definition to be sure.

Second, it means that no special syntax is necessary if you want to
explicitly reference or call the method from a particular class. In
C++, if you want to use a method from base class that is overridden in
a derived class, you have to use the :: operator -- in Python you can
write baseclass.methodname(self, <argument list>). This is
particularly useful for __init__() methods, and in general in cases
where a derived class method wants to extend the base class method of
the same name and thus has to call the base class method somehow.

Lastly, for instance variables, it solves a syntactic problem with
assignment: since local variables in Python are (by definition!) those
variables to which a value assigned in a function body (and that
aren't explicitly declared global), there has to be some way to tell
the interpreter that an assignment was meant to assign to an instance
variable instead of to a local variable, and it should preferably be
syntactic (for efficiency reasons). C++ does this through
declarations, but Python doesn't have declarations and it would be a
pity having to introduce them just for this purpose. Using the
explicit "self.var" solves this nicely. Similarly, for using instance
variables, having to write "self.var" means that references to
unqualified names inside a method don't have to search the instance's
directories.

> 7) More about contexts: having to fully specify variable contexts
> really bugs me. The more Python I write, the more it bugs me.
> It is correlated. It is a trend. I am bugged. Because: I prefer
> a language that does the work for me. Why isn't there a predefined
> context search order?

I'm sorry, I'm lost here. Maybe you can give an example of a
situation where you think the language could do more work for you?

> 8) Why can't the debugger (pdb) understand the syntax:
> break <class>.<method>
> Instead I have to type:
> break <class>.__dict__['<method>']
> 9) Why does "cl" (clear all breaks) bomb? (pdb)

These are bugs. The following patch should fix both:

*** 1.9.2.3 1994/06/03 15:41:39
--- bdb.py 1994/07/14 14:00:55
***************
*** 203,209 ****
return 'There are no breakpoints in that file!'
del self.breaks[filename]

! def clear_all_breaks(self, filename, lineno):
if not self.breaks:
return 'There are no breakpoints!'
self.breaks = {}
--- 203,209 ----
return 'There are no breakpoints in that file!'
del self.breaks[filename]

! def clear_all_breaks(self):
if not self.breaks:
return 'There are no breakpoints!'
self.breaks = {}
***************
*** 247,253 ****
frame, lineno = frame_lineno
filename = frame.f_code.co_filename
s = filename + '(' + `lineno` + ')'
! s = s + frame.f_code.co_name
if frame.f_locals.has_key('__args__'):
args = frame.f_locals['__args__']
if args is not None:
--- 247,256 ----
frame, lineno = frame_lineno
filename = frame.f_code.co_filename
s = filename + '(' + `lineno` + ')'
! if frame.f_code.co_name:
! s = s + frame.f_code.co_name
! else:
! s = s + "<lambda>"
if frame.f_locals.has_key('__args__'):
args = frame.f_locals['__args__']
if args is not None:

*** 1.17.2.4 1994/06/03 15:41:41
--- pdb.py 1994/07/14 14:01:00
***************
*** 86,94 ****
except:
# Try function name as the argument
import codehack
- g_frame = self.curframe.f_globals
try:
! code = eval(arg, g_frame).func_code
except:
print '*** Could not eval argument:', arg
return
--- 86,97 ----
except:
# Try function name as the argument
import codehack
try:
! func = eval(arg, self.curframe.f_globals,
! self.curframe.f_locals)
! if hasattr(func, 'im_func'):
! func = func.im_func
! code = func.func_code
except:
print '*** Could not eval argument:', arg
return
***************
*** 170,179 ****
do_q = do_quit

def do_args(self, arg):
! if self.curframe.f_locals.has_key('__return__'):
! print `self.curframe.f_locals['__return__']`
else:
! print '*** Not arguments?!'
do_a = do_args

def do_retval(self, arg):
--- 173,182 ----
do_q = do_quit

def do_args(self, arg):
! if self.curframe.f_locals.has_key('__args__'):
! print `self.curframe.f_locals['__args__']`
else:
! print '*** No arguments?!'
do_a = do_args

def do_retval(self, arg):

> That's all for now. I'm not about to stop using Python because of
> these issues. I would like an explaination of the various design aspects.
> Hope no one is really touchy about these issues. Guess I'll find out.

Not touched -- pleased to provide answers!

--Guido van Rossum, CWI, Amsterdam <Guido.van.Rossum@cwi.nl>
<URL:http://www.cwi.nl/cwi/people/Guido.van.Rossum.html>