Augmented assignment syntax for objects.

Hi. I suspect that this may have been discussed to death at some point in the past, but I've done some searching and I didn't come up with much. Apologies if I'm rehashing an old argument ;) I often find myself writing __init__ methods of the form: def __init__(self, foo, bar, baz, spam, ham): self.foo = foo self.bar = bar self.baz = baz self.spam = spam self.ham = ham This seems a little wordy and uses a lot of vertical space on the screen. Occasionally, I have considered something like: def __init__(self, foo, bar, baz, spam, ham): self.foo, self.bar, self.baz, self.spam, self.ham = \ foo, bar, baz, spam, ham ... just to make it a bit more compact - though in practice, I'd probably not do that with a list quite that long ... two or three items at most: def __init__(self, foo, bar, baz): self.foo, self.bar, self.baz = foo, bar, baz When I do that I'm torn because I know it has a runtime impact to create and unpack the implicit tuples and I'm also introducing a style asymmetry in my code just because of the number of parameters a method happens to have. So why not have an augmented assignment operator for object attributes? It addresses one of the same broad issues that the other augmented assignment operators were introduced for (that of repeatedly spelling names). The suggestion therefore is: def __init__(self, foo, bar, baz, spam, ham): self .= foo, bar, baz, spam, ham This is purely syntactic sugar for the original example: def __init__(self, foo, bar, baz, spam, ham): self.foo = foo self.bar = bar self.baz = baz self.spam = spam self.ham = ham ... so if any of the attributes have setters, then they are called as usual. It's purely a syntactic shorthand. Any token which is not suitable on the RHS of the dot in a standard "obj.attr =" assignment is a syntax error (no "self .= 1"). The comma-separators in the example are not creating a tuple object, they would work at the same level in the parser as the import statement's comma-separated lists - in the same way that "from pkg import a, b, c" is the same as saying: import pkg a = pkg.a b = pkg.b c = pkg.c ... "self .= a, b, c" is the same as writing: self.a = a self.b = b self.c = c E.

On Tue, Apr 25, 2017 at 11:08 AM, Erik <python@lucidity.plus.com> wrote:
Bikeshedding: Your example looks a lot more like tuple assignment than multiple assignment. I'd rather it link more with the way that multiple assignment works: # simple multiple assignment a = b = c = d = e = 0 # object member assignment self .= foo .= bar .= baz .= spam .= ham The trouble is that this syntax is really only going to be used inside __init__. It's hard to justify new syntax for one purpose like this. So I'm swaying between -0 and +0.5 on this. ChrisA

On 25/04/17 02:15, Chris Angelico wrote:
Bikeshedding: Your example looks a lot more like tuple assignment than multiple assignment.
Well, originally, I thought it was just the spelling-the-same-name-twice thing that irritated me and I was just going to suggest a single assignment version like: self .= foo self .= bar Then I thought that this is similar to importing (referencing an object from one namespace in another under the same name). In that scenario, instead of: from other import foo from other import bar we have: from other import foo, bar That's where the comma-separated idea came from, and I understand it looks like a tuple (which is why I explicitly mentioned that) but it does in the import syntax too ;) The single argument version (though it doesn't help with vertical space) still reads better to me for the same reason that augmented assignment is clearer - there is no need to mentally parse that the same name is being used on both sides of the assignment because it's only spelled once.
self .= foo .= bar .= baz .= spam .= ham
Thanks for being the only person so far to understand that I don't necessarily want to bind ALL of the __init__ parameters to the object, just the ones I explicitly reference, but I'm not convinced by this suggestion. In chained assignment the thing on the RHS is bound to each name to the left of it and that is really not happening here.
The trouble is that this syntax is really only going to be used inside __init__.
Even if that was true, who ever writes one of those? :D E.

On Tue, Apr 25, 2017 at 02:08:05AM +0100, Erik wrote:
It does, and while both are annoying, in the grand scheme of things they're a very minor annoyance. After all, this is typically only an issue once per class, and not even every class, and vertical space is quite cheap. In general, the barrier for accepting new syntax is quite high, and "minor annoyance" generally doesn't reach it. I'm completely with you about the annoyance factor here. It is especially annoying during the early stages of development when the __init__ method is evolving rapidly (parameters are being renamed, added or removed). One work-around is this pattern: def __init__(self, spam, ham, eggs, cheese, aardvark): vars(self).update(locals()) del self.self ... which is cute but I've never quite been brave enough to use it in production. But I don't think the annoyance factor is high enough, or common enough, to justify syntactic sugar to "fix" it.
I'm not too worried about the performance impact unless I've profiled my code and it is actually significant. As far as the style asymmetry, if we use your proposed syntax, that's introducing style asymmetry too.
So why not have an augmented assignment operator for object attributes?
Wrong question. Don't ask "why not add this feature?". The question to ask is "why add this feature?" Every new feature has cost, whether it is run-time cost, development cost, feature bloat, learning curve cost, etc, so the feature must be justified as adding sufficiently more value than it costs. Here are a few reasons why this feature fails to meet the barrier for new syntax. - This feature doesn't add any new capability to the language. There's nothing that this feature allows you to do which you couldn't do before. - It has quite narrow applicability. It's really only useful inside a small proportion of __init__ methods. - In my experience, it is very rare to have a whole set of unadorned assignments in the way you show it. MUCH more common is to have some sort of pre-processing of the argument before assignment, or for the assignment target to be different from the parameter name: def __init__(self, spam, eggs, cheese, aardvark, ...): # type checks if not isinstance(spam, Foo): raise TypeError self._spam = spam # not all parameters are public attributes # value checks if eggs < 0: raise ValueError self.eggs = eggs # replacement of None if cheese is None: cheese = make_cheese() self.cheese = cheese # other pre-processing self.mammals = [aardvark, get_weasel()] which reduces the applicability of this syntax even further. - Having big parameter lists is something of a mild code smell. As the parameter list gets bigger, the smell gets worse. Having so many parameters that this feature becomes attractive should be painful, because its a warning that maybe your class has too many parameters.
But there's a major difference between this and the other augmented assignment operators, so major that the analogy between them doesn't hold up. In augmented assignment, the target can be any valid target, and the RHS can be any expression mylist[0].attr['key'] += (some_value() or another) + 99 Your suggestion is very different: there are *many* targets, and the RHS must be a comma-separated list of identifiers. They're quite different kinds of assignment. And unlike augmented assignment, which calls one of the __iop__ methods, this would have to be handled purely as syntax.
Right. And that *seriously* limits the applicability of this. Yes, I've written classes with a dozen or twenty parameters. But especially with long parameter lists, invariably at least *some* of those arguments are pre-processed before assignment to an attribute: self.spam = spam + 1 or are assigned to a different name: self._spam = spam or both: self.foods = [spam, eggs, cheese] So my expectation is that even if this syntax was accepted, I wouldn't use it, because by the time I've processed those assignments, the ones that are left are too few to bother with the special syntax. The bottom line is that I think you've identified a very real, but minor, annoyance, but the applicability of the solution is too low to justify new syntax. -- Steve

On 25 April 2017 at 03:53, Steven D'Aprano <steve@pearwood.info> wrote:
I suspect that with a suitably creative use of inspect.signature() you could write a decorator for this: @auto_attrs def __init__(self, a, b, c): # Remaining init code, called with self.a, self.b and self.c set I don't have time to experiment right now, but will try to find time later. If nothing else, such a decorator would be a good prototype for the proposed functionality, and may well be sufficient for the likely use cases without needing a syntax change. Paul

I actually saw a decorator like that last week, https://twitter.com/judy2k/status/854330478068977664 On 25 April 2017 at 08:56, Paul Moore <p.f.moore@gmail.com> wrote:
-- Daniel F. Moisset - UK Country Manager www.machinalis.com Skype: @dmoisset

2017. ápr. 25. de. 10:04 ezt írta ("Paul Moore" <p.f.moore@gmail.com>): On 25 April 2017 at 03:53, Steven D'Aprano <steve@pearwood.info> wrote:
I suspect that with a suitably creative use of inspect.signature() you could write a decorator for this: @auto_attrs def __init__(self, a, b, c): # Remaining init code, called with self.a, self.b and self.c set I don't have time to experiment right now, but will try to find time later. If nothing else, such a decorator would be a good prototype for the proposed functionality, and may well be sufficient for the likely use cases without needing a syntax change. Paul _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/ Hi, such a decorator would be very grateful ;-) in the standard as for example I use class parameters this way too in more than 90% of the cases. BR George

Agreed with Steven, although I do find myself a little more annoyed and bothered by a typical init than him I guess. Even so I didn't think the current proposals went far enough. To tilt the balance farther, to make it easier, let's go all the way! Instead of continuing duplication:
def __init__(self, foo, bar, baz, spam, ham): self .= foo, bar, baz, spam, ham
or
# object member assignment self .= foo .= bar .= baz .= spam .= ham
How about? def __init__(self, foo, bar, baz, spam, ham): self .= * The asterisk here used to be reminiscent of argument unpacking (minus self). That would imply a double asterisk for keyword assignment which could be used as well. Also, I did find the decorator proposal intriguing, though have to say I probably wouldn't bother to use it unless it were a builtin or I had a dozen parameters to deal with. -Mike

Le 25/04/17 à 20:16, Mike Miller a écrit :
If you *need* a shorter solution, even though I'm not entirely sure there's a real need for it, it may be easier to get something like this (I think, as there is no new operator involved) : def __init__(self, *args, **kwargs): self.* = *args self.** = **kwargs And I'm sure this could easily find new use cases outside of a constructor. But, any of these proposals, mine and yours, if you really need this to shorten the code writting time or vertical space only, is not a better idea than to propose a macro to your IDE editor, or a pull request if it's open source. Such a macro would generate automatically those long-to-write lines, and maybe implement some code folding if vertical space is an issue. - Brice

On 25 April 2017 at 22:27, Mike Miller <python-ideas@mgmiller.net> wrote:
Well, class MyClass: @auto_args def __init__(self, a, b, c=None): pass seems ideal for you then (using the auto_args decorator from https://twitter.com/judy2k/status/854330478068977664 - a 13-line function including the import of inspect and some comments). (Arguing for auto_args to be in the stdlib may be a better option than arguing for new syntax, BTW...) Paul

On Tue, Apr 25, 2017 at 6:09 PM, Paul Moore <p.f.moore@gmail.com> wrote:
(Arguing for auto_args to be in the stdlib may be a better option than arguing for new syntax, BTW...)
Having such a decorator in the stdlib would allow IDEs and syntax highlighters to know what's going on. -- Juancarlo *Añez*

On 25 April 2017 at 22:15, Brice PARENT <contact@brice.xyz> wrote:
It seems to me that the number of people for whom both of the following hold: 1. Writing out the assignments "longhand" is an unacceptable burden. 2. Using a decorator (which can be written directly in your project, doesn't even need to be an external dependency) is unacceptable. is likely to be small enough that it's not a compelling argument for adding new syntax. Add to that the fact that these people would be arguing "I want the ability to avoid writing out the assignments, but I don't want that capability enough to use a decorator" - which hardly says that a syntax change is vital - and it's difficult to see this proposal getting accepted. Paul

On 25/04/17 23:05, Paul Moore wrote:
1. Writing out the assignments "longhand" is an unacceptable burden.
There are reasons why augmented assignment was implemented. One of them was to make the code easier to read: foil = foil + 1 foil = foi1 + 1 foil += 1 Should one be silly enough to have a "foil" and "foi1" variable in scope, only one of those is clearly incrementing a variable without requiring a slightly harder look ;) It's not about the time taken to type the line. It's about the clarity of what the line is expressing.
2. Using a decorator (which can be written directly in your project, doesn't even need to be an external dependency) is unacceptable.
All of the decorators (or other language tricks that modify the object's dict) suggested so far assume that ALL of the method's arguments are to be assigned. I do not want that. I want to be able to say: def __init__(self, foo, bar, baz, spam): self .= foo, bar, spam self.baz = baz * 100 It's all still explicit inside the body of the method.
As I said above, it's not about the effort writing it out. It's about the effort (and accuracy) of reading the code after it has been written. And as I also said above, decorators don't cut it anyway (at least not those proposed) because they blindly assign ALL of the arguments. I'm more than happy to hear of something that solves both of those problems without needing syntax changes though, as that means I can have it today ;) E.

On Tue, Apr 25, 2017 at 3:30 PM, Erik <python@lucidity.plus.com> wrote:
But if this were the only argument for +=, then I'm not sure it would have ever been added :-). The really compelling cases for += are things like: foo.some_attr[get_the_key(kwarg=something)] = foo.some_attr[get_the_key(kwarg=something)] + 1 vs foo.some_attr[get_the_key(kwarg=something)] += 1 where the latter is *much* more readable, and it only calls get_the_key once, which (depending on what it does) may be both a substantial efficiency win and also guarantees this is actually an increment – we don't have to read get_the_key's source code first to figure out if the two calls actually return the same value. Another example: arr = arr + 1 arr += 1 If 'arr' is a numpy array then these actually do very different things: the first one allocates a new array and then rebinds the name 'arr' to the copy; the second modifies the array object in place. The former is usually what you want, but without augmented assignment you have to remember to do awkward things like 'arr[...] = arr + 1'. And it gets worse: since 'arr[...] = arr + 1' has to make a copy, it's about twice as slow as 'arr += 1'. The real equivalent of 'arr += 1' is 'np.add(arr, 1, out=arr)', which is *really* bad. Are there any similar arguments for .=? -n -- Nathaniel J. Smith -- https://vorpus.org

On 26/04/17 01:39, Nathaniel Smith wrote: [snip discussion of why current augmented assignment operators are better for other reasons]
Are there any similar arguments for .=?
It doesn't make anything more efficient, however all of the suggestions of how to do it with current syntax (mostly decorators) _do_ make things less efficient. So rather than a win/win as with current augmented assignment (compact/clearer code *and* potentially a performance improvement), it's now a tradeoff (wordy code *or* a performance reduction). E.

On 26 April 2017 at 21:51, Erik <python@lucidity.plus.com> wrote:
Is instance creation the performance bottleneck in your application? That seems unusual. I guess it's possible if you're reading a large database file and creating objects for each row. But in that case, as Chris A says, you may be better with something like a named tuple. In any case, optimising for performance is not what generic solutions are good at, so it's not surprising that a generic decorator involves a performance hit. Paul

On 26/04/17 22:28, Paul Moore wrote:
No, not at all. This discussion has split into two: 1) How can I personally achieve what I want for my own personal use-cases. This should really be on -list, and some variation of the decorator thing will probably suffice for me. 2) The original proposal, which does belong on -ideas and has to take into account the general case, not just my specific use-case. The post you are responding to is part of (2), and hence reduced performance is a consideration. Regards, E.

On 26 April 2017 at 22:42, Erik <python@lucidity.plus.com> wrote:
Ah, OK. I'm discounting the original proposal, as there don't seem to be sufficient (i.e., any :-)) actual use cases that aren't covered by the decorator proposal(s). Or to put it another way, if the only reason for the syntax proposal is performance then show me a case where performance is so critical that it warrants a language change. Sorry for not being clear myself. Paul

On 26/04/17 23:28, Paul Moore wrote:
It's the other way around. The proposal (arguably) makes the code clearer but does not impact performance (and is a syntax error today, so does not break existing code). The suggestions (decorators etc) make the code (arguably) clearer today without a syntax change, but impact performance. So, those who think the decorators make for clearer code have to choose between source code clarity and performance. E.

On 25 April 2017 at 23:30, Erik <python@lucidity.plus.com> wrote:
As I said above, it's not about the effort writing it out. It's about the effort (and accuracy) of reading the code after it has been written.
Well, personally I find all of the syntax proposals relatively unreadable. So that's definitely a matter of opinion. And the "explicit is better than implicit" principle argues for the longhand form. As has been pointed out, the case for += is more about incremented complex computed cases than simply avoiding repeating a variable name (although some people find that simpler case helpful, too - I'm personally ambivalent).
That's something that wasn't clear from your original post, but you're correct. It should be possible to modify the decorator to take a list of the variable names you want to assign, but I suspect you won't like that - it does reduce the number of times you have to name the variables from 3 to 2, the same as your proposal, though. class MyClass: @auto_args('a', 'b') def __init__(self, a, b, c=None): pass Paul

On 26/04/17 08:59, Paul Moore wrote:
Now you're second-guessing me.
I had forgotten that decorators could take parameters. Something like that pretty much ticks the boxes for me. I'd _prefer_ something that sits inside the method body rather than just outside it, and I'd probably _prefer_ something that wasn't quite so heavyweight at runtime (which may be an irrational concern on my part ;)), but those aren't deal breakers, depending on the project - and the vast majority of what I do in Python is short-lived one-off projects and rapid prototyping for later implementation in another language, so I do seem to be fleshing out a set of classes from scratch and writing a bunch of __init__ methods far more of the time than people with long-lived projects would do. Perhaps that's why it irritates me more than it does some others ;) E.

On Wed, Apr 26, 2017 at 11:17 AM, Erik <python@lucidity.plus.com> wrote:
I had forgotten that decorators could take parameters. Something like that pretty much ticks the boxes for me.
There are decorators with "include" and "included" in this SO Q&A: http://stackoverflow.com/q/3652851/545637
The same strategies applied by the decorators may be applied by a by a function called from within __init__.
For the cases I've found in which classes define several attributes that are initialized in the constructor I think that a library like https://github.com/python-attrs/attrs does what's needed. The downside is that code-writing tools (like IDEs) don't understand what's going on under the hood. -- Juancarlo *Añez*

On 25 April 2017 at 19:30, Erik <python@lucidity.plus.com> wrote:
Sorry - a decorator won't "blindly assign all argments" - it will do that just if it is written to do so. It is perfectly feasible to have a decorator to which you can simply pass a list of not wanted auto-parameters, or that will just auto-assign parameters that have been declared on the class body, or even inspect the function signature and check for special annotations - there are tens of ways we could spec a list of exceptions to such a decorator, and still avouid typing three times each name. That said, I am all in favor of the addition of such a decorator to the stdlib. js -><-

On 26/04/17 13:19, Joao S. O. Bueno wrote:
Right, and the three or four variants suggested (and the vars(self).update() suggestion) all do exactly that. I was talking about the specific responses (though I can see my language is vague). [FWIW I've been using Python the whole time that decorators have existed and I've yet to need to write one - I've _used_ some non-parameterized ones though - so I guess I'd forgotten that they can take parameters] E.

I was wondering that if there are so many arguments to a function that it *looks* ugly, that it might just *be* ugly. For one, too many required arguments to a function (constructor, whatever) is already strange. Binding them as attributes of the object, unmodified in a constructor also seems to be rare. Matplotlib, for example, might take 8 (optional keyword) arguments for a Figure ( https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/figure.p...), but then at a bare minimum, the vertical space required is *doubled* by the need for pesky if-blocks and logic. Self-assignment is a pittance. Are there any better examples of where adding new syntax could help readability for programmers, both new and old? On Wed, Apr 26, 2017 at 9:59 AM, Erik <python@lucidity.plus.com> wrote:

On 26/04/17 16:10, Nick Timkovich wrote:
Yes, and perhaps it's more of a problem for me because of my possibly-atypical use of Python. The background is that what I find myself doing a lot of for private projects is importing data from databases into a structured collection of objects and then grouping and analyzing the data in different ways before graphing the results. So yes, I tend to have classes that accept their entire object state as parameters to the __init__ method (from the database values) and then any other methods in the class are generally to do with the subsequent analysis (including dunder methods for iteration, rendering and comparison etc). E.

On 26/04/2017 21:50, Chris Angelico wrote:
Something like https://docs.python.org/3/library/sqlite3.html#row-objects ? -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence

On 26.04.2017 23:50, Mark Lawrence via Python-ideas wrote:
Or something like https://docs.djangoproject.com/en/1.11/topics/db/models/ Sven

On 26/04/17 21:50, Chris Angelico wrote:
I did look at this. It looked promising. What I found was that I spent a lot of time working out how to subclass namedtuples properly (I do need to do that to add the extra logic - and sometimes some state - for my analysis) and once I got that working, I was left with a whole different set of boilerplate and special cases and therefore another set of things to remember if I return to this code at some point. So I've reverted to regular classes and multiple assignments in __init__. E.

On 2017-04-25 15:30, Erik wrote:
I don't see ALL being set a big problem, and less work than typing several of them out again. Just because all args get set at once, doesn't mean the init stops there and attributes can't be further modified or deleted. In your example above, you modify baz afterward as an argument that might need to be changed, as they often do. If this doesn't give enough control, the manual way is always available. Setting them all makes simple things *very* simple, and the medium and complicated still possible.

On 26/04/17 18:42, Mike Miller wrote:
Because, some of the parameters might be things that are just passed to another constructor to create an object that is then referenced by the object being created. If one doesn't want the object's namespace to be polluted by that stuff (which may be large and also now can't be garbage collected while the object is alive) then a set of "del self.xxx" statements is required instead, so you've just replaced one problem with another ;) I'd rather just explicitly say what I want to happen rather than have *everything* happen and then have to tidy that up instead ... E.

On 2017-04-25 15:05, Paul Moore wrote:
While the word unacceptable is probably a bit too strong, this is a good insight. We have a "middling" difficulty here. Not the end of the world, but an annoyance, as the existence of the attrs library demonstrates. Kivy's properties solve a similar problem (though a bit different scope). I've overheard Java snobs sneer at Python's init so it is a well known issue. If there is going to be a solution, I submit it needs to be much easier to use and to read. Making it a little easier is not good enough, not enough reason to change. (Believe you mentioned a 3 to 2 reduction elsewhere in the thread.) Meh. If I have install a module in pip and go back to the top of the file and write an import line and then come back and write out a decorator it's not going to be worth it. Currently, I use editor snippets to ease authoring of init, but readability and DRY are still concerns afterward. As the new syntax ideas piggyback on existing syntax, it doesn't feel like that its a complete impossibility to have this solved. Could be another "fixed papercut" to drive Py3 adoption. Taken individually not a big deal but they add up.

On 26/04/17 19:15, Mike Miller wrote:
*sigh* OK, this has occurred to me over the last couple of days but I didn't want to suggest it as I didn't want the discussion to fragment even more. But, if we're going to bikeshed and there is some weight behind the idea that this "papercut" should be addressed, then given my previous comparisons with importing, what about having 'import' as an operator: def __init__(self, a, b, c): self import a, b self.foo = c * 100 Also allows renaming: def __init__(self, a, b, c): self import a, b, c as _c Because people are conditioned to think the comma-separated values after "import" are not tuples, perhaps the use of import as an operator rides on that wave ... (I do realise that blurring the lines between statements and operators like this is probably not going to work for technical reasons (and it just doesn't quite read correctly anyway), but now we're bikeshedding and who knows what someone else might come up with in response ...). E.

On Wed, Apr 26, 2017 at 11:29:19PM +0100, Erik wrote:
[snarky] If we're going to randomly choose arbitrary keywords with no connection to the operation being performed, can we use `del` instead of `import` because it's three characters less typing? [/snarky] *wink* But seriously, I hate this idea. `import` has certain semantics that don't apply here: - import goes through the full import system (sys.modules, reading files from disk, sys.path, etc.); this "self import ..." does not; - import creates local variables; this "self import ..." does not. The semantics are very different and there's little or no connection between importing a module and setting an attribute on self. If we're going to discuss pie-in-the-sky suggestions, here is an only semi-serious suggestion. A statement that injects identifiers into an explicitly given name space: inject spam, eggs as cackleberry, cheese into self (If you don't like "inject", I'm okay with "load" or even "push".) This would be equivalent to: self.spam = spam self.cackleberry = eggs self.cheese = cheese Presumably it would be implemented a little more efficiently, e.g. only a single lookup for self, etc. Note that the target is completely general: you can inject into any object which will accept setting attributes: inject spam, eggs into namespace If the "inject as" form is used, the value can be any expression: inject x + 1 as y into namespace otherwise the value must be a valid identifier but not necessarily a local variable. If I were designing a language from scratch, one with a maximalist approach to syntax, I'd take this seriously. But Python has relatively minimal syntax and keywords, and the problem this solves isn't big or important enough for the disruption of adding a new keyword. -- Steve

On 27/04/17 23:43, Steven D'Aprano wrote:
The keyword I chose was not random or arbitrary and it _does_ have a connection to the operation being performed (bind a value in the source namespace to the target namespace using the same name it had in the source namespace - or rename it using the 'as' keyword).
can we use `del` instead of `import` because it's three characters less typing?
Comments like this just serve to dismiss or trivialize the discussion. We acknowledged that we're bikeshedding so it was not a serious suggestion, just a "synapse prodder" ...
But seriously, I hate this idea.
Good. It's not a proposal, but something that was supposed to generate constructive discussion.
The semantics are very different and there's little or no connection between importing a module and setting an attribute on self.
At the technical level of what goes on under the covers, yes. At the higher level of what the words mean in spoken English, it's really not so different a concept.
If we're going to discuss pie-in-the-sky suggestions,
That is just dismissing/trivializing the conversation again.
(If you don't like "inject", I'm okay with "load" or even "push".)
No you're not, because that's a new keyword which might break existing code and that is even harder to justify than re-using an existing keyword in a different context.
the problem this solves isn't big or important enough for the disruption of adding a new keyword.
So far, you are the only one to have suggested adding a new keyword, I think ;) E.

On 28 April 2017 at 00:18, Erik <python@lucidity.plus.com> wrote:
I disagree. If you were importing into the *class* (instance?) I might begin to see a connection, but importing into self? Also, if you try to make the obvious generalisations (which you'd *have* to be able to make due to the way Python works) things quickly get out of hand: def __init__(self, a): self import a OK, but self is just a variable name, so we can reasonably use a different name: def __init__(foo, a): foo import a So the syntax is <variable> import <var-list> Presumably the following also works, because there's nothing special about parameters? def __init__(x, a): calc = a**2 x import calc And of course there's nothing special about __init__ def my_method(self, a): self import a Or indeed about methods def standalone(a, b): a import b or statements inside functions: if __name __ == '__main__: a = 12 b = 13 a import b Hmm, I'd hope for a type error here. But what types would be allowed for a? Class instances? Well, a is an int, which is a class instance. See what I mean? Things get out of hand *very* fast.
Well, picking an existing keyword simply[1] to avoid using a new one is an equally bad justification. [1] I know that's not what you did, but without the "it's sort of like importing" justification, which Steven has said he doesn't agree with, and nor do I, you're only left with "because it means we don't need a new keyword" - at which point suggesting that we need to bite the bullet and consider whether a new keyword is justified is perfectly reasonable. (IMO, a new keyword is *not* justified - nor is reusing an existing one unless you can find a *much* more compelling parallel than you did with import). To summarise: 1. There's some serious technical issues with your proposal, which as far as I can see can only be solved by arbitrary restrictions on how it can be used 2. The use of import as a keyword is at best controversial 3. Using a new keyword for this construct is pretty much a non-starter But I do agree that the proposal was a reasonable way to reframe the debate here. It's just that what it's done is confirm my belief that there's nothing here that needs improvement compared to the status quo (even though I agree that long lists of "self.x = x" statements can be tedious boilerplate). Paul

On 28/04/17 10:47, Paul Moore wrote:
I know you already understand the following, but I'll spell it out anyway. Here's a module: ----------------- $ cat foo.py def foo(): global sys import sys current_namespace = set(globals().keys()) print(initial_namespace ^ current_namespace) def bar(): before_import = set(locals().keys()) import os after_import = set(locals().keys()) print(before_import ^ after_import) initial_namespace = set(globals().keys()) ----------------- Now, what happens when I run those functions: $ python3 Python 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information.
... so the net effect of "import" is to bind an object into a namespace (a dict somewhere). In the case of 'foo()' it's binding the module object for "sys" into the dict of the module object that represents 'foo.py'. In the case of 'bar()' it's binding the module object for "os" into the dict representing the local namespace of the current instance of the bar() call. Isn't binding an object to a namespace the same operation that assignment performs? So it's a type of assignment, and one that doesn't require the name to be spelled twice in the current syntax (and that's partly why I took offense at a suggestion - not by you - that I was picking "random or arbitrary" keywords. I picked it for that specific reason). I realize that there are other semantic changes (importing a module twice doesn't do anything - and specifically repeated "import * from mod" will not do anything if the module mutates) - and perhaps this is your point.
self.a = a
foo.a = a
x.calc = calc
self.a = a
a.b = b
a.b = b
Hmm, I'd hope for a type error here. But what types would be allowed for a?
I think you're assuming I'm suggesting some sort of magic around "self" or some such thing. I'm not. I've written above exactly what I would expect the examples to be equivalent to. It's just an assignment which doesn't repeat the name (and in the comma-separated version allows several names to be assigned using compact syntax without spelling them twice, which is where this whole thing spawned from).
See what I mean? Things get out of hand *very* fast.
I don't see how that's getting "out of hand". The proposal is nothing more complicated than a slightly-different spelling of assignment. It could be done today with a text-based preprocessor which converts the proposed form to an existing valid syntax. Therefore, if it's "out of hand" then so is the existing assignment syntax ;) FWIW, I should probably state for the record that I'm not actually pushing for _anything_ right now. I'm replying to questions asked and also to statements made which I think have missed the point of what I was trying to say earlier. So I'm just engaging in the conversation at this point - if it appears confrontational then it's not meant to.
To be honest, I still don't understand what the serious technical issues are (other than the parser probably doesn't handle this sort of keyword/operator hybrid!). Is it just that I'm seeing the word "import" in this context as a type of assignment and you're seeing any reference to the word "import" as being a completely different type of operation that comes with baggage? Regards, E.

typing.NamedTuple was already mentioned in this discussion, I just would like to add few comments: 1. I think that changing Python syntax to support declarative classes is not a realistic option in nearby future. In contrast, there is an existing syntax change - variable annotations - that can be actively experimented with. Variable annotations are designed to play well with metaclass machinery, for example, they will respect __prepare__ method, if it injects __annotations__ as a custom mapping, etc. 2. Possible strategy for declarative classes could be developing independent *composable* mixins/traits: @comparable @pickleable class MyClass: x: int y: int or class MyClass(Comparable, Pickleable): x: int y: int The problem with the second approach is that it can lead to metaclass conflicts. A possible solution will be to provide a standard metaclass for mixins in stdlib. 3. Composability could be a problem if only normal variable assignments are used: after one mixin transforms the class, the original declarations are lost. Simple possible solution would be to agree that mixins should *not* modify __annotations__, only __dict__. Currently, PEP 484 and PEP 526 specify that preferable use of annotations is type declarations. However, I think this can be generalized to just declarations. For example, by providing a special typing construct that will allow additional info apart from type: def large(val): return val > 42 @comparable @pickleable class MyClass x: Spec(int, validator=large) y: Spec(List[int], default_factory=list) -- Ivan

On 29 April 2017 at 09:51, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
Between __init_subclass__, __set_name__, __annotations__, and guaranteed order preservation for class namespaces, Python 3.6 has provided some pretty powerful tools for folks to experiment with new approaches to declarative class definitions. One point worth considering is that given standard library implementations of the explicit class decorator forms (e.g in the `types` module, or in the modules defining the relevant protocols), it should be relatively straightforward to create mixin classes that use __init_subclass__ to implicitly apply the explicit decorators. The same doesn't hold the other way around - given a mixin, it's tricky to convert that to a class decorator form. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 28 April 2017 at 23:04, Erik <python@lucidity.plus.com> wrote:
Well with your clarification, it's now clear to me that your proposal is for <obj> import <var1>, <var2> ... to be equivalent to _tmp = <obj> _tmp.var1 = var1 _tmp.var2 = var2 ... del _tmp Please correct me if I'm wrong here - but that's what I get from your comments. Note that the way I've described the proposal allows for an *expression* on the left of import - that's simply because I see no reason not to allow that (the whole "arbitrary restrictions" thing again). So based on the above, you're proposing reusing the import keyword for a series of attribute assignments, on the basis that the current meaning of import is to assign values to a number of attributes of the current namespace. However, that misses the fact that the main point of the current import statement is to do the *import* - the RHS is a list of module names. The assignment is just how we get access to the modules that have been imported. So by "reusing" the import keyword purely by analogy with the assignment part of the current semantics, you're ignoring the fundamental point of an import, which is to load a module. So based on your clarification, I'm now solidly against reusing the "import" keyword, on the basis that your proposed syntax doesn't import any modules. On the other hand, the construct you describe above of repeated attribute assignments is only really common in __init__ functions. It's similar to the sort of "implied target" proposals that have been raised before (based on the Pascal "with" statement and similar constructs): SOME_KEYWORD <obj>: .var1 = var1 .var2 = var2 Your construct is more limited in that it doesn't allow the RHS of the assignments to be anything other than a variable with the same name as the attribute (unless you extend it with "expr as var", which I can't recall if you included in your proposal), nor does it allow for statements other than assignments in the block. And the implied target proposal doesn't really avoid the verbosity that prompted your proposal. So while it's similar in principle, it may not actually help much in your case. So the reason I was saying things got "out of hand" was because the final version I got to had absolutely nothing to do with an import (in my view) - but on reflection, you're right it's no different than the self version. It's just that the self version also has nothing to do with an import! However, in the original context, it's easier to see "name injection into a namespace" as a common factor, and miss "importing a module" as the key point (at least it was easier for me to miss that - you may have seen that and been happy that your proposal was unrelated to modules even though it used the import keyword). I agree with Nick - the place we should be looking for improvements here is in trying to find abstractions for common patterns of class creation. Instead of looking at how to make the low-level mechanisms like __init__ easier to write, we should be concentrating on making it rare for people to *need* to use the low-level forms (while still leaving them present for specialised cases). Paul

On Fri, Apr 28, 2017 at 11:04:00PM +0100, Erik wrote:
Isn't binding an object to a namespace the same operation that assignment performs?
Yes.
That was me. You took *offense*? For somebody who made it clear that you didn't care about this "self import name" suggestion, you sure are touchy to have it criticised. Assignment is the least interesting and important thing that the import statement does. Many keywords do an name binding operation, not just import, but also: for, with, except, def, class and most relevant to my comment: del is actually defined by the documentation as a name binding operation. https://docs.python.org/3/reference/executionmodel.html I'm sorry that the joke was too subtle, I did put a wink after it, but you deleted it from your reply. The point is, import's name binding is the least interesting and least important thing that it does. We could have defined import to be a function that returns a module object, or a tuple of objects, and left the binding up to the caller: # What If import were a function instead of a statement? math = import("math") sin, cos = import("math", "sin", "cos") That's rather similar to what __import__ does, but of course import as a statement is a much better design. Nevertheless, we could take away the name-binding aspect of import, and it would still be recognisable as performing an import: it would search the module cache, search the path, load modules, compile the code, create module objects, and all the other things needed to import a module. The name binding (assignment) is just an convenience. But take away the *import functionality*, and "import" is no longer an appropriate name. Why is it called import when it doesn't import anything? Because of the name binding? Well, these keywords all do name binding too, why can't we use them? self for attr self except attr self def attr self class attr etc. I'm sorry that my posting style rubbed you the wrong way and made you defensive, by I stand by my comment: chosing import as the keyword to do something completely unrelated to importing is an arbitrary choice, and we might as well choose del (or def, or for) and save some typing. If Python were the sort of language to use more symbols rather than names, like Perl, we could invent a bind to the left arrow operator: self <- a, b, c Well, that wouldn't work, because of operator precedence. But you get the idea. Or we could bind to the right: a, b, c -> self Make of them what you will. -- Steve

On 25/04/17 22:15, Brice PARENT wrote:
it may be easier to get something like this (I think, as there is no new operator involved) :
No new operator, but still a syntax change, so that doesn't help from that POV.
What is "self.* = *args" supposed to do? For each positional argument, what name in the object is it bound to? E.

On Tue, Apr 25, 2017 at 4:31 PM, Erik <python@lucidity.plus.com> wrote:
For what it's worth, that's what I don't really like about the initially proposed syntax too ... self .= foo, bar, baz works OK, but: tup = foo, bar, baz self .= tup doesn't work. Admittedly, that could be part of the definition of this feature, but it feels really unexpected to all of a sudden give my tuple a temporary name and have the code behave in a dramatically different fashion.
-- Matt Gilson | Pattern Software Engineer getpattern.com <https://www.getpattern.com?utm_source=email&utm_medium=email&utm_campaign=signature-matt>

On Mon, Apr 24, 2017 at 6:08 PM, Erik <python@lucidity.plus.com> wrote:
This isn't a direct response, but you might be interested in the attrs library: https://attrs.readthedocs.io/en/stable/examples.html -n -- Nathaniel J. Smith -- https://vorpus.org

On 25.04.2017 20:16, Mike Miller wrote:
Same here. And practical experience tells me that the usage of this decorator wouldn't last long. In Steven's "early stages of development", the day will come where there is a need for a parameter which must not be an attribute on self. So I prefer the "annoyance" since it's bloody easy to read AND to change. Sven

FWIW I always liked Dart's/Ruby's/Crystal's/(Coffee|Moon)Script's/WhateverElse's style: class Cls { Cls(this.a); // IT'S MAGIC } but the Python equivalent is admittedly weirder: def ___init__(self, self.attr): partly because, it'd have to work on pretty much any other variable name, yet there's no other real use case outside of methods. -- Ryan (ライアン) Yoko Shimomura > ryo (supercell/EGOIST) > Hiroyuki Sawano >> everyone else http://refi64.com On Apr 24, 2017 8:08 PM, "Erik" <python@lucidity.plus.com> wrote:

On Wed, Apr 26, 2017 at 10:05 AM, Ryan Gonzalez <rymg19@gmail.com> wrote:
In a sense, what you have is this (modulo keyword arguments): def __init__(*args): self, self.attr = args which is perfectly legal, albeit weird. So it needn't actually be magic per se, just a change in the definition of an argument name (from NAME to whatever is legal as an assignment target). I don't think it's particularly useful, though. ChrisA

On Tue, Apr 25, 2017 at 8:05 PM, Ryan Gonzalez <rymg19@gmail.com> wrote:
def ___init__(self, self.attr):
I'm not a python developer, I'm just a developer that uses python. That said, I really like this form. It eliminates most of the redundancy, while still being explicit. It's true that you have to repeat the 'self' name, but it feels like the right balance in my mind. It seems like anyone who's familiar with python function definitions would immediately grasp what's going on. -- Jerry

On Wed, Apr 26, 2017 at 6:57 PM, Mike Miller <python-ideas@mgmiller.net> wrote:
Yes, I like it too. Removes the triple repetition, and has precedent in the other languages.
This discussion has been around how to deal with repetition in object constructors, and the proposals have been a new assignment statement (patch assignments?) or decorators to __init__ (patch init). In my experience, what Python is lacking is a way to declare attributes outside of the constructor. Take a look at how it's done in C#, Swisft, or Go. Object attributes outside of the constructor would solve things more relevant than the vertical space used when assigning constructor parameters to attributes. For example, multiple inheritance is well designed in Python, except that it often requires constructors with no parameters, which leads to objects with no default attributes. The namespace after "class" is taken by class attributes. There's no namespace for default object attributes outside __init__. Solving that would likely lead to simple and sensible solutions to the OPs query. I don't think that a solution to the OPs query should be treated as a No-No, because there's namedtuple, which is quirky, but solved lots of use cases (the same goes for Enum). IMHO, something like the attrs library solves lots of common use cases... except that tool support is poor because attrs is not part of stdlib. It is not about having to write a few more lines in an __init__ constructor. There are common and frequent use cases of "objects are mostly data" that are partially solved in Python (mostly through namedtuple?). Cheers! -- Juancarlo *Añez*

On Thu, Apr 27, 2017 at 7:28 PM, Steven D'Aprano <steve@pearwood.info> wrote:
They make attribure declarations at the class declaration scope be instance attributes. Python makes that kind of declaration class attributes (statics in some other languages). This is the spec for C#: https://goo.gl/FeBTuy The reason *why* they do it that way is because declaring instance fields/variables is much more frequent than declaring class ones.
Instance attributes may be defined with or without default values without having to pass them as arguments to or mention them in a constructor.
A class hierarchy in which there is multiple inheritance requires constructors with no arguments. This is typical: https://goo.gl/l54tx7 I don't know which would be the best syntax, but it would be convenient to be able to declare something like: class A: var a = 'a' And have "a" be an instance attribute. -- Juancarlo *Añez*

On Wed, Apr 26, 2017 at 03:54:22PM -0400, Jerry Hill wrote:
I don't like it, not even a bit. It's not general, "self" is effectively a special case. Consider: What happens if you use this syntax in a top-level function rather than a method? (Or a static method?) def function(x, y, x.attr): ... (And don't forget that behind the scenes, methods *are* functions.) What would this syntax even mean? Or if you use some other name other than the first parameter? def method(self, spam, foo.eggs): ... Conceptually, it is mixing up two distinct tasks: - declaring the parameter list of the function/method; - running part of the body of the method. The parameter list of the method is the *interface* to the method: it tells you the public names and default values (and possibly types, if you use type annotations) of the method parameters. But this syntax overloads the parameter list to show part of the *implementation* of the method (namely, that some parameters are assigned directly to attributes of self). Every time the implementation changes, the parameter list will change too. For instance, if you decide to add some argument validation to your arguments (checking that their values and types are correct), using the self.attr form is inappropriate. And while it looks okay in the toy example shown by Ryan: def ___init__(self, self.attr): it doesn't look so good in larger, more realistic cases, especially with other syntax. Here's a parameter list taken from some real code of mine, with the "self." syntax added: class BinnedData(object): def __init__(self, self.number:int, self.start:float=None, self.end:float=None, self.width:float=None, *, self.policy=None, self.mark=False ): The repetition of "self" is not only tedious and verbose, it adds noise to the parameter list and pushes the reader's attention away from the important part (the name of the parameter, e.g. "width") and to something irrelevant to the interface ("self"). And I am not looking forward to having to explain to beginners to Python why this doesn't work: data = BinnedData(self.number = 8, self.start = 0, self.end = 20) -- Steve

On Fri, Apr 28, 2017 at 9:21 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Exactly the same thing as: def function(*args): x, y, x.attr = args def method(*args): self, spam, foo.eggs = args It's well-defined - at least for positional args. Not sure what keyword arg handling would be though. ChrisA

On Fri, Apr 28, 2017 at 09:54:55AM +1000, Chris Angelico wrote:
Obviously we can define syntax to do anything we like, but what is the logical connection between the syntax and the semantics? What part of "function parameter list" suggests "assign attributes to arbitrary objects"? Do we support arbitrary targets in their full generality? def function(arg, (name if name is not None else other).the_object.attr[0].method(foo)['key'].argument ): ... Of course, there are lots of things which are technically allowed but if someone is silly enough to do it, we say "don't do that". However, even the simple case is problematic: def trigonometry(x, y, math.pi) We could make that parameter declaration have the side-effect of assigning to math.pi, but that's a pretty surprising change to function declarations. Everything else declared inside a parameter list is local to the function: parameters themselves are local variables, default values and annotations are stored in the function not externally. The only thing which is not local is that default values may be arbitrary expressions, which may have side-effects, but that is not a declaration, its an expression evaluated for its value. What's the connection between parameter declarations and this sort of external side-effect? It seems pretty arbitrary to be a built-in part of the language, like having def function(x) automatically print x, or pickle it and upload the file to the cloud. (Obviously the side-effect doesn't occur until the function is called, but still, it is part of the parameter list declaration, not the body of the function.) I have grave doubts that people will find this obvious, let alone useful. Having a function declaration, the parameter list, have side-effects outside of the function seems like both a surprising and a bad idea to me.
It's well-defined - at least for positional args. Not sure what keyword arg handling would be though.
Why would it be any different? If it makes sense to allow: def function(spam, eggs, something.foo) assign to something.foo, then why should it matter whether it was called by keyword or positional? Or declared as keyword only? def function(spam, eggs, *, something.cheese) I don't think it makes sense either way, but it isn't because it is a keyword arg. I think it is arbitrarily mixing two distinct things into one convenient but confusing syntax. function(spam=1, eggs=2, something.cheese=3) # won't work -- Steve

On Fri, Apr 28, 2017 at 2:06 PM, Steven D'Aprano <steve@pearwood.info> wrote:
What part of a 'for' loop suggests that you can do this? odds = [0]*10; evens = [0]*10 for idx, (odds if idx%2 else evens)[idx//2] in stuff: ... Nothing whatsoever says that this is a good idea, but it's perfectly legal, because the for loop is defined in terms of assignment. If this were to be accepted (which, fwiw, I'm not actually advocating, but IF), it would also be defined in terms of assignment. You still shouldn't be assigning to arbitrary objects, especially not randomly rebinding module names, but it's easy to grok the assignment equivalence. And yes, it WOULD reinstate the argument unpacking removed by PEP 3113. So for this to go forward, the objections in that PEP would have to be addressed. ChrisA

On Fri, Apr 28, 2017 at 03:30:29PM +1000, Chris Angelico wrote:
I'm not sure what "this" is supposed to do. You've written some obscure but perfectly legal Python code:
My guess is that "this" refers to the side-effect that assigning to a list item updates the list item. Um, yeah, it does. What's your point? Of course it does. That's what its supposed to do. Perhaps you think that there's something weird about using an arbitrary assignment target as the for-loop. I don't know why you think that's weird. Here's a simpler example: for obj.attr in seq: ... Yes, its a bit unusual to do that, but its not weird. The assignment target for a loop is just an ordinary assignment target, and the assignment occurs during the execution of code, just like any other assignment that occurs inside the body of the function. What is weird is to have the function *declaration* have global side effects and perform assignments outside of the function. The parameter list is *not* a general assignment statement, it is a declaration of what local variables will be assigned to when you call the function. In Python 3, function paramaters are plain (non-qualified) identifiers, not general assignment targets. Even in Python 2, the most that was supported were tuple-unpacking. Even that can be read as conceptually just a declaration: def func(a, (x,y)): declares that the first argument is a, and the second argument is a pair of values x and y. But this proposal has to be read as a declaration plus a separate assignment: def func(a, spam.x): declares that the second argument is called "x", (but not spam.x), and *in addition* there will be an assignment spam.x = x at some point, presumably before the function body gets entered.
Why should it be defined in terms of general assignment? That's the point I'm making. While function sigs are a form of assignment, they're more of a declaration than an executable statement like the other binding statements. There's a superficial connection, but they really are quite different things. (For example: if you allow spam.foo as a parameter, that can call arbitrary Python code in spam.__setattr__, which assigning to foo as a parameter will not do.)
You need to speak to more beginners if you think the connection between spam.x and x is obvious: def func(spam.x): print(x) Where is x declared? It looks like there's a local spam.x which isn't used, and a global x that is. But that's completely wrong. Without the context of somebody telling you "this is syntax for magically assigning to self.attributes in the constructor", I believe this will be unfathomable to the average non-expert.
Nicely remembered :-) -- Steve

On Fri, Apr 28, 2017 at 4:57 PM, Steven D'Aprano <steve@pearwood.info> wrote:
Waaaaaait a minute. Since when is the *declaration* doing this? No no no. In every proposal, it's been a part of the function's *execution*. The original example was an __init__ function that does the exact same thing as my for loop example: it first sets "self" to the first parameter, and then sets some attributes on self. As you say:
Um, yeah, it does. What's your point? Of course it does. That's what its supposed to do.
There is nothing surprising in it (although my example was deliberately convoluted and a bad example of code). It's assignment, pure and simple. When you call a function, the values you pass to it get assigned to the names provided in the declaration. No assignments happen at declaration other than the function's own name. (I suppose you could say that default argument values are "assigned" at that point, but I prefer to say "collected", since they're basically assembled into a tuple and slapped into an attribute on the function.)
There's a lot more similarities than you might think. For example, both of these will create "spam" as a local name, shadowing the global: spam = 1 def func1(spam): print(spam) def func2(): spam = 2 print(spam) As will many other forms of assignment, like "import spam" or "with x as spam:". Some forms are more restricted than others ("import" requires a NAME, not an arbitrary expression), but they all behave the same way with regard to global/nonlocal/local naming - you can "global numpy; import numpy" inside a function to do a delayed import, for instance.
Sure, but that's a difference between simple name assignment and item/attribute assignment, not a difference between function parameters and other types of variables. Are you next going to point out that you can't "import spam as foo[0]"? Or that importing is somehow special because of this? No. It's still assignment, just one with a restricted syntax.
Not sure what you mean. By my reading, that's exactly correct - there IS a global x that is being used here, although "local spam.x" is not accurate. I wouldn't call that "completely wrong". And if you find that confusing, let's look at importing. os = "Linux" path = "/tmp" def func(): import os.path print(path) What's local and what's global? What name is actually bound by "import os.path"? What if you said "import os.path as path"? "import os.path as os"? Does all this confuse people? IMO it's *less* confusing if you can simply say "this is equivalent to <parameter> = <argument>".
I didn't remember the PEP number, but the Google is helpful :) ChrisA

On Fri, Apr 28, 2017 at 05:23:59PM +1000, Chris Angelico wrote:
Waaaaaait a minute. Since when is the *declaration* doing this?
That's what the suggested syntax says. You put the attribute assignment you want in the parameter list, which is part of the function/method declaration, not the body. If I recall correctly, the proposal was exactly: def __init__(self, self.attr): That's not in the body of the function, it's in the parameter list, hence part of the declaration. When you call the constructor: instance = MyClass(42) __init__ is called, the argument 42 is bound to the formal parameter "attr", the assignment self.attr = attr is run, and THEN the body of the method is called. (Before you object, see below for justification for why there has to be both a local variable and an attribute.) If there is an exception, what do you think the stack trace will point to? It cannot point to a line of source code that says "self.attr = attr", because there is no such line. At best, it can point to the declaration "def __init__(self, self.attr):" although it might not even do that.
No no no. In every proposal, it's been a part of the function's *execution*.
I did explicitly state that the attribute assignment doesn't occur until the function is called, so I'm aware of this. Nevertheless, the source code being executed at function call time does not reside in the body of the function, but in the function signature. Maybe I'd be less disturbed by this if we had late binding of defaults (defaults are re-evaluated each time they are needed), and a history of abusing that for the side-effects: def function(a, b=print("using b default") or default): But Python function defaults are early binding, and they're only evaluated once, so there's currently no analogy to this proposal. [...]
Right. Function parameters are local variables. But you can't write: def func(global spam): to make it assign to a global instead of a local, or nonlocal, and you can't (currently) write: def func(math.spam): to make it assign to an attribute of the math module. It's a big conceptual leap to go from "formal parameters are always bound to local variables of the function" to "formal parameters of the function are bound to anything, anywhere". I wonder whether any other language allows this?
Yes, we could restrict the syntax to simple identifiers with a maximum of a single dot: def __init__(self, self.attr, # allowed self[0].foo(1)['key'], # SyntaxError ): which will avoid the worst of the horrors. [...]
No there isn't. "x" is the parameter name, even though it is written with a "spam." prefix. Let's go back to the original: class MyClass: def __init__(self, self.attr): ... instance = MyClass(42) That binds 42 to the local variable "attr", as well as doing the self.attr=attr assignment. After all, surely we want to be able to refer to the parameter by its local variable, for efficiency (avoiding redundant look-ups of self), or to by-pass any __getattr__ on self. Or to really push the message, think about calling it by keyword: instance = MyClass(self.attr=42) # No. instance = MyClass(attr=42) # Yes. In other words, even though the formal parameter is written as "self.attr", the argument is bound to the local "attr". Quite frankly, I thought this was so self-evident that it didn't even occur to me that anyone would have imagined that no local variable "attr" would be created. As weird as it is to have a parameter declared as "self.attr" but bound to "attr", that's not as weird as having a parameter declared as "self.attr" but not bound to any local at all. (If instance attributes and locals used the same notation, namely a bare identifier, we could gloss over this distinction. But we're not using Java :-) -- Steve

On Sat, Apr 29, 2017 at 1:31 AM, Steven D'Aprano <steve@pearwood.info> wrote:
I don't see why it couldn't. You'll get an AttributeError trying to set "attr", or maybe it'll be an error from inside __setattr__, but either way, it's going to come from the 'def' line.
It's a big conceptual leap to go from "iteration steps through a sequence, binding a variable to successive items in it" to the full flexibility of Python's for loop. Just look at this: for idx, val in enumerate(iterable): It's completely intuitive to an expert Python programmer because it's a common idiom, but think about it. Firstly, you create an iterable as a derivative of another iterable. We're building not just lists out of lists, but lazy iterables out of each other. And that iterable yields two-item tuples. Finally, we do an unpacking assignment, stashing the two items into two separate names. And nobody is suggesting that we should restrict this syntax to simple examples like this; there is absolutely nothing wrong with having incredibly complicated and ridiculous assignments inside a for loop. Why? Because a for loop does simple assignment, nothing more, nothing less.
Yeah. In that sense, it actually is more like decorator syntax, which is defined as a strict subset of expression syntax - you have an absolute guarantee that any legal decorator is semantically equivalent to the same expression elsewhere, but there are lots of expressions that you can't use in a decorator. I'd be fine with that. It's still defined in terms of assignment, but your example would indeed give a SyntaxError.
Waaaaaaaaaait. Why? If you're slapping it in there, you should have no guarantee that it exists under any other name.
No way is that self-evident. If you want something in two places, you put it there yourself. The parameter puts the value into exactly one place. In all my examples of equivalence, there was not a single hint of a local name "attr". Maybe that's why you consider this weird? Because your imagined semantics are NOT equivalent to assignment?
Haha. Yeah, well, that actually doesn't work - or at least, not in C++ (not sure about Java but I expect it's the same). You can have locals and instance attributes with the same names, and then you refer to the latter as "this->foo". So it still wouldn't help. :) ChrisA

On Fri, Apr 28, 2017 at 5:02 PM, Random832 <random832@fastmail.com> wrote:
Now that is a very good, dare I say it, argument. It's easy enough to explain to an expert, but it's not clear to a beginner. Expert explanation (omitting kwargs): def __init__(self, self.spam=[]): is roughly equivalent to: _defaults = ([],) def __init__(*args): self, self.spam = (args + _defaults)[:2] It's pretty straight-forward for default arguments to be evaluated at declaration time, but assignment targets at execution time; but it isn't clear. I was never really in favour of the proposal, but this is a fairly big downside. ChrisA

On Thu, Apr 27, 2017 at 7:21 PM, Steven D'Aprano <steve@pearwood.info> wrote:
I wasn't thinking that 'self' should be a special case at all.
It would mean the same thing as "def function(x, y, z):", which is: take the first three positional arguments, and assign them to the local names listed in the function definition. The same as def function(foo, bar, baz): x = foo y = bar x.attr = baz
What's the problem? How is this any different from def method(self, bar, baz): spam = bar foo.eggs = baz I mean, it does imply that 'foo' is some globally writable bit of data, being modified via side effect of calling a method, which is certainly a code smell. Still, we're all consenting adults here, and it doesn't seem any worse than the already-legal variant I mention above.
Is assigning your parameters to their local names really part of 'running the body of the method'? I mean, I almost never write my functions like this: def fun(*args): foo = args[0] bar = args[1] baz = args[2] Do you? If not, aren't you using the function definition to assign parameters to local names? Also, if you do write functions that way, aren't you *also* losing the semantic hints about what parameters are acceptable and what they are used for?
I guess I don't agree that the function definition only tells you the 'public names' of the parameters being passed. Instead, I think the function definition tells you the names those parameters are going to go by inside the function. That is, their local names. And if you're taking the parameters and assigning them to attributes of self anyway, then you have the exact same issue with your parameter list changing every time the implementation changes anyway, don't you?
I won't argue with that, I suppose. I'm not convinced it's worse than the status quo, but yes, if you pack all the possible options into your parameters, you can end up with a messy function signature. I guess my python experience is atypical if most of your methods take 6+ parameters, including some that are keyword-only, most of which have default values, and are annotated with type hints.
For what it's worth, if that construct wouldn't work, then I'm convinced that the idea is a bad one. :) I'm not sure I see why that would have to be forbidden, though. -- Jerry

On 2017-05-01 11:50, Jerry Hill wrote:
I don't think the existing cases are really parallel. In the example with x.attr, you're introducing a dependency among different arguments. That isn't currently possible. You can't define a function like this: def function(x, y, x): So there's never any question of which order function arguments are bound in. In your system, what would happen if I do this: def function(x.attr, x) Would this, when called, assign an attribute on whatever is referred to by a global variable x, and then, after that, create a local variable x (necessarily shadowing the global whose attribute I just set)? You can argue that assigning in left-to-right order is the natural choice, and I'd agree, but that doesn't change the fact that introducing potential order dependency is new behavior. You also say that the existing behavior is "assign to local names", but that's just the thing. "x.attr = blah" is not an assignment to a local name, because "x.attr" is not a name. It's an attribute assignment, because "x.attr" is an attribute reference. Those are very different things. (The latter, for instance, can be hooked with __setattr__, but assignment to local names is not hookable.) Right now you can only use function arguments to assign to local names. But if you could start putting other things as function arguments, you could use them to assign to things that are not local names. That is a major change. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

Why not simply do this : class MyClass: def _set_multiple(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) def __init__(self, a, b, c): self._set_multiple(a=a, b=b, c=c) -> No new syntax, no inspection, simple to read, parse and understand, and explicit. And you could directly do some simple cleanings or not define a variable, like : class MyClass: def _set_multiple(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) def __init__(self, a, b, c): self._set_multiple(a=a * 2, b=clean_field(b)) - Brice Le 25/04/17 à 03:08, Erik a écrit :

On Wed, Apr 26, 2017 at 01:12:14PM +0200, Brice PARENT <contact@brice.xyz> wrote:
self.__dict__.update(kwargs) Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

On Wed, Apr 26, 2017 at 2:02 PM, <tritium-list@sdamon.com> wrote:
vars(self).update(kwargs)? ㎝ -- |:**THE 🍺-WARE LICENSE** *(Revision ㊷)*: | <miron@🐍.it> wrote this mail. As long as you retain this | notice you can do whatever you want with this stuff. | If we meet some day, and you think this stuff is worth it, | you can buy me a 🍺 in return. —㎝

On Wed, Apr 26, 2017 at 08:02:39AM -0400, tritium-list@sdamon.com wrote:
self.__dict__.update(kwargs)
Touching __dict__ feels dirty to me.
Indeed. The right way is: vars(self).update(kwargs) although that doesn't work if self is written to use __slots__ instead of having a __dict__. You can even (almost) get this same effect and still have named parameters: def __init__(self, fe, fi, fo, fum, spam, eggs, foo, bar, baz): vars(self).update(locals()) del self.self -- Steve

On 2017-04-26 18:46, Mike Miller wrote:
An alternative is to use a special comment, for example: class MyClass: def __init__(self, a, b, c): #> a, b, c And then use, say, a Python script to expand it to: class MyClass: def __init__(self, a, b, c): #> a, b, c self.a = a self.b = b self.c = c #< The Python script could be called from the editor. If you want to add or remove an argument, edit the comment and then run the script again. (Hopefully it'll be smart enough to make the necessary changes).

Le 26/04/17 à 19:46, Mike Miller a écrit :
You still save some typing (no need for /self./ for every assignment), and a lot of vertical space. Also, it allows you to use any dict (like **kwargs) as well as doing very common things like : class MyClass: def _set_multiple(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) def __init__(self, a, b, c): self._set_multiple(a=a, _b=b) which is equivalent to the following really common pattern : class MyClass: def __init__(self, a, b, c): self.a = a self._b = b (well, I didn't try it, but it should work) This means your public API (the signature of the constructor here) has no incidence on the way the class works on the inside. Meaning an attribute can become a property, which is something that occurs from time to time, and you just need to update your constructor (self._set_multiple(a=a, b=b) becomes self._set_multiple(a=a, _b=b)) and you're done. And if at some point your community doesn't think "a" is a good name for your variable, you can update it to something every user will understand without any significant change to your class (self._set_multiple(a=way_better, _b=b)). -Brice

Le 26/04/17 à 13:12, Brice PARENT a écrit :
You still have to type twice the name of the variable instead of one though, if you don't want to use *args and **kwargs, but you still save the same vertical space with something efficient and way more useful. It is currently an invalid syntax, but that allows to be sure that it doesn't break old source code and doesn't require any deprecation. As a new syntax, it can probably be optimized a lot at the implementation level. It can be used with *args and **kwargs also, ... class MyClass: def __init__(self, *args, **kwargs): self.(a, b, c) = args self.(*kwargs.keys()) = kwargs.values() # or self.(kwargs.keys()) = kwargs.values() ... on instantiated objects (so not only in self), ... background_color.('r', 'g', 'b') = 255, 127, 0 ... update the values to be stored on-the-fly, ... class MyClass: def __init__(self, a, b, c): self.(a, b, c) = a, b * 2.54, c ... make any validation you want, ... class MyClass: def __init__(self, a, b, c): if c > 10: b = 0 elif c < 0: raise ValidationError("c can't be lower than 0") self.(a, b, c) = a, b, c ... update the names of the attributes and insert names that weren't in the signature ... class MyClass: def __init__(self, a, b, c): self.(a, b, _c, uuid) = a, b, c, str(uuid.uuid4()) Morality : This is a completely other thing to what's proposed here, even if it does solve more or less OP's initial problem. It's open to discussion whether it's useful or not, but it allows some kind of unpacking at the classes properties level, not exclusively into active scope. - Brice

On 25 April 2017 at 11:08, Erik <python@lucidity.plus.com> wrote:
Hi. I suspect that this may have been discussed to death at some point in the past, but I've done some searching and I didn't come up with much.
Hi Erik, Offering more concise ways of writing more powerful class definitions is certainly a common topic of exploration in Python development. A couple of folks have already mentioned Hynek Schlawack's `attrs` packages, which pairs up a class decorator with particular kinds of values set as class level attributes: from attr import attrs, attrib @attrs class MyClass: foo = attrib() bar = attrib() baz = attrib() ... Here, the `attrs` decorator not only writes __init__ for you, but also __repr__, comparison operators, pickle support, and a range of other things. Glyph Lefkowitz put together a good post last year describing just how much functionality attrs implicitly adds to a typical class definition, all while making it shorter and less repetitive: https://glyph.twistedmatrix.com/2016/08/attrs.html So I think if Python were to make any changes to the default toolset in this area, it would be more along the lines of how `attrs` works (i.e. offering a more declarative approach to the high level task of defining structured data containers), than it would be along the lines of a low level syntactic solution that only benefited `__init__` and `__new__` without addressing any of the other repetitive aspects of writing comprehensive class definitions. For example, if we decided we wanted to go down that path, we could add a new `autoclass` module and write the equivalent of the above as: from autoclass import make, field @make class MyClass: foo = field() bar = field() baz = field() ... That would provide us with a clear terminological distinction between autoclass fields and class attributes in general, while still letting people eliminate a lot of the boilerplate currently involved in writing classes that produce nice and easy to use instances. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

*cough* I'll just drop this here a sec *cough*: https://code.activestate.com/recipes/580790-auto-assign-self-attributes-in-_... On Thu, Apr 27, 2017 at 10:24 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
-- Ryan (ライアン) Yoko Shimomura > ryo (supercell/EGOIST) > Hiroyuki Sawano >> everyone else http://refi64.com/

On Tue, Apr 25, 2017 at 11:08 AM, Erik <python@lucidity.plus.com> wrote:
Bikeshedding: Your example looks a lot more like tuple assignment than multiple assignment. I'd rather it link more with the way that multiple assignment works: # simple multiple assignment a = b = c = d = e = 0 # object member assignment self .= foo .= bar .= baz .= spam .= ham The trouble is that this syntax is really only going to be used inside __init__. It's hard to justify new syntax for one purpose like this. So I'm swaying between -0 and +0.5 on this. ChrisA

On 25/04/17 02:15, Chris Angelico wrote:
Bikeshedding: Your example looks a lot more like tuple assignment than multiple assignment.
Well, originally, I thought it was just the spelling-the-same-name-twice thing that irritated me and I was just going to suggest a single assignment version like: self .= foo self .= bar Then I thought that this is similar to importing (referencing an object from one namespace in another under the same name). In that scenario, instead of: from other import foo from other import bar we have: from other import foo, bar That's where the comma-separated idea came from, and I understand it looks like a tuple (which is why I explicitly mentioned that) but it does in the import syntax too ;) The single argument version (though it doesn't help with vertical space) still reads better to me for the same reason that augmented assignment is clearer - there is no need to mentally parse that the same name is being used on both sides of the assignment because it's only spelled once.
self .= foo .= bar .= baz .= spam .= ham
Thanks for being the only person so far to understand that I don't necessarily want to bind ALL of the __init__ parameters to the object, just the ones I explicitly reference, but I'm not convinced by this suggestion. In chained assignment the thing on the RHS is bound to each name to the left of it and that is really not happening here.
The trouble is that this syntax is really only going to be used inside __init__.
Even if that was true, who ever writes one of those? :D E.

On Tue, Apr 25, 2017 at 02:08:05AM +0100, Erik wrote:
It does, and while both are annoying, in the grand scheme of things they're a very minor annoyance. After all, this is typically only an issue once per class, and not even every class, and vertical space is quite cheap. In general, the barrier for accepting new syntax is quite high, and "minor annoyance" generally doesn't reach it. I'm completely with you about the annoyance factor here. It is especially annoying during the early stages of development when the __init__ method is evolving rapidly (parameters are being renamed, added or removed). One work-around is this pattern: def __init__(self, spam, ham, eggs, cheese, aardvark): vars(self).update(locals()) del self.self ... which is cute but I've never quite been brave enough to use it in production. But I don't think the annoyance factor is high enough, or common enough, to justify syntactic sugar to "fix" it.
I'm not too worried about the performance impact unless I've profiled my code and it is actually significant. As far as the style asymmetry, if we use your proposed syntax, that's introducing style asymmetry too.
So why not have an augmented assignment operator for object attributes?
Wrong question. Don't ask "why not add this feature?". The question to ask is "why add this feature?" Every new feature has cost, whether it is run-time cost, development cost, feature bloat, learning curve cost, etc, so the feature must be justified as adding sufficiently more value than it costs. Here are a few reasons why this feature fails to meet the barrier for new syntax. - This feature doesn't add any new capability to the language. There's nothing that this feature allows you to do which you couldn't do before. - It has quite narrow applicability. It's really only useful inside a small proportion of __init__ methods. - In my experience, it is very rare to have a whole set of unadorned assignments in the way you show it. MUCH more common is to have some sort of pre-processing of the argument before assignment, or for the assignment target to be different from the parameter name: def __init__(self, spam, eggs, cheese, aardvark, ...): # type checks if not isinstance(spam, Foo): raise TypeError self._spam = spam # not all parameters are public attributes # value checks if eggs < 0: raise ValueError self.eggs = eggs # replacement of None if cheese is None: cheese = make_cheese() self.cheese = cheese # other pre-processing self.mammals = [aardvark, get_weasel()] which reduces the applicability of this syntax even further. - Having big parameter lists is something of a mild code smell. As the parameter list gets bigger, the smell gets worse. Having so many parameters that this feature becomes attractive should be painful, because its a warning that maybe your class has too many parameters.
But there's a major difference between this and the other augmented assignment operators, so major that the analogy between them doesn't hold up. In augmented assignment, the target can be any valid target, and the RHS can be any expression mylist[0].attr['key'] += (some_value() or another) + 99 Your suggestion is very different: there are *many* targets, and the RHS must be a comma-separated list of identifiers. They're quite different kinds of assignment. And unlike augmented assignment, which calls one of the __iop__ methods, this would have to be handled purely as syntax.
Right. And that *seriously* limits the applicability of this. Yes, I've written classes with a dozen or twenty parameters. But especially with long parameter lists, invariably at least *some* of those arguments are pre-processed before assignment to an attribute: self.spam = spam + 1 or are assigned to a different name: self._spam = spam or both: self.foods = [spam, eggs, cheese] So my expectation is that even if this syntax was accepted, I wouldn't use it, because by the time I've processed those assignments, the ones that are left are too few to bother with the special syntax. The bottom line is that I think you've identified a very real, but minor, annoyance, but the applicability of the solution is too low to justify new syntax. -- Steve

On 25 April 2017 at 03:53, Steven D'Aprano <steve@pearwood.info> wrote:
I suspect that with a suitably creative use of inspect.signature() you could write a decorator for this: @auto_attrs def __init__(self, a, b, c): # Remaining init code, called with self.a, self.b and self.c set I don't have time to experiment right now, but will try to find time later. If nothing else, such a decorator would be a good prototype for the proposed functionality, and may well be sufficient for the likely use cases without needing a syntax change. Paul

I actually saw a decorator like that last week, https://twitter.com/judy2k/status/854330478068977664 On 25 April 2017 at 08:56, Paul Moore <p.f.moore@gmail.com> wrote:
-- Daniel F. Moisset - UK Country Manager www.machinalis.com Skype: @dmoisset

2017. ápr. 25. de. 10:04 ezt írta ("Paul Moore" <p.f.moore@gmail.com>): On 25 April 2017 at 03:53, Steven D'Aprano <steve@pearwood.info> wrote:
I suspect that with a suitably creative use of inspect.signature() you could write a decorator for this: @auto_attrs def __init__(self, a, b, c): # Remaining init code, called with self.a, self.b and self.c set I don't have time to experiment right now, but will try to find time later. If nothing else, such a decorator would be a good prototype for the proposed functionality, and may well be sufficient for the likely use cases without needing a syntax change. Paul _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/ Hi, such a decorator would be very grateful ;-) in the standard as for example I use class parameters this way too in more than 90% of the cases. BR George

Agreed with Steven, although I do find myself a little more annoyed and bothered by a typical init than him I guess. Even so I didn't think the current proposals went far enough. To tilt the balance farther, to make it easier, let's go all the way! Instead of continuing duplication:
def __init__(self, foo, bar, baz, spam, ham): self .= foo, bar, baz, spam, ham
or
# object member assignment self .= foo .= bar .= baz .= spam .= ham
How about? def __init__(self, foo, bar, baz, spam, ham): self .= * The asterisk here used to be reminiscent of argument unpacking (minus self). That would imply a double asterisk for keyword assignment which could be used as well. Also, I did find the decorator proposal intriguing, though have to say I probably wouldn't bother to use it unless it were a builtin or I had a dozen parameters to deal with. -Mike

Le 25/04/17 à 20:16, Mike Miller a écrit :
If you *need* a shorter solution, even though I'm not entirely sure there's a real need for it, it may be easier to get something like this (I think, as there is no new operator involved) : def __init__(self, *args, **kwargs): self.* = *args self.** = **kwargs And I'm sure this could easily find new use cases outside of a constructor. But, any of these proposals, mine and yours, if you really need this to shorten the code writting time or vertical space only, is not a better idea than to propose a macro to your IDE editor, or a pull request if it's open source. Such a macro would generate automatically those long-to-write lines, and maybe implement some code folding if vertical space is an issue. - Brice

On 25 April 2017 at 22:27, Mike Miller <python-ideas@mgmiller.net> wrote:
Well, class MyClass: @auto_args def __init__(self, a, b, c=None): pass seems ideal for you then (using the auto_args decorator from https://twitter.com/judy2k/status/854330478068977664 - a 13-line function including the import of inspect and some comments). (Arguing for auto_args to be in the stdlib may be a better option than arguing for new syntax, BTW...) Paul

On Tue, Apr 25, 2017 at 6:09 PM, Paul Moore <p.f.moore@gmail.com> wrote:
(Arguing for auto_args to be in the stdlib may be a better option than arguing for new syntax, BTW...)
Having such a decorator in the stdlib would allow IDEs and syntax highlighters to know what's going on. -- Juancarlo *Añez*

On 25 April 2017 at 22:15, Brice PARENT <contact@brice.xyz> wrote:
It seems to me that the number of people for whom both of the following hold: 1. Writing out the assignments "longhand" is an unacceptable burden. 2. Using a decorator (which can be written directly in your project, doesn't even need to be an external dependency) is unacceptable. is likely to be small enough that it's not a compelling argument for adding new syntax. Add to that the fact that these people would be arguing "I want the ability to avoid writing out the assignments, but I don't want that capability enough to use a decorator" - which hardly says that a syntax change is vital - and it's difficult to see this proposal getting accepted. Paul

On 25/04/17 23:05, Paul Moore wrote:
1. Writing out the assignments "longhand" is an unacceptable burden.
There are reasons why augmented assignment was implemented. One of them was to make the code easier to read: foil = foil + 1 foil = foi1 + 1 foil += 1 Should one be silly enough to have a "foil" and "foi1" variable in scope, only one of those is clearly incrementing a variable without requiring a slightly harder look ;) It's not about the time taken to type the line. It's about the clarity of what the line is expressing.
2. Using a decorator (which can be written directly in your project, doesn't even need to be an external dependency) is unacceptable.
All of the decorators (or other language tricks that modify the object's dict) suggested so far assume that ALL of the method's arguments are to be assigned. I do not want that. I want to be able to say: def __init__(self, foo, bar, baz, spam): self .= foo, bar, spam self.baz = baz * 100 It's all still explicit inside the body of the method.
As I said above, it's not about the effort writing it out. It's about the effort (and accuracy) of reading the code after it has been written. And as I also said above, decorators don't cut it anyway (at least not those proposed) because they blindly assign ALL of the arguments. I'm more than happy to hear of something that solves both of those problems without needing syntax changes though, as that means I can have it today ;) E.

On Tue, Apr 25, 2017 at 3:30 PM, Erik <python@lucidity.plus.com> wrote:
But if this were the only argument for +=, then I'm not sure it would have ever been added :-). The really compelling cases for += are things like: foo.some_attr[get_the_key(kwarg=something)] = foo.some_attr[get_the_key(kwarg=something)] + 1 vs foo.some_attr[get_the_key(kwarg=something)] += 1 where the latter is *much* more readable, and it only calls get_the_key once, which (depending on what it does) may be both a substantial efficiency win and also guarantees this is actually an increment – we don't have to read get_the_key's source code first to figure out if the two calls actually return the same value. Another example: arr = arr + 1 arr += 1 If 'arr' is a numpy array then these actually do very different things: the first one allocates a new array and then rebinds the name 'arr' to the copy; the second modifies the array object in place. The former is usually what you want, but without augmented assignment you have to remember to do awkward things like 'arr[...] = arr + 1'. And it gets worse: since 'arr[...] = arr + 1' has to make a copy, it's about twice as slow as 'arr += 1'. The real equivalent of 'arr += 1' is 'np.add(arr, 1, out=arr)', which is *really* bad. Are there any similar arguments for .=? -n -- Nathaniel J. Smith -- https://vorpus.org

On 26/04/17 01:39, Nathaniel Smith wrote: [snip discussion of why current augmented assignment operators are better for other reasons]
Are there any similar arguments for .=?
It doesn't make anything more efficient, however all of the suggestions of how to do it with current syntax (mostly decorators) _do_ make things less efficient. So rather than a win/win as with current augmented assignment (compact/clearer code *and* potentially a performance improvement), it's now a tradeoff (wordy code *or* a performance reduction). E.

On 26 April 2017 at 21:51, Erik <python@lucidity.plus.com> wrote:
Is instance creation the performance bottleneck in your application? That seems unusual. I guess it's possible if you're reading a large database file and creating objects for each row. But in that case, as Chris A says, you may be better with something like a named tuple. In any case, optimising for performance is not what generic solutions are good at, so it's not surprising that a generic decorator involves a performance hit. Paul

On 26/04/17 22:28, Paul Moore wrote:
No, not at all. This discussion has split into two: 1) How can I personally achieve what I want for my own personal use-cases. This should really be on -list, and some variation of the decorator thing will probably suffice for me. 2) The original proposal, which does belong on -ideas and has to take into account the general case, not just my specific use-case. The post you are responding to is part of (2), and hence reduced performance is a consideration. Regards, E.

On 26 April 2017 at 22:42, Erik <python@lucidity.plus.com> wrote:
Ah, OK. I'm discounting the original proposal, as there don't seem to be sufficient (i.e., any :-)) actual use cases that aren't covered by the decorator proposal(s). Or to put it another way, if the only reason for the syntax proposal is performance then show me a case where performance is so critical that it warrants a language change. Sorry for not being clear myself. Paul

On 26/04/17 23:28, Paul Moore wrote:
It's the other way around. The proposal (arguably) makes the code clearer but does not impact performance (and is a syntax error today, so does not break existing code). The suggestions (decorators etc) make the code (arguably) clearer today without a syntax change, but impact performance. So, those who think the decorators make for clearer code have to choose between source code clarity and performance. E.

On 25 April 2017 at 23:30, Erik <python@lucidity.plus.com> wrote:
As I said above, it's not about the effort writing it out. It's about the effort (and accuracy) of reading the code after it has been written.
Well, personally I find all of the syntax proposals relatively unreadable. So that's definitely a matter of opinion. And the "explicit is better than implicit" principle argues for the longhand form. As has been pointed out, the case for += is more about incremented complex computed cases than simply avoiding repeating a variable name (although some people find that simpler case helpful, too - I'm personally ambivalent).
That's something that wasn't clear from your original post, but you're correct. It should be possible to modify the decorator to take a list of the variable names you want to assign, but I suspect you won't like that - it does reduce the number of times you have to name the variables from 3 to 2, the same as your proposal, though. class MyClass: @auto_args('a', 'b') def __init__(self, a, b, c=None): pass Paul

On 26/04/17 08:59, Paul Moore wrote:
Now you're second-guessing me.
I had forgotten that decorators could take parameters. Something like that pretty much ticks the boxes for me. I'd _prefer_ something that sits inside the method body rather than just outside it, and I'd probably _prefer_ something that wasn't quite so heavyweight at runtime (which may be an irrational concern on my part ;)), but those aren't deal breakers, depending on the project - and the vast majority of what I do in Python is short-lived one-off projects and rapid prototyping for later implementation in another language, so I do seem to be fleshing out a set of classes from scratch and writing a bunch of __init__ methods far more of the time than people with long-lived projects would do. Perhaps that's why it irritates me more than it does some others ;) E.

On Wed, Apr 26, 2017 at 11:17 AM, Erik <python@lucidity.plus.com> wrote:
I had forgotten that decorators could take parameters. Something like that pretty much ticks the boxes for me.
There are decorators with "include" and "included" in this SO Q&A: http://stackoverflow.com/q/3652851/545637
The same strategies applied by the decorators may be applied by a by a function called from within __init__.
For the cases I've found in which classes define several attributes that are initialized in the constructor I think that a library like https://github.com/python-attrs/attrs does what's needed. The downside is that code-writing tools (like IDEs) don't understand what's going on under the hood. -- Juancarlo *Añez*

On 25 April 2017 at 19:30, Erik <python@lucidity.plus.com> wrote:
Sorry - a decorator won't "blindly assign all argments" - it will do that just if it is written to do so. It is perfectly feasible to have a decorator to which you can simply pass a list of not wanted auto-parameters, or that will just auto-assign parameters that have been declared on the class body, or even inspect the function signature and check for special annotations - there are tens of ways we could spec a list of exceptions to such a decorator, and still avouid typing three times each name. That said, I am all in favor of the addition of such a decorator to the stdlib. js -><-

On 26/04/17 13:19, Joao S. O. Bueno wrote:
Right, and the three or four variants suggested (and the vars(self).update() suggestion) all do exactly that. I was talking about the specific responses (though I can see my language is vague). [FWIW I've been using Python the whole time that decorators have existed and I've yet to need to write one - I've _used_ some non-parameterized ones though - so I guess I'd forgotten that they can take parameters] E.

I was wondering that if there are so many arguments to a function that it *looks* ugly, that it might just *be* ugly. For one, too many required arguments to a function (constructor, whatever) is already strange. Binding them as attributes of the object, unmodified in a constructor also seems to be rare. Matplotlib, for example, might take 8 (optional keyword) arguments for a Figure ( https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/figure.p...), but then at a bare minimum, the vertical space required is *doubled* by the need for pesky if-blocks and logic. Self-assignment is a pittance. Are there any better examples of where adding new syntax could help readability for programmers, both new and old? On Wed, Apr 26, 2017 at 9:59 AM, Erik <python@lucidity.plus.com> wrote:

On 26/04/17 16:10, Nick Timkovich wrote:
Yes, and perhaps it's more of a problem for me because of my possibly-atypical use of Python. The background is that what I find myself doing a lot of for private projects is importing data from databases into a structured collection of objects and then grouping and analyzing the data in different ways before graphing the results. So yes, I tend to have classes that accept their entire object state as parameters to the __init__ method (from the database values) and then any other methods in the class are generally to do with the subsequent analysis (including dunder methods for iteration, rendering and comparison etc). E.

On 26/04/2017 21:50, Chris Angelico wrote:
Something like https://docs.python.org/3/library/sqlite3.html#row-objects ? -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence

On 26.04.2017 23:50, Mark Lawrence via Python-ideas wrote:
Or something like https://docs.djangoproject.com/en/1.11/topics/db/models/ Sven

On 26/04/17 21:50, Chris Angelico wrote:
I did look at this. It looked promising. What I found was that I spent a lot of time working out how to subclass namedtuples properly (I do need to do that to add the extra logic - and sometimes some state - for my analysis) and once I got that working, I was left with a whole different set of boilerplate and special cases and therefore another set of things to remember if I return to this code at some point. So I've reverted to regular classes and multiple assignments in __init__. E.

On 2017-04-25 15:30, Erik wrote:
I don't see ALL being set a big problem, and less work than typing several of them out again. Just because all args get set at once, doesn't mean the init stops there and attributes can't be further modified or deleted. In your example above, you modify baz afterward as an argument that might need to be changed, as they often do. If this doesn't give enough control, the manual way is always available. Setting them all makes simple things *very* simple, and the medium and complicated still possible.

On 26/04/17 18:42, Mike Miller wrote:
Because, some of the parameters might be things that are just passed to another constructor to create an object that is then referenced by the object being created. If one doesn't want the object's namespace to be polluted by that stuff (which may be large and also now can't be garbage collected while the object is alive) then a set of "del self.xxx" statements is required instead, so you've just replaced one problem with another ;) I'd rather just explicitly say what I want to happen rather than have *everything* happen and then have to tidy that up instead ... E.

On 2017-04-25 15:05, Paul Moore wrote:
While the word unacceptable is probably a bit too strong, this is a good insight. We have a "middling" difficulty here. Not the end of the world, but an annoyance, as the existence of the attrs library demonstrates. Kivy's properties solve a similar problem (though a bit different scope). I've overheard Java snobs sneer at Python's init so it is a well known issue. If there is going to be a solution, I submit it needs to be much easier to use and to read. Making it a little easier is not good enough, not enough reason to change. (Believe you mentioned a 3 to 2 reduction elsewhere in the thread.) Meh. If I have install a module in pip and go back to the top of the file and write an import line and then come back and write out a decorator it's not going to be worth it. Currently, I use editor snippets to ease authoring of init, but readability and DRY are still concerns afterward. As the new syntax ideas piggyback on existing syntax, it doesn't feel like that its a complete impossibility to have this solved. Could be another "fixed papercut" to drive Py3 adoption. Taken individually not a big deal but they add up.

On 26/04/17 19:15, Mike Miller wrote:
*sigh* OK, this has occurred to me over the last couple of days but I didn't want to suggest it as I didn't want the discussion to fragment even more. But, if we're going to bikeshed and there is some weight behind the idea that this "papercut" should be addressed, then given my previous comparisons with importing, what about having 'import' as an operator: def __init__(self, a, b, c): self import a, b self.foo = c * 100 Also allows renaming: def __init__(self, a, b, c): self import a, b, c as _c Because people are conditioned to think the comma-separated values after "import" are not tuples, perhaps the use of import as an operator rides on that wave ... (I do realise that blurring the lines between statements and operators like this is probably not going to work for technical reasons (and it just doesn't quite read correctly anyway), but now we're bikeshedding and who knows what someone else might come up with in response ...). E.

On Wed, Apr 26, 2017 at 11:29:19PM +0100, Erik wrote:
[snarky] If we're going to randomly choose arbitrary keywords with no connection to the operation being performed, can we use `del` instead of `import` because it's three characters less typing? [/snarky] *wink* But seriously, I hate this idea. `import` has certain semantics that don't apply here: - import goes through the full import system (sys.modules, reading files from disk, sys.path, etc.); this "self import ..." does not; - import creates local variables; this "self import ..." does not. The semantics are very different and there's little or no connection between importing a module and setting an attribute on self. If we're going to discuss pie-in-the-sky suggestions, here is an only semi-serious suggestion. A statement that injects identifiers into an explicitly given name space: inject spam, eggs as cackleberry, cheese into self (If you don't like "inject", I'm okay with "load" or even "push".) This would be equivalent to: self.spam = spam self.cackleberry = eggs self.cheese = cheese Presumably it would be implemented a little more efficiently, e.g. only a single lookup for self, etc. Note that the target is completely general: you can inject into any object which will accept setting attributes: inject spam, eggs into namespace If the "inject as" form is used, the value can be any expression: inject x + 1 as y into namespace otherwise the value must be a valid identifier but not necessarily a local variable. If I were designing a language from scratch, one with a maximalist approach to syntax, I'd take this seriously. But Python has relatively minimal syntax and keywords, and the problem this solves isn't big or important enough for the disruption of adding a new keyword. -- Steve

On 27/04/17 23:43, Steven D'Aprano wrote:
The keyword I chose was not random or arbitrary and it _does_ have a connection to the operation being performed (bind a value in the source namespace to the target namespace using the same name it had in the source namespace - or rename it using the 'as' keyword).
can we use `del` instead of `import` because it's three characters less typing?
Comments like this just serve to dismiss or trivialize the discussion. We acknowledged that we're bikeshedding so it was not a serious suggestion, just a "synapse prodder" ...
But seriously, I hate this idea.
Good. It's not a proposal, but something that was supposed to generate constructive discussion.
The semantics are very different and there's little or no connection between importing a module and setting an attribute on self.
At the technical level of what goes on under the covers, yes. At the higher level of what the words mean in spoken English, it's really not so different a concept.
If we're going to discuss pie-in-the-sky suggestions,
That is just dismissing/trivializing the conversation again.
(If you don't like "inject", I'm okay with "load" or even "push".)
No you're not, because that's a new keyword which might break existing code and that is even harder to justify than re-using an existing keyword in a different context.
the problem this solves isn't big or important enough for the disruption of adding a new keyword.
So far, you are the only one to have suggested adding a new keyword, I think ;) E.

On 28 April 2017 at 00:18, Erik <python@lucidity.plus.com> wrote:
I disagree. If you were importing into the *class* (instance?) I might begin to see a connection, but importing into self? Also, if you try to make the obvious generalisations (which you'd *have* to be able to make due to the way Python works) things quickly get out of hand: def __init__(self, a): self import a OK, but self is just a variable name, so we can reasonably use a different name: def __init__(foo, a): foo import a So the syntax is <variable> import <var-list> Presumably the following also works, because there's nothing special about parameters? def __init__(x, a): calc = a**2 x import calc And of course there's nothing special about __init__ def my_method(self, a): self import a Or indeed about methods def standalone(a, b): a import b or statements inside functions: if __name __ == '__main__: a = 12 b = 13 a import b Hmm, I'd hope for a type error here. But what types would be allowed for a? Class instances? Well, a is an int, which is a class instance. See what I mean? Things get out of hand *very* fast.
Well, picking an existing keyword simply[1] to avoid using a new one is an equally bad justification. [1] I know that's not what you did, but without the "it's sort of like importing" justification, which Steven has said he doesn't agree with, and nor do I, you're only left with "because it means we don't need a new keyword" - at which point suggesting that we need to bite the bullet and consider whether a new keyword is justified is perfectly reasonable. (IMO, a new keyword is *not* justified - nor is reusing an existing one unless you can find a *much* more compelling parallel than you did with import). To summarise: 1. There's some serious technical issues with your proposal, which as far as I can see can only be solved by arbitrary restrictions on how it can be used 2. The use of import as a keyword is at best controversial 3. Using a new keyword for this construct is pretty much a non-starter But I do agree that the proposal was a reasonable way to reframe the debate here. It's just that what it's done is confirm my belief that there's nothing here that needs improvement compared to the status quo (even though I agree that long lists of "self.x = x" statements can be tedious boilerplate). Paul

On 28/04/17 10:47, Paul Moore wrote:
I know you already understand the following, but I'll spell it out anyway. Here's a module: ----------------- $ cat foo.py def foo(): global sys import sys current_namespace = set(globals().keys()) print(initial_namespace ^ current_namespace) def bar(): before_import = set(locals().keys()) import os after_import = set(locals().keys()) print(before_import ^ after_import) initial_namespace = set(globals().keys()) ----------------- Now, what happens when I run those functions: $ python3 Python 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information.
... so the net effect of "import" is to bind an object into a namespace (a dict somewhere). In the case of 'foo()' it's binding the module object for "sys" into the dict of the module object that represents 'foo.py'. In the case of 'bar()' it's binding the module object for "os" into the dict representing the local namespace of the current instance of the bar() call. Isn't binding an object to a namespace the same operation that assignment performs? So it's a type of assignment, and one that doesn't require the name to be spelled twice in the current syntax (and that's partly why I took offense at a suggestion - not by you - that I was picking "random or arbitrary" keywords. I picked it for that specific reason). I realize that there are other semantic changes (importing a module twice doesn't do anything - and specifically repeated "import * from mod" will not do anything if the module mutates) - and perhaps this is your point.
self.a = a
foo.a = a
x.calc = calc
self.a = a
a.b = b
a.b = b
Hmm, I'd hope for a type error here. But what types would be allowed for a?
I think you're assuming I'm suggesting some sort of magic around "self" or some such thing. I'm not. I've written above exactly what I would expect the examples to be equivalent to. It's just an assignment which doesn't repeat the name (and in the comma-separated version allows several names to be assigned using compact syntax without spelling them twice, which is where this whole thing spawned from).
See what I mean? Things get out of hand *very* fast.
I don't see how that's getting "out of hand". The proposal is nothing more complicated than a slightly-different spelling of assignment. It could be done today with a text-based preprocessor which converts the proposed form to an existing valid syntax. Therefore, if it's "out of hand" then so is the existing assignment syntax ;) FWIW, I should probably state for the record that I'm not actually pushing for _anything_ right now. I'm replying to questions asked and also to statements made which I think have missed the point of what I was trying to say earlier. So I'm just engaging in the conversation at this point - if it appears confrontational then it's not meant to.
To be honest, I still don't understand what the serious technical issues are (other than the parser probably doesn't handle this sort of keyword/operator hybrid!). Is it just that I'm seeing the word "import" in this context as a type of assignment and you're seeing any reference to the word "import" as being a completely different type of operation that comes with baggage? Regards, E.

typing.NamedTuple was already mentioned in this discussion, I just would like to add few comments: 1. I think that changing Python syntax to support declarative classes is not a realistic option in nearby future. In contrast, there is an existing syntax change - variable annotations - that can be actively experimented with. Variable annotations are designed to play well with metaclass machinery, for example, they will respect __prepare__ method, if it injects __annotations__ as a custom mapping, etc. 2. Possible strategy for declarative classes could be developing independent *composable* mixins/traits: @comparable @pickleable class MyClass: x: int y: int or class MyClass(Comparable, Pickleable): x: int y: int The problem with the second approach is that it can lead to metaclass conflicts. A possible solution will be to provide a standard metaclass for mixins in stdlib. 3. Composability could be a problem if only normal variable assignments are used: after one mixin transforms the class, the original declarations are lost. Simple possible solution would be to agree that mixins should *not* modify __annotations__, only __dict__. Currently, PEP 484 and PEP 526 specify that preferable use of annotations is type declarations. However, I think this can be generalized to just declarations. For example, by providing a special typing construct that will allow additional info apart from type: def large(val): return val > 42 @comparable @pickleable class MyClass x: Spec(int, validator=large) y: Spec(List[int], default_factory=list) -- Ivan

On 29 April 2017 at 09:51, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
Between __init_subclass__, __set_name__, __annotations__, and guaranteed order preservation for class namespaces, Python 3.6 has provided some pretty powerful tools for folks to experiment with new approaches to declarative class definitions. One point worth considering is that given standard library implementations of the explicit class decorator forms (e.g in the `types` module, or in the modules defining the relevant protocols), it should be relatively straightforward to create mixin classes that use __init_subclass__ to implicitly apply the explicit decorators. The same doesn't hold the other way around - given a mixin, it's tricky to convert that to a class decorator form. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
participants (25)
-
Brendan Barnwell
-
Brice PARENT
-
Carlo Miron
-
Chris Angelico
-
Daniel Moisset
-
Erik
-
George Fischhof
-
Ivan Levkivskyi
-
Jerry Hill
-
Joao S. O. Bueno
-
Juancarlo Añez
-
Mark Lawrence
-
Matt Gilson
-
Mike Miller
-
MRAB
-
Nathaniel Smith
-
Nick Coghlan
-
Nick Timkovich
-
Oleg Broytman
-
Paul Moore
-
Random832
-
Ryan Gonzalez
-
Steven D'Aprano
-
Sven R. Kunze
-
tritium-list@sdamon.com