immutable classes [was: pre-PEP: Default Argument Expressions]
I have added python-ideas to the Cc list, and suggest removing
python-3000 from additional replies.
BJörn Lindqvist gave an example explaining why he might want to
re-evaluate mutable default arguments. It still looks like like buggy
code, but it isn't the error I was expecting -- and I think it comes
from the difficulty of declaring something immutable.
On 2/15/07, BJörn Lindqvist
On 2/15/07, Jim Jewett
wrote:
Then are there *any* good use cases for [non-persistent mutable defaults]
(1) Not really (treated as) mutable. ==> Doesn't care
>>> def f(extra_settings={}) ...
usually doesn't modify or even store extra_settings; ...
That is dangerous code. Sooner or later someone will modify the extra_settings dict.
How? >>> f.func_defaults[0]['key']=value may be misguided, but it probably isn't an accident. BJörn's example does store the mutable directly, but it makes a bit more sense because it looks like a complex object rather than just a mapping.
class Vector: def __init__(self, x, y, z): self.x = x self.y = y self.z = z
class Ray: def __init__(self, direction, origin = Vector(0, 0, 0)): self.direction = direction self.origin = origin
ray1 = Ray(Vector(0, 0, -1)) ray2 = Ray(Vector(0, 0, 1)) ray3 = Ray(Vector(-1, 0, 0), Vector(2, 3, 4))
The above code looks quite nice, but is wrong.
Why is vector mutable? Is the real problem that it is too hard to declare objects or attributes immutable? My solution is below, but I'll grant that it isn't as straightforward as I would have liked. Is this something that could be solved with a recipe, or a factory to make immutable classes?
class Vector3D(tuple): ... def __new__(self, x, y, z): ... return super(Vector3D, self).__new__(self, (x, y, z)) ... x=property(lambda self: self[0]) ... y=property(lambda self: self[1]) ... z=property(lambda self: self[2])
-jJ
"Jim Jewett"
class Vector3D(tuple): ... def __new__(self, x, y, z): ... return super(Vector3D, self).__new__(self, (x, y, z)) ... x=property(lambda self: self[0]) ... y=property(lambda self: self[1]) ... z=property(lambda self: self[2])
That looks very much like Raymond Hettinger's named tuple recipe, recently offered in python-dev: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/500261 - Josiah
Well, except that Raymond's is indeed better -- so consider this a
request for inclusion in 2.6
On 2/17/07, Josiah Carlson
"Jim Jewett"
wrote: class Vector3D(tuple): ... def __new__(self, x, y, z): ... return super(Vector3D, self).__new__(self, (x, y, z)) ... x=property(lambda self: self[0]) ... y=property(lambda self: self[1]) ... z=property(lambda self: self[2])
That looks very much like Raymond Hettinger's named tuple recipe, recently offered in python-dev:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/500261
-jJ
On the 'using old semantics when you really want to' part, that's very
well possible with a decorator under the proposed semantics:
def caching(**cachevars):
def inner(func):
def wrapper(**argdict):
for var in cachevars:
if not var in argdict:
argdict[var] = cachevars[var]
return func(**argdict)
return wrapper
return inner
@caching(cache={})
def foo(in, cache):
result = bar(in)
cache[in] = result
return result
This implementation of caching doesn't handle positional args, but it can
be made to. One such decorator would still be a net win of several hundred
lines of code in the standard lib.
Of course, IMHO, the real fix to this is to 1) have default expressions be
evaluated at calltime, and 2) have _all_ lexical variables be bound at
definition time and 3) make them immutable. Then something like
lst = []
for i in range(10):
lst.append(lambda i: i*i)
would work. That would be a real win for functional programming. (good
thing)
Unfortunately Guido's decided not to support (1), and (2) has been
proposed some time ago and didn't make it. In both cases because it would
be to big a departure from how Python currently works. (3) is quite
impossible in a language like python.
"Jan Kanis"
lst = [] for i in range(10): lst.append(lambda i: i*i)
You must mean something like... lambda j: i*j
I just hope if python were designed today it would have done these. </mode>
Probably not. Value binding breaks closures. - Josiah
On Tue, 20 Feb 2007 00:17:53 +0100, Josiah Carlson
"Jan Kanis"
wrote: [snip] lst = [] for i in range(10): lst.append(lambda i: i*i)
You must mean something like... lambda j: i*j
yes. my mistake.
I just hope if python were designed today it would have done these. </mode> Probably not. Value binding breaks closures.
That depends on how you exactly define closures. The basics of having an inner function with free variables and initializing those free variables to the values they have in the parent scope still works.
- Josiah
On Tue, 20 Feb 2007 14:28:23 +0100, Nick Coghlan
Jan Kanis wrote:
I just hope if python were designed today it would have done these. </mode>
If Python had done these, it wouldn't be Python ;)
True. Let's make the better-than-python language. (aiming high) ;)
"Jan Kanis"
On Tue, 20 Feb 2007 00:17:53 +0100, Josiah Carlson
wrote: "Jan Kanis"
wrote:
I just hope if python were designed today it would have done these. </mode> Probably not. Value binding breaks closures.
That depends on how you exactly define closures. The basics of having an inner function with free variables and initializing those free variables to the values they have in the parent scope still works.
It would break closures as defined for Python version 1.? to Python 2.5, Python 3.x, and beyond. Changing the semantics of Python closures is not the right thing to do, whether in 2.6, 3.x or otherwise. - Josiah
On Sun, 25 Feb 2007 23:45:08 +0100, Josiah Carlson
"Jan Kanis"
wrote: On Tue, 20 Feb 2007 00:17:53 +0100, Josiah Carlson
wrote: "Jan Kanis"
wrote:
I just hope if python were designed today it would have done these. </mode> Probably not. Value binding breaks closures.
That depends on how you exactly define closures. The basics of having an inner function with free variables and initializing those free variables to the values they have in the parent scope still works.
It would break closures as defined for Python version 1.? to Python 2.5, Python 3.x, and beyond.
Changing the semantics of Python closures is not the right thing to do, whether in 2.6, 3.x or otherwise.
Yup. That's why I used '
Jan Kanis wrote:
python were designed today it would have done these. </mode>
If Python had done these, it wouldn't be Python ;) There are many, many programming language design decisions which have good arguments on each side (and some which seem obviously correct may involve hidden costs which aren't appreciated until after it is too late to change them). That's one of the major reasons why there are so many different programming languages out there. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
participants (4)
-
Jan Kanis
-
Jim Jewett
-
Josiah Carlson
-
Nick Coghlan