First of all I'm new to this. I tried figuring out if some inquiry like mine below already was posted but I couldn't find it, frankly partly because I don't know what to look for, I'm not sure if there's a name for this idea. I'm not convinced my idea below is solid so apologies if it's naïve but I figured to post it anyway. It has to do with the possibility to fold typical opcodes pairs by introducing a language construct. The idea is to be able to write this code: myobject.a myobject.b myobject.c() myobject.d = 1 like this: using myobject: .a .b .c() .d = 1 The first version would lead to these instructions: LOAD_FAST 0 (self) LOAD_ATTR 0 (a) POP_TOP LOAD_FAST 0 (self) LOAD_ATTR 1 (b) POP_TOP LOAD_FAST 0 (self) LOAD_ATTR 2 (c) CALL_FUNCTION 0 POP_TOP LOAD_CONST 1 (1) LOAD_FAST 0 (self) STORE_ATTR 3 (d) LOAD_CONST 0 (None) the using keyword would grab an internal handle to the object, leading to the ability to reduce the number of opcodes by introducing folded ones: PUSH_HANDLE 0 (myobject) LOAD_HATTR 0 (a) POP_TOP LOAD_HATTR 1 (b) POP_TOP LOAD_HATTR 1 (b) CALL_FUNCTION 0 (c) POP_TOP LOAD_CONST 1 (1) STORE_HATTR 3 (d) POP_HANDLE LOAD_CONST 0 (None) The rationale behind is that both typographically for the programmer this is more elegant than typing the variable name over and again. For the internals of Python it would reduce the number of handled opcodes at the cost of a number of new opcodes: PUSH_HANDLE => pushes object to 'direct handle stack' LOAD_HATTR = LOAD_FAST + LOAD_ATTR STORE_HATTR = LOAD_FAST + STORE_ATTR POP_HANDLE => pops object to 'direct handle stack' Since these pairs are quite numerous this could lead to a speed gain for code that uses a lot of "self." or "object." invocations. Shoot me. Robert
Robert van Geel wrote:
using myobject: .a .b .c() .d = 1
The rationale behind is that both typographically for the programmer this is more elegant than typing the variable name over and again.
Suggestions like this have been made before, and the conclusion has always been that very little would be gained over using a short intermediate name, e.g. m = myobject m.a() m.b() m.c() m.d = 1 That's just as readable and almost as easy to type. It also has the advantage that you're not restricted to just one "easy acess" object at a time. The only new thing in your proposal is the change to the bytecode, and that could be achieved by treating it as an optimisation. A sufficiently smart code generator could notice that you were repeatedly operating on the same object and produce the bytecode you suggest. -- Greg
On Mon, May 2, 2016 at 9:56 AM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
The only new thing in your proposal is the change to the bytecode, and that could be achieved by treating it as an optimisation. A sufficiently smart code generator could notice that you were repeatedly operating on the same object and produce the bytecode you suggest.
"self" was mentioned. I wonder whether there could be some additional optimization magic applied to a function's first argument, same as the no-arg super() call? Since it's a semantic-free optimization, any Python implementation would be free to mess with this. Have opcodes for "LOAD_ARG1_ATTR" and "STORE_ARG1_ATTR", semantically equivalent to LOAD_FAST 0 followed by LOAD_ATTR or STORE_ATTR. ChrisA
Hello Robert, and welcome, On Sun, May 01, 2016 at 10:27:04PM +0200, Robert van Geel wrote: [...]
The idea is to be able to write this code:
myobject.a myobject.b myobject.c() myobject.d = 1
like this:
using myobject: .a .b .c() .d = 1
I think this is closely related to the Pascal "with" statement, which has a FAQ: https://docs.python.org/2/faq/design.html#why-doesn-t-python-have-a-with-sta... You require a leading dot to access attribute names, which avoids the ambiguity between attributes and variables, but it also fails the "new syntax shouldn't look like grit on Tim's monitor" test. using myobject: .method(.eggs + (.spam or ham) - tomato - .cheese) All of these proposals raise the question, what do you do with nested blocks? using myobject: print(.attr) using yourobject: print(.attr) Obviously in the outer block, ".attr" refers to myobject. My guess is that inside the inner block, it refers to yourobject. What happens if you want to refer to both? Would we have to write this? using myobject: print(.attr) # myobject using yourobject: print(.attr) # yourobject print(myobject.attr) # be explicit Now imagine that you are a beginner, trying to understand this code block. What would you think it does? Might you not be surprised that the two references to ".attr" refer to different variables? And of course there is always the objection that the barrier to adding a new keyword is quite high. Somewhere out there, somebody is using "using" as a variable name (perhaps a decorator?) and making this a keyword will break her code. Is it worth it? Despite these objections, I'm cautiously interested in this. I don't think the objections are insurmountable, and if there is a significant gain in readability and performance, it may be worth while. A cautious and tentative +1. It may be worth you doing a survey of other languages and seeing if they have anything similar. -- Steve
On Sun, May 1, 2016, at 21:02, Steven D'Aprano wrote:
Now imagine that you are a beginner, trying to understand this code block. What would you think it does? Might you not be surprised that the two references to ".attr" refer to different variables?
And of course there is always the objection that the barrier to adding a new keyword is quite high. Somewhere out there, somebody is using "using" as a variable name (perhaps a decorator?) and making this a keyword will break her code. Is it worth it?
Or you could make it context-sensitive, with it being an identifier unless it's used in this construction. Not saying it's a good idea, just that it's technically an option.
Despite these objections, I'm cautiously interested in this. I don't think the objections are insurmountable, and if there is a significant gain in readability and performance, it may be worth while.
A cautious and tentative +1.
It may be worth you doing a survey of other languages and seeing if they have anything similar.
Javascript's deprecated "with" statement is superficially similar, but it doesn't use the dot, instead it basically turns the scope for non-local variables into a chained map. Visual Basic has something (also called "with") that is much more similar to what's being proposed. Is there any reason not to simply implement a performance optimization for the normal syntax? Is the behavior of code which reaches into the parent frame and modifies its local variables guaranteed?
On Mon, May 02, 2016 at 11:56:00AM +1200, Greg Ewing wrote:
Suggestions like this have been made before, and the conclusion has always been that very little would be gained over using a short intermediate name, e.g.
m = myobject m.a() m.b() m.c() m.d = 1
That's just as readable and almost as easy to type. It also has the advantage that you're not restricted to just one "easy acess" object at a time.
Multiple short, simple statements like the above are not the best demonstration of the "one letter variable name" technique, nor of the advantage of the "Pascal with" (using) keyword. We should be thinking of larger expressions, with multiple references to the same name: print(myobject.method(myobject.eggs + (myobject.spam or ham)) - myobject.tomato - cheese) which becomes either: m = myobject print(m.method(m.eggs + (m.spam or ham)) - m.tomato - cheese) del m versus: using myobject: print(.method(.eggs + (.spam or ham)) - .tomato - cheese) I don't think there's a lot between them, but if the second can be significantly more efficient, that might push it over the line. Why do I `del m` at the end? Because this might be in the global scope, not inside a function, and you might not want an extraneous variable floating around polluting the namespace.
The only new thing in your proposal is the change to the bytecode, and that could be achieved by treating it as an optimisation. A sufficiently smart code generator could notice that you were repeatedly operating on the same object and produce the bytecode you suggest.
No, I don't think it can be. Or rather, the semantics are not the same, and the compiler shouldn't choose to optimize the code. Consider: myobject.a() myobject.b() You cannot assume that it is the same myobject in both statements! Method a might have rebound the name to something else. Perhaps Victor's FAT Python will be smart enough to tell whether or not a global name myobject has been changed, but how about: myobject[1].lookup['key'].property.a() myobject[1].lookup['key'].property.b() I don't think any Python compiler is going to be able to look deep inside an arbitrarily complex reference and tell whether or not it is safe to optimize. But the programmer may be able to explicitly choose the optimization. Of course, in this case, they can just as easily factor out the constant part and assign it to a temporary short name: m = myobject[1].lookup['key'] m.property.a() m.property.b() # versus using myobject[1].lookup['key']: .property.a() .property.b() so it becomes a matter of two things: taste and performance. In my opinion, the two idioms are roughly the same in readability, give or take a few concerns about temp variables versus grit on Tim's monitor. But the first has to do two name lookups of "m", which the second can optimize. Can the second beat that by a significant amount? What if there were ten name lookups? Thirty? -- Steve
To be fair the second problem you mention already exists with nested functions: def example(x): print(x) def inner(x): print(x) suffers from exactly the same issues as the proposed example. Admittedly, the case you give is much, much less explicit since the attributes are given in the function call example but not with the attribute access one. My thoughts on this syntax would be to add a special object to the standard library, that uses the existing `with` syntax. Take this as an example: import math with Namespace(math): print(pi**2) import math with Namespace(m=math): # alternatively `Namespace(math) as m:` print(m.pi**2) import foo, bar with Namespace(f=foo, b=bar): print(f.spam) print(b.spam) import math, pprint with Namespace(math, pprint): pprint(pi**2) With all of these modifying locals within the context manager. I assume that currently this could be done with some AST rewriting, but a clean standard implementation would be perhaps nice. There are some obvious problems: name conflicts could through an error, but that then leads to really ugly nested code or kludgy workarounds. I'm not sure of good solutions to those, but I'm a +1 if that matters, assuming those can be solved. --Josh On Sun, May 1, 2016 at 9:07 PM Steven D'Aprano <steve@pearwood.info> wrote:
Hello Robert, and welcome,
On Sun, May 01, 2016 at 10:27:04PM +0200, Robert van Geel wrote: [...]
The idea is to be able to write this code:
myobject.a myobject.b myobject.c() myobject.d = 1
like this:
using myobject: .a .b .c() .d = 1
I think this is closely related to the Pascal "with" statement, which has a FAQ:
https://docs.python.org/2/faq/design.html#why-doesn-t-python-have-a-with-sta...
You require a leading dot to access attribute names, which avoids the ambiguity between attributes and variables, but it also fails the "new syntax shouldn't look like grit on Tim's monitor" test.
using myobject: .method(.eggs + (.spam or ham) - tomato - .cheese)
All of these proposals raise the question, what do you do with nested blocks?
using myobject: print(.attr) using yourobject: print(.attr)
Obviously in the outer block, ".attr" refers to myobject. My guess is that inside the inner block, it refers to yourobject. What happens if you want to refer to both? Would we have to write this?
using myobject: print(.attr) # myobject using yourobject: print(.attr) # yourobject print(myobject.attr) # be explicit
Now imagine that you are a beginner, trying to understand this code block. What would you think it does? Might you not be surprised that the two references to ".attr" refer to different variables?
And of course there is always the objection that the barrier to adding a new keyword is quite high. Somewhere out there, somebody is using "using" as a variable name (perhaps a decorator?) and making this a keyword will break her code. Is it worth it?
Despite these objections, I'm cautiously interested in this. I don't think the objections are insurmountable, and if there is a significant gain in readability and performance, it may be worth while.
A cautious and tentative +1.
It may be worth you doing a survey of other languages and seeing if they have anything similar.
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Sun, May 01, 2016 at 09:25:17PM -0400, Random832 wrote:
Is there any reason not to simply implement a performance optimization for the normal syntax?
Yes. You can't assume that myobject is the same object in both statements. A silly toy example: class MyObject: def deactivate(self): global myobject myobject = None def fire(self): launch_missiles() myobject.deactivate() myobject.fire() A more realistic case would be if you have a mutable object: myobject[1].lookup['key'].property.a() myobject[1].lookup['key'].property.b() The compiler cannot assume that (myobject[1].lookup['key'].property) will refer to the same thing in the two calls. But the programmer can explicitly do so, using either a temporary variable, or the proposed "using" statement. -- Steve
On Sun, May 1, 2016, at 21:36, Steven D'Aprano wrote:
On Sun, May 01, 2016 at 09:25:17PM -0400, Random832 wrote:
Is there any reason not to simply implement a performance optimization for the normal syntax?
Yes. You can't assume that myobject is the same object in both statements. A silly toy example: [snip] A more realistic case would be if you have a mutable object: [snip]
Well, by "normal" I meant assigning it to a temporary variable... and a local one, at that. Too bad there's no easy way to get a quick local scope without making a function, for stuff at the module level...
On Mon, May 2, 2016 at 11:36 AM, Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, May 01, 2016 at 09:25:17PM -0400, Random832 wrote:
Is there any reason not to simply implement a performance optimization for the normal syntax?
Yes. You can't assume that myobject is the same object in both statements. A silly toy example:
class MyObject: def deactivate(self): global myobject myobject = None def fire(self): launch_missiles()
myobject.deactivate() myobject.fire()
A more realistic case would be if you have a mutable object:
myobject[1].lookup['key'].property.a() myobject[1].lookup['key'].property.b()
The compiler cannot assume that (myobject[1].lookup['key'].property) will refer to the same thing in the two calls. But the programmer can explicitly do so, using either a temporary variable, or the proposed "using" statement.
If you profile a big program, I expect you'll find that module-level code amounts to a tiny proportion of run-time. Restricting this optimization to function-local names (no nonlocals, no globals - the LOAD_FAST opcode in current CPython) wouldn't materially harm it, and it would make the code a lot easier to reason about. The only possible issue that I can see is with an inner function messing with the outer function: def f(): obj = MyObject() def switch_obj(): nonlocal obj obj = MyObject() return 5 obj.attr1 = switch_obj() obj.attr2 = 42 return obj But a quick check with dis.dis(f) shows that this, too, uses LOAD_DEREF, so that should be safe - a byte-code transformation should safely be able to look for the LOAD_FAST and depend on it not changing (short of insane shenanigans - if you mutate sys._getframe().f_locals to tamper with another function's locals, you deserve all you get... and I'm not sure if it'd even work). This would be restricted to lookups on simple names. Frankly, I wouldn't expect anything else. If you want to call "myobject[1].lookup['key'].property.a" and "etc etc .b", you should probably be giving that a name *for the benefit of humans*, never mind about the interpreter! And if you want it optimized, definitely assign that to a local name. ChrisA
On Mon, May 02, 2016 at 12:05:13PM +1000, Chris Angelico wrote:
This would be restricted to lookups on simple names. Frankly, I wouldn't expect anything else.
And I completely disagree. If this proposal is for simple names alone, I don't want it -- it would be one more syntactic feature to learn for virtually no benefit to the reader or the writer. (If the name is so long as to be a burden to type, copy and paste it.)
If you want to call "myobject[1].lookup['key'].property.a" and "etc etc .b", you should probably be giving that a name *for the benefit of humans*
That goes completely against the advice "use a temporary one-letter name". Using, let's say, "x" or "m" for the variable name is not really for the benefit of the reader, otherwise it would be given a meaningful name like current_record_address. But that obviously goes against the idea of reducing the amount needed to type/read. *Whatever* solution you use, whether it is the one-letter temp name, or some variation on Pascal-with/using, you are compromising explicitness for convenience and/or efficiency. If this is worth doing, it has to be worth doing for complex references not just simple names. Otherwise, what's the point? Just use a shorter name.
never mind about the interpreter! And if you want it optimized, definitely assign that to a local name.
If you have to create a one-off function just to get access to a local variable, the cost of creating a function will surely blow away any realistic gains you might get from using a local name. That shouldn't apply to the "using" block, which can use the same reference without needing to create a function object. If I'm reading you correctly, you're suggesting that (1) "using" should be limited to simple names, and (2) any (hypothetical) optimization should be only applied if the name is a function local, not inside a class definition or global scope. With these restrictions, the value of this feature keeps going down and down and down, to the point that it is not worth adding it. There is cost to new syntactic features, and once you strip away most of the benefits, you're left with something that is no longer worth the effort of adding and learning the new syntax. So a very strong -1 on the crippled/restricted version of "using", and a tentative +1 for the unrestricted version if it can be shown that there are significant performance gains to be made over the temp name version. (I'm still not quite 100% convinced that the benefit outways the "grit on Tim's monitor rule", but I'm leaning that way.) -- Steve
On Sun, May 1, 2016 at 7:05 PM, Chris Angelico <rosuav@gmail.com> wrote:
On Mon, May 2, 2016 at 11:36 AM, Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, May 01, 2016 at 09:25:17PM -0400, Random832 wrote:
Is there any reason not to simply implement a performance optimization for the normal syntax?
If the variable is already a local, there's not much of a performance win to be had, since replacing a LOAD_FAST with a DUP_TOP doesn't make much of a difference (and if it is anything else the optimization isn't safe, as you already explained). HOWEVER... I doubt that this is what the OP is after. I also don't think they're really after having to write less. I think what they want is to express the IDEA of doing various things to/using attributes of a specific object. (OK, I will now stop using all caps. :-) Like Steven, I think it's a reasonable idea. The idea of using a leading dot is good. There are some edge cases around nesting but I think they can be dealt with. I have also seen plenty of code that would benefit from this idiom, e.g. code that repeats something like `team.users[i]` several times in one call -- I imagine the author just didn't want to interrupt their flow by putting it in a local variable. But unlike Steven, I'm still lukewarm at most and would currently vote -0. Does this feature find a lot of use in other languages? If so, is the code using it typically clearer than the alternative? If a coder currently writes team.evict_user(team.users[i].uid, team.users[i].email, "Bye bye") because they can't be bothered to write user = team.users[i] team.evict_user(user.uid, user.email, "Bye bye") then would they bother writing this instead? using team.users[i]: team.evict_user(.uid, .email, "Bye bye") And is that really clearer? Even if it's used often enough that people will recognize it, the dot is an awfully small character and easily missed (on my screen there are smudges larger than a dot in the standard font :-). Plus there's the cost of the extra indent. Sure, omitting the user variable probably makes the line shorter, but what if there are other lines in the same block that don't use it? I've seen plenty of code doing something less readable to avoid an extra indent (which might require breaking lines that would otherwise just fit). -- --Guido van Rossum (python.org/~guido)
On Sun, May 1, 2016 at 4:27 PM, Robert van Geel <robert@bign.nl> wrote:
First of all I'm new to this. I tried figuring out if some inquiry like mine below already was posted but I couldn't find it, frankly partly because I don't know what to look for, I'm not sure if there's a name for this idea. I'm not convinced my idea below is solid so apologies if it's naïve but I figured to post it anyway. It has to do with the possibility to fold typical opcodes pairs by introducing a language construct.
The idea is to be able to write this code:
myobject.a myobject.b myobject.c() myobject.d = 1
like this:
using myobject: .a .b .c() .d = 1
Would the following solve your usecase? Explicit naming: with myobject import a,b,c,d: a b c() d = 1 Alternatively, putting the object at the end (like in gen expressions): with a,b,c,d from myobject: a b c() d = 1 Questions: * Should this add additional scope? * Descriptors and `__getattr__`-only attributes: Do you get the attribute at the start of the block, or do you call `__getattr__` every time? * That `d = 1` is Pythonically odd if it works as in the original example.
On Sun, May 01, 2016 at 08:27:40PM -0700, Guido van Rossum wrote:
Like Steven, I think it's a reasonable idea. [...] But unlike Steven, I'm still lukewarm at most and would currently vote -0.
That's why I say it's a tentative +1 :-) I think we need to see that the performance gains, if any, are real (not just hypothetical), and that the proposal is readable enough to overcome the fact that the leading dot is awfully small. And no, I wouldn't like to see a second attribute-access syntax like $attr just for the sake of this proposal. If the OP still wants to argue in favour of this, perhaps he can pull out some real-life examples of code that would benefit? -- Steve
On Sun, May 1, 2016, at 08:27 PM, Guido van Rossum wrote:
If a coder currently writes
team.evict_user(team.users[i].uid, team.users[i].email, "Bye bye")
because they can't be bothered to write user = team.users[i] team.evict_user(user.uid, user.email, "Bye bye") then would they bother writing this instead? using team.users[i]: team.evict_user(.uid, .email, "Bye bye")
I seriously don't clean my screen enough to find this an OK syntax. "self.x" is perfectly readable, but not because the dot is particularly visible, but because the dot is sufficient to combine those two parts into a single unit to my eye. It makes the two parts blur together more then the dot is, itself, strongly visible. Every use case I can think of for this feature is better (as defined as more readable?) spelled as a local variable, even if its a short variable I'd normally argue against, like: u = team.users[i] team.evict_user(u.uid, u.email, "Bye bye") Normally I don't like variables like 'u', but if they're very local in functions that aren't long, they're fine. In those cases where they aren't fine, the proposed using's dot syntax would remove from vision what you're currently operating on and would therefore not be good either. To me this feature sacrifices readability for writability, and readability is more important. -- Stephen Hansen m e @ i x o k a i . i o
On Sun, May 1, 2016 at 7:02 PM, Steven D'Aprano <steve@pearwood.info> wrote:
And of course there is always the objection that the barrier to adding a new keyword is quite high. Somewhere out there, somebody is using "using" as a variable name (perhaps a decorator?) and making this a keyword will break her code. Is it worth it?
In particular, it would break Django: https://docs.djangoproject.com/en/1.9/ref/models/querysets/#using
Joshua Morton wrote:
import math with Namespace(math): print(pi**2)
That can't work if pi isn't assigned to as a local somewhere else in the function, because the compiler won't have allocated it a slot in locals for Namespace to modify.
with Namespace(m=math):` print(m.pi**2)
I don't see how that's any better than m = math print(m.pi**2) -- Greg
I just tried to extrapolate this idea a little bit. I searched for sources with many "self" in one line and found this function in mpmath/calculus/extrapolation.py: def factor_sidi(self, i): return (self.theta + self.n - 1) * (self.theta + self.n - 2) / self.ctx.mpf((self.theta + 2 * self.n - i - 2) * (self.theta + 2 * self.n - i - 3)) def factor_sidi(using self, i): return (.theta + .n - 1) * (.theta + .n - 2) / .ctx.mpf((.theta + 2 * .n - i - 2) * (.theta + 2 * .n - i - 3)) What dou you think?
On 5/2/2016 5:28 AM, Franklin? Lee wrote:
On Sun, May 1, 2016 at 4:27 PM, Robert van Geel <robert@bign.nl> wrote:
First of all I'm new to this. I tried figuring out if some inquiry like mine below already was posted but I couldn't find it, frankly partly because I don't know what to look for, I'm not sure if there's a name for this idea. I'm not convinced my idea below is solid so apologies if it's naïve but I figured to post it anyway. It has to do with the possibility to fold typical opcodes pairs by introducing a language construct.
The idea is to be able to write this code:
myobject.a myobject.b myobject.c() myobject.d = 1
like this:
using myobject: .a .b .c() .d = 1 Would the following solve your usecase? Explicit naming:
with myobject import a,b,c,d: a b c() d = 1
Alternatively, putting the object at the end (like in gen expressions):
with a,b,c,d from myobject: a b c() d = 1
Questions: * Should this add additional scope? * Descriptors and `__getattr__`-only attributes: Do you get the attribute at the start of the block, or do you call `__getattr__` every time? * That `d = 1` is Pythonically odd if it works as in the original example.
Sidenote: the idea I posed is actually borrowed from a language i used to use, Visual Foxpro, where it was implemented using the 'with' keyword. Your proposal has the need to duplicate all variables, so instead of multiple times typing "self." you have to type them double in the line using "with" so it's shorter but not optimally short. Also if you want the accompanying faster opcodes that means you have to put 4 variables on the stack straight away for fast access, instead of one. To answer you questions: - No it would not add scope, it's just a combination of syntactical sugar combined with faster opcodes. - The attributes are not grabbed at the beginning of the block, instead internally in memory there's a 'grabbed object' stack that can be accessed directly without the LOAD_FAST opcode - Your fourth remarks refers to your non-dotted code. I think the ".d = 1" statement feels pythonic but again maybe because I was so used to the syntax in the VFP language
On 5/2/2016 8:50 AM, Pavol Lisy wrote:
I just tried to extrapolate this idea a little bit.
I searched for sources with many "self" in one line and found this function in mpmath/calculus/extrapolation.py:
def factor_sidi(self, i): return (self.theta + self.n - 1) * (self.theta + self.n - 2) / self.ctx.mpf((self.theta + 2 * self.n - i - 2) * (self.theta + 2 * self.n - i - 3))
def factor_sidi(using self, i): return (.theta + .n - 1) * (.theta + .n - 2) / .ctx.mpf((.theta + 2 * .n - i - 2) * (.theta + 2 * .n - i - 3))
What dou you think?
As stated in another post, I'm personally used to this syntax because I used to work in a language that has this (Visual Foxpro) so for me it feels logic and natural hence 'pythonic'. I can imagine that for the python eye this currently looks ... off... but the whole idea is that the construct is new so it would look a bit unusual for a while.
I think the ".d = 1" statement feels like a bug waiting to happen. It's very easy to miss a dot. On 2 May 2016 at 09:34, Robert van Geel <robert@bign.nl> wrote:
On 5/2/2016 5:28 AM, Franklin? Lee wrote:
On Sun, May 1, 2016 at 4:27 PM, Robert van Geel <robert@bign.nl> wrote:
First of all I'm new to this. I tried figuring out if some inquiry like mine below already was posted but I couldn't find it, frankly partly because I don't know what to look for, I'm not sure if there's a name for this idea. I'm not convinced my idea below is solid so apologies if it's naïve but I figured to post it anyway. It has to do with the possibility to fold typical opcodes pairs by introducing a language construct.
The idea is to be able to write this code:
myobject.a myobject.b myobject.c() myobject.d = 1
like this:
using myobject: .a .b .c() .d = 1
Would the following solve your usecase? Explicit naming:
with myobject import a,b,c,d: a b c() d = 1
Alternatively, putting the object at the end (like in gen expressions):
with a,b,c,d from myobject: a b c() d = 1
Questions: * Should this add additional scope? * Descriptors and `__getattr__`-only attributes: Do you get the attribute at the start of the block, or do you call `__getattr__` every time? * That `d = 1` is Pythonically odd if it works as in the original example.
Sidenote: the idea I posed is actually borrowed from a language i used to use, Visual Foxpro, where it was implemented using the 'with' keyword.
Your proposal has the need to duplicate all variables, so instead of multiple times typing "self." you have to type them double in the line using "with" so it's shorter but not optimally short. Also if you want the accompanying faster opcodes that means you have to put 4 variables on the stack straight away for fast access, instead of one.
To answer you questions: - No it would not add scope, it's just a combination of syntactical sugar combined with faster opcodes. - The attributes are not grabbed at the beginning of the block, instead internally in memory there's a 'grabbed object' stack that can be accessed directly without the LOAD_FAST opcode - Your fourth remarks refers to your non-dotted code. I think the ".d = 1" statement feels pythonic but again maybe because I was so used to the syntax in the VFP language
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
That's a matter of perception. As said this syntax was implemented and used for a long time in another (now obsolete) language (visual foxpro) and there it never led to confusion or errors nor have I ever heard such complaints from any other user in that community. For Python developers, since this is a yet unfamiliar syntax, this might look unfamiliar and hence rejectable. On 5/2/2016 1:48 PM, João Santos wrote:
I think the ".d = 1" statement feels like a bug waiting to happen. It's very easy to miss a dot.
On 2 May 2016 at 09:34, Robert van Geel <robert@bign.nl <mailto:robert@bign.nl>> wrote:
On 5/2/2016 5:28 AM, Franklin? Lee wrote:
On Sun, May 1, 2016 at 4:27 PM, Robert van Geel <robert@bign.nl <mailto:robert@bign.nl>> wrote:
First of all I'm new to this. I tried figuring out if some inquiry like mine below already was posted but I couldn't find it, frankly partly because I don't know what to look for, I'm not sure if there's a name for this idea. I'm not convinced my idea below is solid so apologies if it's naïve but I figured to post it anyway. It has to do with the possibility to fold typical opcodes pairs by introducing a language construct.
The idea is to be able to write this code:
myobject.a myobject.b myobject.c() myobject.d = 1
like this:
using myobject: .a .b .c() .d = 1
Would the following solve your usecase? Explicit naming:
with myobject import a,b,c,d: a b c() d = 1
Alternatively, putting the object at the end (like in gen expressions):
with a,b,c,d from myobject: a b c() d = 1
Questions: * Should this add additional scope? * Descriptors and `__getattr__`-only attributes: Do you get the attribute at the start of the block, or do you call `__getattr__` every time? * That `d = 1` is Pythonically odd if it works as in the original example.
Sidenote: the idea I posed is actually borrowed from a language i used to use, Visual Foxpro, where it was implemented using the 'with' keyword.
Your proposal has the need to duplicate all variables, so instead of multiple times typing "self." you have to type them double in the line using "with" so it's shorter but not optimally short. Also if you want the accompanying faster opcodes that means you have to put 4 variables on the stack straight away for fast access, instead of one.
To answer you questions: - No it would not add scope, it's just a combination of syntactical sugar combined with faster opcodes. - The attributes are not grabbed at the beginning of the block, instead internally in memory there's a 'grabbed object' stack that can be accessed directly without the LOAD_FAST opcode - Your fourth remarks refers to your non-dotted code. I think the ".d = 1" statement feels pythonic but again maybe because I was so used to the syntax in the VFP language
_______________________________________________ Python-ideas mailing list Python-ideas@python.org <mailto:Python-ideas@python.org> https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
That is matter of experience, I already lost too many hours of my life looking for weird bugs caused by misspellings and missing commas, and I don't even work with people new to python. On 2 May 2016 14:04, "Robert van Geel" <robert@bign.nl> wrote:
That's a matter of perception. As said this syntax was implemented and used for a long time in another (now obsolete) language (visual foxpro) and there it never led to confusion or errors nor have I ever heard such complaints from any other user in that community. For Python developers, since this is a yet unfamiliar syntax, this might look unfamiliar and hence rejectable.
On 5/2/2016 1:48 PM, João Santos wrote:
I think the ".d = 1" statement feels like a bug waiting to happen. It's very easy to miss a dot.
On 2 May 2016 at 09:34, Robert van Geel <robert@bign.nl> wrote:
On 5/2/2016 5:28 AM, Franklin? Lee wrote:
On Sun, May 1, 2016 at 4:27 PM, Robert van Geel <robert@bign.nl> wrote:
First of all I'm new to this. I tried figuring out if some inquiry like mine below already was posted but I couldn't find it, frankly partly because I don't know what to look for, I'm not sure if there's a name for this idea. I'm not convinced my idea below is solid so apologies if it's naïve but I figured to post it anyway. It has to do with the possibility to fold typical opcodes pairs by introducing a language construct.
The idea is to be able to write this code:
myobject.a myobject.b myobject.c() myobject.d = 1
like this:
using myobject: .a .b .c() .d = 1
Would the following solve your usecase? Explicit naming:
with myobject import a,b,c,d: a b c() d = 1
Alternatively, putting the object at the end (like in gen expressions):
with a,b,c,d from myobject: a b c() d = 1
Questions: * Should this add additional scope? * Descriptors and `__getattr__`-only attributes: Do you get the attribute at the start of the block, or do you call `__getattr__` every time? * That `d = 1` is Pythonically odd if it works as in the original example.
Sidenote: the idea I posed is actually borrowed from a language i used to use, Visual Foxpro, where it was implemented using the 'with' keyword.
Your proposal has the need to duplicate all variables, so instead of multiple times typing "self." you have to type them double in the line using "with" so it's shorter but not optimally short. Also if you want the accompanying faster opcodes that means you have to put 4 variables on the stack straight away for fast access, instead of one.
To answer you questions: - No it would not add scope, it's just a combination of syntactical sugar combined with faster opcodes. - The attributes are not grabbed at the beginning of the block, instead internally in memory there's a 'grabbed object' stack that can be accessed directly without the LOAD_FAST opcode - Your fourth remarks refers to your non-dotted code. I think the ".d = 1" statement feels pythonic but again maybe because I was so used to the syntax in the VFP language
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, May 2, 2016 at 2:48 PM, João Santos <jmcs@jsantos.eu> wrote:
I think the ".d = 1" statement feels like a bug waiting to happen. It's very easy to miss a dot.
I suppose that's a valid concern, especially regarding assignments, because the code typically would still run. But in the beginning of the line, it is usually quite easy to see if there's a dot or not, assuming a fixed-width font and proper indenting. Anyway, it seems to me this would make sense in with statements: with open(filename): #do stuff .write(stuff) #do more stuff .write(more_stuff) -- Koos
with open(foo), open(bar): .write(baz) # does what? On 5/2/2016 08:20, Koos Zevenhoven wrote:
On Mon, May 2, 2016 at 2:48 PM, João Santos <jmcs@jsantos.eu> wrote:
I think the ".d = 1" statement feels like a bug waiting to happen. It's very easy to miss a dot.
I suppose that's a valid concern, especially regarding assignments, because the code typically would still run. But in the beginning of the line, it is usually quite easy to see if there's a dot or not, assuming a fixed-width font and proper indenting.
Anyway, it seems to me this would make sense in with statements:
with open(filename): #do stuff .write(stuff) #do more stuff .write(more_stuff)
-- Koos _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 02/05/16 13:20, Koos Zevenhoven wrote:
On Mon, May 2, 2016 at 2:48 PM, João Santos <jmcs@jsantos.eu> wrote:
I think the ".d = 1" statement feels like a bug waiting to happen. It's very easy to miss a dot.
I suppose that's a valid concern, especially regarding assignments, because the code typically would still run. But in the beginning of the line, it is usually quite easy to see if there's a dot or not, assuming a fixed-width font and proper indenting. It may be easy to see if there's a dot or not, but it may not be easy to tell whether there /should/ be a dot when there isn't.
e.g. <snip> .configuration = {'yes': 'no'} vs <snip> configuration = {'yes': 'no'} When you might have later in the code something that assigns to a variable with the same name, and/or operates on that variable I think it'd become more difficult to determine. Thanks, S
On 02/05/2016 07:50, Pavol Lisy wrote:
I just tried to extrapolate this idea a little bit.
I searched for sources with many "self" in one line and found this function in mpmath/calculus/extrapolation.py:
def factor_sidi(self, i): return (self.theta + self.n - 1) * (self.theta + self.n - 2) / self.ctx.mpf((self.theta + 2 * self.n - i - 2) * (self.theta + 2 * self.n - i - 3))
def factor_sidi(using self, i): return (.theta + .n - 1) * (.theta + .n - 2) / .ctx.mpf((.theta + 2 * .n - i - 2) * (.theta + 2 * .n - i - 3))
What dou you think? What is wrong with
def factor_sidi(self, i): theta = self.theta n = self.n return (theta + n - 1) * (theta + n - 2) / self.ctx.mpf((theta + 2 * n - i - 2) * (theta + 2 * n - i - 3)) Arguably more readable, and does 6 fewer attribute accesses.
Using a single letter (or short) variable name works well most of the time but has one problem: the variable can leak. It's easy to forget to write the del statement. Imagine a block statement that had the effect of deleting any variables initialized in the block. That is (using the with keyword in this example but there are other choices): x = foo() with: y = x + 1 x = y + 2 is equivalent to: x = foo() y = x + 1 x = y + 2 del y or after optimization: x = (foo() + 1) + 2 [Obviously more useful with real code but this is sufficiently illustrative. Also I imagine someone might say it's just foo() + 3 but I don't know the type of foo().] --- Bruce -- --- Bruce Check out my puzzle book and get it free here: http://J.mp/ingToConclusionsFree (available on iOS)
On Mon, May 2, 2016, at 10:35, Bruce Leban wrote:
Using a single letter (or short) variable name works well most of the time but has one problem: the variable can leak. It's easy to forget to write the del statement. Imagine a block statement that had the effect of deleting any variables initialized in the block.
Should it delete the variables, or should it be a new scope? They're subtly different. If the former, how do you differentiate a variable initialized in the block from a variable assigned in the block? What if you really *do* want to keep one of them? (The answer in both cases if it's a new scope would be to use "nonlocal")
On 02.05.2016 07:07, Stephen Hansen wrote:
I seriously don't clean my screen enough to find this an OK syntax. "self.x" is perfectly readable, but not because the dot is particularly visible, but because the dot is sufficient to combine those two parts into a single unit to my eye. It makes the two parts blur together more then the dot is, itself, strongly visible.
The same for me. -1 for this feature.
Every use case I can think of for this feature is better (as defined as more readable?) spelled as a local variable, even if its a short variable I'd normally argue against, like:
u = team.users[i] team.evict_user(u.uid, u.email, "Bye bye")
Normally I don't like variables like 'u', but if they're very local in functions that aren't long, they're fine. In those cases where they aren't fine, the proposed using's dot syntax would remove from vision what you're currently operating on and would therefore not be good either.
In this case, I could even live with "user".
To me this feature sacrifices readability for writability, and readability is more important.
Yep. Especially, when adding yet another way for "performance reasons". Python can optimize the presented cases without introducing a new syntax. Best, Sven
On 02/05/16 14:57, Koos Zevenhoven wrote:
On Mon, May 2, 2016 at 3:44 PM, Alexander Walters <tritium-list@sdamon.com> wrote:
with open(foo), open(bar): .write(baz) # does what?
Hopefully it would raise an exception,
Then you are suggesting that an attribute is looked up at runtime on _all_ objects declared in all live 'with' scopes every time one is referenced in order to determine if it's an ambiguous reference (at the time of that reference). O(n). I'm pretty sure that will never happen. E.
On 2016-05-02 14:57, Koos Zevenhoven wrote:
On Mon, May 2, 2016 at 3:44 PM, Alexander Walters <tritium-list@sdamon.com> wrote:
with open(foo), open(bar): .write(baz) # does what?
Hopefully it would raise an exception, even though, in the spirit of May 1st, it would pick one of the two files at random ;-).
This: with open(foo), open(bar): .write(baz) # does what? is basically just a shorter way to write this: with open(foo): with open(bar): .write(baz) # does what? so that suggests that it would pick the last one. Anyway, the exception (or warning?) could be raised at compile-time if the leading-dot form was used in a 'with' that had more than one expression.
On 02/05/16 21:41, MRAB wrote:
Anyway, the exception (or warning?) could be raised at compile-time if the leading-dot form was used in a 'with' that had more than one expression.
How will the compiler know what attributes will be available on the objects when the code is executed? Rgds, E.
On Mon, May 2, 2016 at 11:41 PM, MRAB <python@mrabarnett.plus.com> wrote:
On 2016-05-02 14:57, Koos Zevenhoven wrote:
On Mon, May 2, 2016 at 3:44 PM, Alexander Walters wrote:
with open(foo), open(bar): .write(baz) # does what?
Hopefully it would raise an exception, even though, in the spirit of May 1st, it would pick one of the two files at random ;-).
[...]
Anyway, the exception (or warning?) could be raised at compile-time if the leading-dot form was used in a 'with' that had more than one expression.
Exactly. A SyntaxError perhaps? So, if a single-expression with-statement would allow dot-attribute syntax for referring to attributes of the value, like in my previous example: with open(filename, 'w'): #do stuff .write(stuff) #more stuff .write(stuff) Then, if one introduces something roughly as follows: import contextlib @contextlib.contextmanager def implicit(obj): yield obj It would also allow one to do things like: with implicit(food): meal = .spam + .eggs + .cheese # meal = food.spam + food.eggs + food.cheese with implicit(self): .foo = foo # self.foo = foo .bar = bar # self.bar = bar This would be very explicit about what is implicit :). This way, the required addition to the with statement would indeed not change the meaning of 'with' (or mix it with different meanings). The only(?) addition would be .attr access to attributes of the object "yielded" by the context manager. -- Koos
On 02/05/16 22:34, Koos Zevenhoven wrote:
On Mon, May 2, 2016 at 11:41 PM, MRAB <python@mrabarnett.plus.com> wrote:
On 2016-05-02 14:57, Koos Zevenhoven wrote: Anyway, the exception (or warning?) could be raised at compile-time if the leading-dot form was used in a 'with' that had more than one expression.
Exactly. A SyntaxError perhaps?
Now you're suggesting a SyntaxError for valid syntax ;) E.
It should be a name error, perhaps 'Accessed object's name cannot be inferred', or a similar error to UnboundLocalException. On Mon, May 2, 2016, 17:41 Erik <python@lucidity.plus.com> wrote:
On Mon, May 2, 2016 at 11:41 PM, MRAB <python@mrabarnett.plus.com> wrote:
On 2016-05-02 14:57, Koos Zevenhoven wrote: Anyway, the exception (or warning?) could be raised at compile-time if
On 02/05/16 22:34, Koos Zevenhoven wrote: the
leading-dot form was used in a 'with' that had more than one expression.
Exactly. A SyntaxError perhaps?
Now you're suggesting a SyntaxError for valid syntax ;)
E.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 02/05/16 23:00, Joshua Morton wrote:
It should be a name error, perhaps 'Accessed object's name cannot be inferred', or a similar error to UnboundLocalException.
You are missing the point. It is not reasonable to expect an error to be generated at compile time: because the compiler has no way of knowing what attributes will exist on the objects at runtime. It is not reasonable to expect an error to be generated at runtime: because each and every object would have to be queried as to whether they have the attribute to know if there is ambiguity on each and every reference to an attribute. The question of which error or warning or exception should be generated is moot ;) E.
On 2016-05-02 23:06, Erik wrote:
On 02/05/16 23:00, Joshua Morton wrote:
It should be a name error, perhaps 'Accessed object's name cannot be inferred', or a similar error to UnboundLocalException.
You are missing the point.
It is not reasonable to expect an error to be generated at compile time: because the compiler has no way of knowing what attributes will exist on the objects at runtime.
It is not reasonable to expect an error to be generated at runtime: because each and every object would have to be queried as to whether they have the attribute to know if there is ambiguity on each and every reference to an attribute.
The question of which error or warning or exception should be generated is moot ;)
You _don't_ have to know attributes are available. It's as simple(?) as: when it sees the leading-dot notation, it checks how many expressions the enclosing 'with' was followed by. For example: with something: .foo() is OK (the 'with' has 1 expression), but: with first_thing, second_else: .foo() might not be OK (the 'with' has 2 expressions).
It is not reasonable to expect an error to be generated at runtime: because each and every object would have to be queried as to whether they have the attribute to know if there is ambiguity on each and every reference to an attribute.
It absolutely is: if you're using an "inferred" syntax like in object: .value but there are multiple such objects to look at, Python immediately throws an error, before doing any kind of lookup. Hence the error doesn't say "I couldn't find the attribute x on any object" but instead "There's ambiguity so I didn't even try". On Mon, May 2, 2016 at 6:19 PM MRAB <python@mrabarnett.plus.com> wrote:
On 2016-05-02 23:06, Erik wrote:
On 02/05/16 23:00, Joshua Morton wrote:
It should be a name error, perhaps 'Accessed object's name cannot be inferred', or a similar error to UnboundLocalException.
You are missing the point.
It is not reasonable to expect an error to be generated at compile time: because the compiler has no way of knowing what attributes will exist on the objects at runtime.
It is not reasonable to expect an error to be generated at runtime: because each and every object would have to be queried as to whether they have the attribute to know if there is ambiguity on each and every reference to an attribute.
The question of which error or warning or exception should be generated is moot ;)
You _don't_ have to know attributes are available. It's as simple(?) as: when it sees the leading-dot notation, it checks how many expressions the enclosing 'with' was followed by.
For example:
with something: .foo()
is OK (the 'with' has 1 expression), but:
with first_thing, second_else: .foo()
might not be OK (the 'with' has 2 expressions).
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, May 3, 2016 at 12:37 AM, Erik <python@lucidity.plus.com> wrote:
On 02/05/16 22:34, Koos Zevenhoven wrote:
On Mon, May 2, 2016 at 11:41 PM, MRAB <python@mrabarnett.plus.com> wrote:
On 2016-05-02 14:57, Koos Zevenhoven wrote: Anyway, the exception (or warning?) could be raised at compile-time if the leading-dot form was used in a 'with' that had more than one expression.
Exactly. A SyntaxError perhaps?
Now you're suggesting a SyntaxError for valid syntax ;)
No. It seems to me the signal-to-noise ratio of this thread is getting worse. Déjà vu (or French for 'already seen', if unicode fails you). What I'm suggesting (in addition to the actual suggestion) is not to allow plain .attr attribute access inside a with statement "with multiple expressions". Kind of the same way as, for instance, break and continue are not allowed outside loops: "SyntaxError: 'continue' not properly in loop". Or the same way as plain .attr syntax is currently not allowed at all: "SyntaxError: invalid syntax". -- Koos
On 02/05/16 23:34, Koos Zevenhoven wrote:
It seems to me the signal-to-noise ratio of this thread is getting worse.
I tend to agree.
Déjà vu (or French for 'already seen', if unicode fails you).
What has translating one human language phrase to another human language got to do with Unicode?
What I'm suggesting (in addition to the actual suggestion) is not to allow plain .attr attribute access inside a with statement "with multiple expressions". Kind of the same way as, for instance, break and continue are not allowed outside loops:
You're now suggesting that the lexer/parser changes behaviour dynamically depending on what has been seen before.
Kind of the same way as, for instance, break and continue are not allowed outside loops:
Not the same thing at all. Those are absolute syntax rules. What you are suggesting is conditional based on context. E.
On Tue, May 3, 2016 at 8:19 AM, MRAB <python@mrabarnett.plus.com> wrote:
You _don't_ have to know attributes are available. It's as simple(?) as: when it sees the leading-dot notation, it checks how many expressions the enclosing 'with' was followed by.
For example:
with something: .foo()
is OK (the 'with' has 1 expression), but:
with first_thing, second_else: .foo()
might not be OK (the 'with' has 2 expressions).
Replace "might" with "will" and I'd agree with you. Keep it simple: no ambiguity. ChrisA
On 2016-05-02 23:59, Erik wrote:
On 02/05/16 23:34, Koos Zevenhoven wrote:
It seems to me the signal-to-noise ratio of this thread is getting worse.
I tend to agree.
Déjà vu (or French for 'already seen', if unicode fails you).
What has translating one human language phrase to another human language got to do with Unicode?
The French phrase has accents.
What I'm suggesting (in addition to the actual suggestion) is not to allow plain .attr attribute access inside a with statement "with multiple expressions". Kind of the same way as, for instance, break and continue are not allowed outside loops:
You're now suggesting that the lexer/parser changes behaviour dynamically depending on what has been seen before.
Kind of the same way as, for instance, break and continue are not allowed outside loops:
Not the same thing at all. Those are absolute syntax rules. What you are suggesting is conditional based on context.
The use of 'break' and 'continue' are also conditional on context; they can be used only within a loop. Another example: an empty 'raise' statement can be used only within an 'except' suite. This suggestion is that a leading .attr can be used only within a 'with' statement that has a single expression.
On Mon, May 2, 2016 at 8:35 AM, Bruce Leban <bruce@leban.us> wrote:
Using a single letter (or short) variable name works well most of the time but has one problem: the variable can leak. It's easy to forget to write the del statement. Imagine a block statement that had the effect of deleting any variables initialized in the block. That is (using the with keyword in this example but there are other choices):
x = foo() with: y = x + 1 x = y + 2
is equivalent to:
x = foo() y = x + 1 x = y + 2 del y
or after optimization:
x = (foo() + 1) + 2
[Obviously more useful with real code but this is sufficiently illustrative. Also I imagine someone might say it's just foo() + 3 but I don't know the type of foo().]
Hmm. That reminds me of the "given" syntax (PEP 3150: https://www.python.org/dev/peps/pep-3150/). This is like allowing blocks of code to be treated as first-order, a la Ruby. Then again, functions give us just about all we need, and the "given" syntax basically gives us multi-line lambdas. :) Perhaps it's time to dust off that proposal. -eric
On Mon, May 02, 2016 at 09:25:01PM +0100, Erik wrote:
On 02/05/16 14:57, Koos Zevenhoven wrote:
On Mon, May 2, 2016 at 3:44 PM, Alexander Walters <tritium-list@sdamon.com> wrote:
with open(foo), open(bar): .write(baz) # does what?
open(bar) could shadow open(foo), just like in this: with open(foo): .write(baz) # refers to foo with open(bar): .write(baz) # refers to bar, shadowing foo There's no ambiguity here: last one seen wins, the same rule used all over Python: import math as m, string as m, urllib as m, decimal as m x, x, x, x = 1, 2, 3, 4 etc. So I don't think this is an insurmountable problem.
Hopefully it would raise an exception,
Then you are suggesting that an attribute is looked up at runtime on _all_ objects declared in all live 'with' scopes every time one is referenced in order to determine if it's an ambiguous reference (at the time of that reference).
O(n). I'm pretty sure that will never happen.
But for very small n. I mean, if you somehow manage to nest five or six hundred with statements, you probably deserve whatever happens to you... *wink* -- Steve
On Mon, May 2, 2016 at 8:21 AM, Random832 <random832@fastmail.com> wrote:
On Mon, May 2, 2016, at 10:35, Bruce Leban wrote:
Using a single letter (or short) variable name works well most of the time but has one problem: the variable can leak. It's easy to forget to write the del statement. Imagine a block statement that had the effect of deleting any variables initialized in the block. <snip>
Should it delete the variables, or should it be a new scope? They're subtly different.
If the former, how do you differentiate a variable initialized in the block from a variable assigned in the block? What if you really *do* want to keep one of them? (The answer in both cases if it's a new scope would be to use "nonlocal")
Fair question. It's not a new scope because it's too clumsy to have to declare every variable outside the block nonlocal. If a variable X is not local to the enclosing scope (or declared global), then it's local to the block. I don't think you can do this at runtime; I think it has to be determined at compile time. Consider: def foo(i): if i: x = 1 with: x = 2 y = 3 This should not del x regardless of the value of i. In the following case the variable in the block affects the outer scope. Consider: def foo(i): with: y = 3 return y Treating this as def foo(i): y = 3 del y return y has the right result (unbound error). Without the block return y would be a reference to a global variable. On Mon, May 2, 2016 at 5:06 PM, Eric Snow <ericsnowcurrently@gmail.com> wrote:
On Mon, May 2, 2016 at 8:35 AM, Bruce Leban <bruce@leban.us> wrote:
Using a single letter (or short) variable name works well most of the time <snip>
x = foo() with: y = x + 1 x = y + 2
Hmm. That reminds me of the "given" syntax (PEP 3150: https://www.python.org/dev/peps/pep-3150/). This is like allowing blocks of code to be treated as first-order, a la Ruby. Then again, functions give us just about all we need, and the "given" syntax basically gives us multi-line lambdas. :) Perhaps it's time to dust off that proposal.
There is a difference. "given" or whatever it's called requires the variables to be declared and initialized at the top, while this would automatically delete any references created in the block. On Mon, May 2, 2016 at 8:31 AM, Guido van Rossum <guido@python.org> wrote:
The choice of keyword is not entirely arbitrary; if we can't come up with a decent keyword the feature is dead. But the introduction would have to include a `from __future__ import <something>` statement anyway for at least one, maybe two release cycles (we did this with the `with` statement itself, around 2.4/2.5). So Django will have plenty of time to change.
Note that the "auto-deleting block" (for want of a better descriptive name) could be introduced with a contextual keyword. Currently <identifier> : is not valid syntax so any identifier could be used for these blocks without requiring it to be recognized as a keyword in other contexts. --- Bruce
participants (21)
-
Alexander Walters
-
Bruce Leban
-
Chris Angelico
-
Eric Snow
-
Erik
-
Franklin? Lee
-
Greg Ewing
-
Guido van Rossum
-
Ian Kelly
-
Joshua Morton
-
João Santos
-
Koos Zevenhoven
-
MRAB
-
Pavol Lisy
-
Random832
-
Rob Cliffe
-
Robert van Geel
-
Stephen Hansen
-
Steven D'Aprano
-
Sven R. Kunze
-
SW