On Saturday, Mar 15, 2003, at 01:35 US/Eastern,
python-dev-request(a)python.org wrote:
> This has its downside too though. A function designed to take an
> immutable class instance cannot rely on the class instance to remain
> unchanged, because the caller could pass it an instance of the
> corresponding mutable subclass! (For example, the function might use
> the argument as a dict key.) In some sense this inheritance pattern
> breaks the "Liskov substibutability" principle: if B is a base class
> of C, whenever a B instance is expected, a C instance may be used.
In practice, this isn't an issue though it does require that the
developer follow a couple of simple patterns. Since Objective-C is a
C derived language, requiring the developer to follow a couple of extra
simple patterns isn't a big deal considering that the developer already
has to deal with all of the really fun memory management issues
associated with a pointer based language.
Namely, if your code takes an array-- for example-- and is going to
hang on to the reference for a while and expect immutability, simply
copy the array when storing it away:
- (void) setSearchPath: (NSArray *) anArray
{
if (searchPath != anArray) {
[searchPath release];
searchPath = [anArray copy];
}
}
If anArray is mutable, the invocation of -copy creates an immutable
copy of the array without copying its contents. If anArray is
immutable, -copy simply returns the same array with the reference count
bumped by one:
// NSArray implementation
- copy
{
return [self retain];
}
Easy and efficient, as long as the developer remembers to follow the
pattern....
b.bum
Just writes:
> It's not so much the inheritance hierarchy that I like about the Cocoa
> core classes, but the fact that mutability is a prominent part of the
> design. I think Python would be a better language if it had a mutable
> string type as well as a mutable byte-oriented data type. An immutable
> dict would be handy at times. An immutable list type would be great,
> too. Wait, we already have that.
I've often had the same thought myself. I'm imagining designing my
own language, and I note that both mutable and immutable strings are
handy, depending on what you're doing. The same is true of data
containers (of all sorts, lists and dicts being examples). "What the
heck?" I say to myself, "In *my* perfect language, there'll be
mutable and immutable versions of every object. (With the obvious
conversion behavior.) Why, you won't even have to code them
separately... just specify some property indicating whether or not
that instance is mutable."
Then I realize that C++ has exactly this feature (it's called "const"),
and that I find it to be an annoyance far more often than I find it
handy. And I begin to question.
Wish-I-knew-the-answer-but-I-haven't-been-enlightened-yet
-- Michael Chermside
GvR wrote:
>
> This has its downside too though. A function designed to take an
> immutable class instance cannot rely on the class instance to remain
> unchanged, because the caller could pass it an instance of the
> corresponding mutable subclass! (For example, the function might use
> the argument as a dict key.) In some sense this inheritance pattern
> breaks the "Liskov substibutability" principle: if B is a base class
> of C, whenever a B instance is expected, a C instance may be used.
Indeed!
Presumably the designers of the NextStep libraries thought to themselves that
they couldn't do it the other way (have NSArray subclass NSMutableArray) because
NSArray couldn't provide a real implementation of a mutation method like
"NSArray addObject".
If you include the immutability guarantee as well as the methods in the
"contract" offered by an interface, then its clear that neither can be a
Liskov-substitution-principle-preserving subtype of the other.
The E Language paid careful attention to this issue because a surprise about
mutability could easily be a security hole. Their solution is quite Pythonic,
inasmuch as type-checking is dynamic, structural (an object matches a type if it
offers the interface regardless of whether it is explicitly declared to be a
subtype), and soft (an object can implement only part of a type).
These are the three noble features of Python's type system. (I occasionally
hear about efforts to cripple Python's type system in order to make it as
ungainly as Java's, but fortunately they always seem to fade away...)
So in E, it's the same: if you are expecting a mutable list (a "FlexList") and
you get an immutable one, you'll get an exception at run-time if you try a
mutation operation like mylist.append("spam").
Like Python, E's strings do the right thing if you invoke immutable list
("ConstList") methods on them.
The syntax for constructing maps and lists and indexing them is similar to
Python's. That syntax always constructs immutable structures, a mutable version
of which is generated with the method "mylist.diverge()". To get an immutable
version of a mutable structure, you write "mylist.snapshot()".
http://erights.org/elang/quick-ref.html#Structures
Regards,
Zooko
http://zooko.com/
^-- newly and incompletely restored
I was recently surprised by:
Python 2.3a2+ (#1, Feb 24 2003, 15:02:10)
[GCC 3.2 20020927 (prerelease)] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> xrange(2 ** 32)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
OverflowError: long int too large to convert to int
Now that we have a kind of long/int integration, maybe it makes sense
to update xrange()? Or is that really a 2.4 feature?
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
Line oriented network protocols are very common, and I often find myself
calling the socket makefile method so I can read complete lines from a
socket. I'm probably not the first one who's wished that socket objects
where more file-like.
While I don't think we'd want to go as far as to turn them into a stdio
based file object, it might make sense to allow them to be iterated over
(and add a .readline() method, I guess). This would necessitate adding some
input buffering, which will complicate things like the .recv() method, so
I'm not sure it's that good an idea, but it removes one gotchya for
neophytes (and forgetful veterans). Thoughts?
--
Andrew McNamara, Senior Developer, Object Craft
http://www.object-craft.com.au/
>> Line oriented network protocols are very common, and I often find myself
>> calling the socket makefile method so I can read complete lines from a
>> socket. I'm probably not the first one who's wished that socket objects
>> where more file-like.
>>
>> While I don't think we'd want to go as far as to turn them into a stdio
>> based file object, it might make sense to allow them to be iterated over
>> (and add a .readline() method, I guess). This would necessitate adding some
>> input buffering, which will complicate things like the .recv() method, so
>> I'm not sure it's that good an idea, but it removes one gotchya for
>> neophytes (and forgetful veterans). Thoughts?
>
>Um, why doesn't the makefile() method do what you want?
The short answer is that it does, but not very tidily - by turning the
socket object into a file object, I lose the original socket object
functionality (for example, shutdown()).
At another level, the concept of a "file-like" object is a very common
python idiom - socket is the odd one out these days.
It's really not a big deal - we could regularise the interface at the
cost of more implementation complexity.
--
Andrew McNamara, Senior Developer, Object Craft
http://www.object-craft.com.au/
>> The short answer is that it does, but not very tidily - by turning the
>> socket object into a file object, I lose the original socket object
>> functionality (for example, shutdown()).
>
>You can just keep the socket around though.
Yes. Which has always struck me as slightly ugly.
>> At another level, the concept of a "file-like" object is a very common
>> python idiom - socket is the odd one out these days.
>>
>> It's really not a big deal - we could regularise the interface at the
>> cost of more implementation complexity.
>
>I'm not sure if I'd call that regularizing. It would by necessity
>become some kind of odd mixture.
I guess you would keep the send() and recv() interfaces for raw access, and
add read(), write(), readlines(), etc, which would be buffered. I'd chose
to then view it as a superset of a file-like object.
>In any case, I find the file abstraction a bit arcane too. Maybe we
>should strive to replace all these with something better in Python 3.0, to
>be prototyped in the standard library starting with 2.4.
And get rid of stdio along the way, with any luck... 8-)
It would also be nice to make the buffering play nicely with
select()/poll()-threaded applications... if we're talking about
wishlists... 8-)
--
Andrew McNamara, Senior Developer, Object Craft
http://www.object-craft.com.au/
[this was sent to python-list, but i'm re-posting here as told by Skip]
hello,
i had a quick look at Objects/abstract.c in 2.2.2's source. almost
every function there checks whether the objects it's passed are not
NULL. if they are, SystemError exception occurs. since i've never come
across such exception i've commented out those checks.
the resulting python binary did 6.5% more pystones on average (the
numbers are below). my question is: are those checks really necessary
in non-debug python build?
the pystone results:
BEFORE:
$ for (( i = 0; i <= 5; i++ )); do ./pystone.py; done
Pystone(1.1) time for 10000 passes = 0.6
This machine benchmarks at 16666.7 pystones/second
Pystone(1.1) time for 10000 passes = 0.56
This machine benchmarks at 17857.1 pystones/second
Pystone(1.1) time for 10000 passes = 0.58
This machine benchmarks at 17241.4 pystones/second
Pystone(1.1) time for 10000 passes = 0.57
This machine benchmarks at 17543.9 pystones/second
Pystone(1.1) time for 10000 passes = 0.57
This machine benchmarks at 17543.9 pystones/second
AFTER:
$ for (( i = 0; i <= 5; i++ )); do ./pystone.py; done
Pystone(1.1) time for 10000 passes = 0.54
This machine benchmarks at 18518.5 pystones/second
Pystone(1.1) time for 10000 passes = 0.57
This machine benchmarks at 17543.9 pystones/second
Pystone(1.1) time for 10000 passes = 0.55
This machine benchmarks at 18181.8 pystones/second
Pystone(1.1) time for 10000 passes = 0.52
This machine benchmarks at 19230.8 pystones/second
Pystone(1.1) time for 10000 passes = 0.52
This machine benchmarks at 19230.8 pystones/second
Pystone(1.1) time for 10000 passes = 0.54
--
fuf (fuf(a)mageo.cz)
Tim Peters <tim.one(a)comcast.net> writes:
> Question: I don't have VC7 and don't know what it does. The traceback
> ended in MSVCRTD.DLL, which I recognize as MS's debug-mode C runtime DLL for
> VC6. Does VC7 use the same DLL name, or some other DLL name?
The same one.
> If the latter, my theory is that PyObject_New used the MSVC6 malloc,
> but that PyObject_NEW used the MSCV7 malloc (due to macro expansion
> in your code).
Brilliant theory!
> In both cases the MSVC6 free() gets called.
Ah, correct. I misread "someone's" code; the delete function just
calls PyObject_Del(). I think "someone" probably ought to do
something more explicit to control where things are allocated/freed.
But for now, I think using PyObject_New/PyObject_Del is reasonable.
> But the MSVC6 and MSVC7 heaps are distinct, so the debug-mode MSVC6
> free() complains because it wasn't the source of the memory getting
> freed. A missing piece of the puzzle: what was the error msg at the
> time this thing died?
unhandled exception at 0x10213638 (MSVCRTD.DLL) in python_d.exe: User
breakpoint.
It seems to me that in light of all this, it's probably worth noting
this difference between PyObject_New and PyObject_NEW in the docs.
People *will* develop extension modules with different compilers from
the one Python was compiled with... I know, submit a patch.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
Someone I work with recently caused a test to start asserting in VC7's
instrumented free() call, using a pydebug build. He explained the
change this way:
"I switched from PyObject_New to PyObject_NEW, which according to it's
documentation omits the check for type_object != 0 and consequently
should run a little bit faster"
[he doesn't ever pass 0 as the typeobject]
Did he miss some other important fact about PyObject_NEW? Does the
doc need to be fixed?
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com