datetime.timedelta literals

Elevator pitch: (2.5h - 14min + 9300ms).total_seconds() # 8169.3 from datetime import datetime as dt start = dt.now() end = dt.now() (end-start) < 5s # True chrono::duration: In C++ 14 the std::chrono::duration was introduced which corresponds somewhat to datetime.timedelta. C++ 14 introduced so-called chrono literals[1], which are literals specified as [number][h|min|s|ms|us|ns], e.g. * 2.5h * 14min * 9300ms These literals should correspond to * datetime.timedelta(0, 9000) # 2.5h = 2.5*3600 = 9000 seconds * datetime.timedelta(0, 840) # 14min = 14*60 = 840 seconds * datetime.timedelta(0, 9, 300000) # 9300ms = 9 seconds + 3*10^5 microseconds If a literal was interpreted as a datetime.timedelta, the following would work out of the box: 2.5h - 14min + 9300ms * 2 which would correspond to from datetime import timedelta as D D(hours=2.5) - D(minutes=14) + D(milliseconds=9300) * 2 # datetime.timedelta(0, 8178, 600000) # (*2 precedes, so that's to be expected) (D(hours=2.5) - D(minutes=14) + D(milliseconds=9300)) * 2 # datetime.timedelta(0, 16338, 600000) Notes: * C++ uses `min` instead of `m`. `min` is a keyword in Python. * In C++, `1d` means the first day of a month [2]. * In C++, `1990y` means the year 1990 (in the Proleptic Gregorian calendar) [3]. * C++ have the types signed integers and not floats, so 2.5h would not be valid. * My apologies if this has been discussed before; my search-fu gave me nothing. References: [1] std::literals::chrono_literals::operator""min http://en.cppreference.com/w/cpp/chrono/operator%22%22min [2] http://en.cppreference.com/w/cpp/chrono/day [3] http://en.cppreference.com/w/cpp/chrono/year Best regards, Pål Grønås Drange

What about 2.5*h - 14*min + 9300*ms * 2 where: h = timedelta(hours=1) min = timedelta (minutes=1) ms = timedelta (milliseconds=1) By the way "min" isn't a keyword, it's a standard function so it can be used as a variable name. However why be limited to time units ? One would want in certain application to define other units, like meter ? Would we want a litteral for that ? For units like those, the "*" operator works well, and if one want more support (like, mix minutes and seconds) one could write simple classes or use libraries, for example to avoid to add time and distance units. Le sam. 2 juin 2018 à 14:21, Pål Grønås Drange <paal.drange@gmail.com> a écrit :

What about
2.5*h - 14*min + 9300*ms * 2
That doesn't seem feasible to implement, however, that is essentially how the Pint [1] module works: import pint u = pint.UnitRegistry() (2.5*u.hour - 14*u.min + 9300*u.ms) * 2 # <Quantity(4.5385, 'hour')> ((2.5*u.hour - 14*u.min + 9300*u.ms) * 2).to('sec') # <Quantity(16338.6, 'second')>
Pint works with all units imaginable: Q = u.Quantity Q(u.c, (u.m/u.s)).to('km / hour') # <Quantity(3.6 speed_of_light, 'kilometer / hour')> However, the idea was just the six (h|min|s|ms|us|ns) time literals; I believe time units are used more often than other units, e.g. in constructs like while end - start < 1min: poll() sleep(1s) # TypeError sleep(1s.total_seconds()) # works, but ugly [1] https://pypi.org/project/Pint/ Best regards, Pål Grønås Drange

Please read the discussions on *https://mail.python.org/pipermail//python-ideas/2016-August/041890.html <https://mail.python.org/pipermail//python-ideas/2016-August/041890.html>*, https://mail.python.org/pipermail//python-ideas/2016-August/041939.html, https://mail.python.org/pipermail//python-ideas/2016-August/042028.html, https://mail.python.org/pipermail//python-ideas/2016-August/041899.html They are more general than your proposal but they still cover pitfalls that may affect yours. It would be better if you could expand your proposal to the concerns raised on those threads. On Sun, Jun 3, 2018 at 12:53 PM, Pål Grønås Drange <paal.drange@gmail.com> wrote:
-- Sebastian Kreft

After reading that thread (this summary should be taken with a grain of salt), the first half is concerned with dimensional analysis not (satisfactorily) performed. The second half of the discussion is dedicated to the major issue with 1E1 meaning 1 and not 1 exa, and it's also pointed out that it is somewhat confusing that 1m means 0.001 and not 1 meter. There is an alternative proposal from that thread, namely that we write 2_m, 2_k etc to mean milli and kilo, respectively. That could also be used in my case. However, the suggested idea with _all the SI units_ is a completely new construct over any programming language in existence; and quite frankly a much bigger change than mine. My suggestion, which is not mine at all, really, has an implementation in C++, with experiences we can learn from. The "timedelta literals" idea has _six_ new literals, whereas Ken Kundert's idea has I don't know how many. I want to highlight one comment I found enlightening, and that is one from Paul Moore: [Python-ideas] SI scale factors in Python Paul Moore p.f.moore at gmail.com Thu Aug 25 16:03:32 EDT 2016
I can understand that a lack of support from people using timedelta will be a blocker. Now, please don't take this as a dismissal of your suggestion that I can learn from the referenced discussion; I did learn a great deal, but I also felt that much of the discussion was around subjects that are less relevant for the "timedelta literals" suggestion. Best regards, Pål Grønås Drange

On 3 June 2018 at 22:03, Pål Grønås Drange <paal.drange@gmail.com> wrote:
I'm not entirely sure what point you're trying to make here, but you quoted that section somewhat out of context. The very next sentence in the same post (full post is at https://mail.python.org/pipermail//python-ideas/2016-August/041878.html) was """ At the moment, I'm not even aware of a particular "dimensional analysis with Python" community, or any particular "best of breed" package in this area that might lead such a proposal - and a language change of this nature probably does need that sort of backing. """ That seems directly relevant here. I'm not aware of a "timedelta users" community, nor is there a particular package (or set of packages) other than the stdlib datetime module, that constitute "best of breed" practice when handling timedeltas. So taking my full quote, I'd have to say that you seem to have undermined your own proposal here. For what it's worth, I use the datetime module and timedeltas regularly, and I would have no use for timedelta literals. Even if the proposal were for a complete set of datetime, date, time and timedelta literals,I still wouldn't have a use for it. Whether that's useful data I don't know, but you seem to be looking for "support from people using timedelta", so I thought I'd clearly state that I, personally, don't support the proposal. Paul

Yes, that was why I quoted it; I thought I should bring the relevant parts of the discussion into this, even if they were against this proposal.
For what it's worth, I use the datetime module and timedeltas regularly, and I [...] don't support the proposal.
That's good to hear, but if you don't mind asking, is your lack of support because you use timedelta "programatically" instead of hard-coded time units, or is there a different (or more) reason(s)? (I'm ready to yield, now I'm just curious.) Best regards, Pål Grønås Drange

On 4 June 2018 at 07:44, Pål Grønås Drange <paal.drange@gmail.com> wrote:
I don't know what you mean by "programatically instead of hard-coded time units". In my code, I've never felt the need to be able to write something like "5min" rather than "timedelta(minutes=5)". Far from it - I find the former jarring, whereas the latter is perfectly clear, so even if the literal form were available I probably wouldn't use it much. I don't see what problem they solve, I've *never* heard anyone claim that using the constructor to create a timedelta object was a problem - even in your proposal you don't explicitly claim that (although I presume you do think that, otherwise why bother making the proposal?). The point of my original comment that you quoted was more to say that *if* a specialist community with expertise in a particular area exists, and they claim that there is benefit to a notation, then their expert knowledge can override the more general view (which is typically pretty conservative, for good reasons). In this case, there's no such expert community, so there's no reason not to go with the default position (which in this case could be summarised as "if none of decimals, scale suffixes or physical units managed to make a case for dedicated literal syntax, timedelta doesn't stand a chance"). Paul

On Mon, Jun 4, 2018 at 12:58 AM, Paul Moore <p.f.moore@gmail.com> wrote:
I think he means essentially: Do you use timedelta with literal arguments? -- as opposed to having it be the result of a calculation or read from a file or ...
I'm the opposite - I use timedelta a fair bit, and find writing: timedelta(hours=24) Pretty awkward. To the point that I make part of my "scripting" API take integer seconds (or floating point hours, or...) rather than a timedelta objects, to save my users from having to do: from datetime import timedelta call_a_function(... timestep = timedelta(seconds=10) ... ) Rather than: call_a_function(... timestep = 10 ... ) The latter of which requires more typing, an extra import, and, most importantly, a knowledge of datetime and the timedelta API. So yeah -- I have a need for it. All that being said, there are an number of things one might want a literal for, and adding a huge pile of them is a bad idea, so I'm -1 on this proposal anyway. It does make me think that I may want to add my own utilities to make this easier: def seconds(s) return timedelta(seconds=s) etc. Then I could add them to my "scripting" library, and my users could do: call_a_function(... timestep = seconds(10) ... ) Not so bad. In fact, maybe it would be a good idea to add such utilities to the datetime module... ANOTHER NOTE: The interface to timedelta is hard to discover because the docstring is incomplete: In [2]: timedelta? Init signature: timedelta(self, /, *args, **kwargs) Docstring: Difference between two datetime values. File: ~/miniconda2/envs/py3/lib/python3.6/datetime.py Type: type Ouch! no idea what keyword argument it takes! [but that's an argument for better docstrings, not adding literals...] -CHB -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

For the general user-defined literals, here are some example use cases: Here is how to expose literals: __literals__ = ['__literal_h__', '__literal_min__', '__literal_s__'] And here is how to import them: from mymodule import _* # imports all literals from mymodule # could also be *_ or ** or ~ or __literals__ ? 90_deg # 1.0 in rad 20_°C # 293.15 Kelvin units.to(20.0_mi, 'km') e = 1_X + 4_G + 13_M + 180_k # SI scale factors can easily be implemented p = 3_x - 2_y + 1_z # Coordinate in 3D M = 4_I # The 4x4 Identity Matrix p * 2_pi # because it's simpler than 2 * np.pi p * 11_π # why not d = 2_sqrt # sqrt(2), or simply 2**(1/2), not a killer argument h1 = 30_px h2 = 4_em # sizes for typography c = 'ffff00'_color # the color yellow h = 'My header'_h3 # renders <h3>My header</h3> or something g = 9.81_m_div_ss # that was ugly ... 'More weird examples'_info # log.info(msg) '2018-06-04'_AD # is a date '192.168.0.42'_ip4 # why not? 'USER'_os # = os.environ.get(x, '') # Can have state? initialize(80) # set default string width to 80 chars 'my string'_c # will now be centralized in 80 chars 'my string'_l # will now be left aligned in 80 chars 'my string'_r # will now be right aligned in 80 chars If we used, e.g. tilde, we could even use it on function calls y = fun(x)~dx # differentiations! Like decorators, but on "call time" I accept that introducing a new symbol has a very high threshold, and will not go through. I just wanted to mention it. Yes, we could write all these as easily as function calls, deg(90) celsius(20) center('my string') # or 'my string'.center(80) But somehow it seems nicer to write 42_km than 12 * pint.UnitRegistry().km - Pål

On Mon, Jun 4, 2018 at 1:59 PM, Pål Grønås Drange <paal.drange@gmail.com> wrote:
For the general user-defined literals, here are some example use cases:
I kind of like the idea of user-defined literals, but:
how about? from pint import km 42*km still not as nice as 42_km, though only by a bit.... So maybe you could propose adding: seconds minutes hours days to the datetime module, and then we could write: 60*seconds == 1*minutes Without any changes to the language at all. -CHB
-- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

On 4 June 2018 at 22:50, Chris Barker via Python-ideas <python-ideas@python.org> wrote:
This strikes me as more of a personal/team style issue, so probably fits better as local definitions in a project's "utilities" module. Putting it in the stdlib implies a certain level of approval of this as a standard idiom, and I'm not sure it's common enough practice to warrant that. Paul

On Mon, Jun 4, 2018 at 3:21 PM, Paul Moore <p.f.moore@gmail.com> wrote:
probably, yes -- that's why I'm not going to push for it. But the point is that we can "solve" this problem in Python by: * Doing nothing, and letting people roll their own simiple solution or * Adding a few names to the datetime module Which is a pretty small lift, at least compared to adding new literal syntax. -CHB -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

I'd say seconds(), minutes() etc functions in the datetime module might be good. Same for better docstrings. I dont really think it's worth adding literals though. @Pål You can't import literals. They're syntax, not just bound names. (Unless we're counting future imports, but those aren't real imports.)

You can't import literals. They're syntax, not just bound names.
I'm way out of my comfort zone now, but the parser could for `123.45_f` give `__literal_f__(123.45)` and then that function should be imported. I'm sure this idea has many shortcomings that I don't see, but that was the reason why I wanted to import stuff. Pål

2018-06-05 10:08 GMT+02:00 Pål Grønås Drange <paal.drange@gmail.com>:
Before your code is executed, python scans your entire file for syntax errors. Since 123.45_f is currently not a valid literal, it'll just print a syntax error without even looking at your imports. To change that, the very core of python would need to look completely different. It'd be a metric fuckton of work for a whole lot of people. Im not a core dev myself or anything, but i'm pretty confident that this isn't going to happen for a rather minor need like this.

second, minute, hour (singular) timedelta objects in the module are a good idea, one could do 5 * minute to get a timedelta or one could do value / minute to get a float. a = datetime.now() b = datetime(2018, 2, 3) + 5 * minute print((a - b).total_seconds()) print((a - b) / minute) Le mar. 5 juin 2018 à 10:23, Jacco van Dorp <j.van.dorp@deonet.nl> a écrit :

i'd also be pretty simple to implement.... Just list: minute = timedelta(minutes=1) hour = timedelta(hours=1) etc... and you could import and use them like that. Or if you really want to write 5*m, the just from datetime import minute as m

For closure, I've added a package, timeliterals (env) [pgdr@hostname ~]$ pip install timeliterals (env) [pgdr@hostname ~]$ python
The source code is at https://github.com/pgdr/timeliterals I'm not going to submit a patch to datetime at this time, but I will if people would be interested. - Pål On 5 Jun 2018 13:56, "Jacco van Dorp" <j.van.dorp@deonet.nl> wrote:

I found it fun to be able to write minutes(50) alongside with 50 * minutes so I did that : from datetime import date, time, datetime, timedelta class CallableTimedelta(timedelta): def __call__(self, x): return self * x seconds, milliseconds, microseconds, days, hours, minutes, weeks = (CallableTimedelta(**{x:1}) for x in ('seconds', 'milliseconds', 'microseconds', 'days', 'hours', 'minutes', 'weeks')) print(minutes(50) / seconds) # 3000.0 print(50 * minutes / seconds) # 3000.0 print(minutes(50).total_seconds()) # 3000.0 2018-06-07 13:34 GMT+02:00 Pål Grønås Drange <paal.drange@gmail.com<mailto:paal.drange@gmail.com>>: For closure, I've added a package, timeliterals (env) [pgdr@hostname ~]$ pip install timeliterals (env) [pgdr@hostname ~]$ python
The source code is at https://github.com/pgdr/timeliterals I'm not going to submit a patch to datetime at this time, but I will if people would be interested. - Pål On 5 Jun 2018 13:56, "Jacco van Dorp" <j.van.dorp@deonet.nl<mailto:j.van.dorp@deonet.nl>> wrote: i'd also be pretty simple to implement.... Just list: minute = timedelta(minutes=1) hour = timedelta(hours=1) etc... and you could import and use them like that. Or if you really want to write 5*m, the just from datetime import minute as m _______________________________________________ Python-ideas mailing list Python-ideas@python.org<mailto:Python-ideas@python.org> https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org<mailto:Python-ideas@python.org> https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

Pål Grønås Drange writes:
Speaking for myself, not for Paul, I guess my big objection would be that there would be too many collisions with other interpretations. Eg, does "5m" mean "5 minutes", "5 meters", or "the number 0.005"? Or perhaps "5 minutes of arc"? If you want something close to a literal, you can take the approach used by Decimal of initializing from a string: def make_timedelta(s): if s[-1] == "m": return timedelta(0, 60*int(s[:-1]), 0) # and so on TD = make_timedelta # alias for brevity td = TD("5m") This is more to type, and less readable (if you have a dominant interpretation for the "unit"!), but more flexible, and more readable (if you have multiple dimensioned types around -- if you forget what the unit means in this context, at least you still know the type of the object). It occurs to me that when we have more experience with typed variables, it might be worth revisting this, because then you could write td: datetime.timedelta td = 5m I personally would still be against it because there's too much potential for collision and I don't see why we'd privilege time units over other units or SI multipliers. I'm also sure that you'd get some degree of opposition from the people who really want types to be optional and fear any other feature that depends on typing as a "Trojan horse" to turn Python into a strictly-typed language. Steve

On 4 June 2018 at 09:44, Pål Grønås Drange <paal.drange@gmail.com> wrote:
... and the fact that it's easy to forget that it's 5min rather than 5m (is it 1h or 1hr, 2s or 2sec?) is a problem with the proposal. Of course there's a similar problem with timedelta(minutes=5) or timedelta(minute=5) (timedelta uses minutes, datetime uses minute, I had to look in the docs to check), so it's not unique to the literal notation, but it is an issue nevertheless. Paul

One thing that could solve both this proposal and the aforementioned SI-proposition by Ken Kundert, could be supporting user-defined literals. Suppose that __litXXX___ would make XXX a literal one could use as a suffix for numbers and strings (and lists, dicts, sets?). A user-defined literal could be defined as __lit<insert literal>__, though I don't know how to import it. Anything without leading underscore could be preserved for the standard library: # In the standard library: def __litj__(x): return complex(0, x) # The above example would make 2+3j work as expected # In the datetime module def __lit_h__(x): return timedelta(hours=x) def __lit_min__(x): return timedelta(minutes=x) # In the Pint (units) module for dimensional analysis def __lit_km__(x): return x * pint.UnitRegistry().km # It wouldn't be limited to numbers def __lit_up__(x): return x.upper() s = 'literal'_up # s = LITERAL # The _(x) from Django could be written def __lit_T__(x): return translate(x) s = 'literal'_T # s = translate('literal') # Heck, it could even be written as (abusing(?) notation) def __lit___(x): return translate(x) s = 'literal'_ # s = translate('literal') If we want to abuse the literals more, one could make def __lit_s__(lst): """Makes a list into a sorted list""" return sortedlist(lst) heap = []_s # heap is of type sortedlist - Pål

On 4 June 2018 at 11:59, Pål Grønås Drange <paal.drange@gmail.com> wrote:
The killer would be putting together a full proposal, though. Unless you mean something very different from the norm when you say "literal", literals are evaluated very early in the parsing process, long before user-defined functions are accessible. If what you actually mean is a specialised function calling syntax (where NNN_suf is parsed as the a "literal call" of "suf" with the number NNN as an argument, so it translates to a call to (something like) __lit_suf__(NNN) at runtime, then that's probably possible, but it's extremely unclear why that function-call syntax has any significant advantage over the standard syntax suf(NNN). "readability" is notoriously difficult to argue, and "allows construction of domain-specific languages" is pretty much an anti-goal in Python. So what's left to justify this? Paul

I like the feel of it. In [1]: import pint In [2]: reg = pint.UnitRegistry() In [3]: from extradict import MapGetter In [4]: with MapGetter(reg): ...: from reg import cm, min, hour, km ...: In [5]: km Out[5]: <Unit('kilometer')> In [6]: 10 * km / hour Out[6]: <Quantity(10.0, 'kilometer / hour')> As for the request that started the thread - there was a thread about this not long ago - less than 1 year, for sure. Please, whoever intend to support it, check the arguments there. On Sun, 3 Jun 2018 at 07:53, Pål Grønås Drange <paal.drange@gmail.com> wrote:

On Sat, Jun 2, 2018 at 2:21 PM, Pål Grønås Drange <paal.drange@gmail.com> wrote:
IMO datetimes are not common enough to deserve their own literals. It would make the language more complex and harder to learn for a relatively little benefit. This would probably make more sense as a third party lib:
Both the string and the possibility to specify function arguments would give you way more expressiveness than language literals. -- Giampaolo - http://grodola.blogspot.com

Agreed. I'll add that interpretstr probably isn't necessary; the constructor for timedelta already lets you write >>> datetime.timedelta(hours=2.5, minutes=-14, milliseconds=9300) datetime.timedelta(0, 8169, 300000) Further, I'd argue that such involved timedelta instances are rarely instantiated explicitly, resulting instead from datetime arithmetic. -- Clint

Pål Grønås Drange, I do like the idea of literals typed with scientific units, but I often get short variable names mixed up, so I am not sure if I could use them without a cheat sheet. Formatting datetime is a good example of how confusing a collection of short names can get: Is month %m or %M? Is minute %m or %i? https://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavi... In case you are thinking, "Kyle, how can you even *think* "%i" means minutes?!", please see https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#functio... :) I made a class, with magic methods, to get close to what you want: (2.5*HOUR - 14*MINUTE + 9300*MILLISECOND).total_seconds() I used full names for less confusion, but you can do the same with shorter names: (2.5*h- 14*min + 9300*ms).total_seconds() Maybe the Python parser can be made to add an implied multiplication between a-number-followed-directly-by-a-variable-name. If so, then I could write: (2.5HOUR - 14MINUTE + 9300MILLISECOND).total_seconds() You can define your short, domain specific, suffixes: (2.5h - 14m + 9300ms).total_seconds() On 2018-06-02 08:21, Pål Grønås Drange wrote:

On 04/06/2018 19:50, Kyle Lahnakoski wrote:
This strikes me as quite a nifty idea, if the implied multiplication calls (by default) __rmul__ on the second operand. A ridiculously simple example:
There would, sadly, be a conflict with floating literals such as "2e3" hex literals such as 0XB complex literals such as 4j numeric literals such as 1_234 any others I haven't thought of So the parser would have to give priority to such existing, valid forms. Rob Cliffe

What about 2.5*h - 14*min + 9300*ms * 2 where: h = timedelta(hours=1) min = timedelta (minutes=1) ms = timedelta (milliseconds=1) By the way "min" isn't a keyword, it's a standard function so it can be used as a variable name. However why be limited to time units ? One would want in certain application to define other units, like meter ? Would we want a litteral for that ? For units like those, the "*" operator works well, and if one want more support (like, mix minutes and seconds) one could write simple classes or use libraries, for example to avoid to add time and distance units. Le sam. 2 juin 2018 à 14:21, Pål Grønås Drange <paal.drange@gmail.com> a écrit :

What about
2.5*h - 14*min + 9300*ms * 2
That doesn't seem feasible to implement, however, that is essentially how the Pint [1] module works: import pint u = pint.UnitRegistry() (2.5*u.hour - 14*u.min + 9300*u.ms) * 2 # <Quantity(4.5385, 'hour')> ((2.5*u.hour - 14*u.min + 9300*u.ms) * 2).to('sec') # <Quantity(16338.6, 'second')>
Pint works with all units imaginable: Q = u.Quantity Q(u.c, (u.m/u.s)).to('km / hour') # <Quantity(3.6 speed_of_light, 'kilometer / hour')> However, the idea was just the six (h|min|s|ms|us|ns) time literals; I believe time units are used more often than other units, e.g. in constructs like while end - start < 1min: poll() sleep(1s) # TypeError sleep(1s.total_seconds()) # works, but ugly [1] https://pypi.org/project/Pint/ Best regards, Pål Grønås Drange

Please read the discussions on *https://mail.python.org/pipermail//python-ideas/2016-August/041890.html <https://mail.python.org/pipermail//python-ideas/2016-August/041890.html>*, https://mail.python.org/pipermail//python-ideas/2016-August/041939.html, https://mail.python.org/pipermail//python-ideas/2016-August/042028.html, https://mail.python.org/pipermail//python-ideas/2016-August/041899.html They are more general than your proposal but they still cover pitfalls that may affect yours. It would be better if you could expand your proposal to the concerns raised on those threads. On Sun, Jun 3, 2018 at 12:53 PM, Pål Grønås Drange <paal.drange@gmail.com> wrote:
-- Sebastian Kreft

After reading that thread (this summary should be taken with a grain of salt), the first half is concerned with dimensional analysis not (satisfactorily) performed. The second half of the discussion is dedicated to the major issue with 1E1 meaning 1 and not 1 exa, and it's also pointed out that it is somewhat confusing that 1m means 0.001 and not 1 meter. There is an alternative proposal from that thread, namely that we write 2_m, 2_k etc to mean milli and kilo, respectively. That could also be used in my case. However, the suggested idea with _all the SI units_ is a completely new construct over any programming language in existence; and quite frankly a much bigger change than mine. My suggestion, which is not mine at all, really, has an implementation in C++, with experiences we can learn from. The "timedelta literals" idea has _six_ new literals, whereas Ken Kundert's idea has I don't know how many. I want to highlight one comment I found enlightening, and that is one from Paul Moore: [Python-ideas] SI scale factors in Python Paul Moore p.f.moore at gmail.com Thu Aug 25 16:03:32 EDT 2016
I can understand that a lack of support from people using timedelta will be a blocker. Now, please don't take this as a dismissal of your suggestion that I can learn from the referenced discussion; I did learn a great deal, but I also felt that much of the discussion was around subjects that are less relevant for the "timedelta literals" suggestion. Best regards, Pål Grønås Drange

On 3 June 2018 at 22:03, Pål Grønås Drange <paal.drange@gmail.com> wrote:
I'm not entirely sure what point you're trying to make here, but you quoted that section somewhat out of context. The very next sentence in the same post (full post is at https://mail.python.org/pipermail//python-ideas/2016-August/041878.html) was """ At the moment, I'm not even aware of a particular "dimensional analysis with Python" community, or any particular "best of breed" package in this area that might lead such a proposal - and a language change of this nature probably does need that sort of backing. """ That seems directly relevant here. I'm not aware of a "timedelta users" community, nor is there a particular package (or set of packages) other than the stdlib datetime module, that constitute "best of breed" practice when handling timedeltas. So taking my full quote, I'd have to say that you seem to have undermined your own proposal here. For what it's worth, I use the datetime module and timedeltas regularly, and I would have no use for timedelta literals. Even if the proposal were for a complete set of datetime, date, time and timedelta literals,I still wouldn't have a use for it. Whether that's useful data I don't know, but you seem to be looking for "support from people using timedelta", so I thought I'd clearly state that I, personally, don't support the proposal. Paul

Yes, that was why I quoted it; I thought I should bring the relevant parts of the discussion into this, even if they were against this proposal.
For what it's worth, I use the datetime module and timedeltas regularly, and I [...] don't support the proposal.
That's good to hear, but if you don't mind asking, is your lack of support because you use timedelta "programatically" instead of hard-coded time units, or is there a different (or more) reason(s)? (I'm ready to yield, now I'm just curious.) Best regards, Pål Grønås Drange

On 4 June 2018 at 07:44, Pål Grønås Drange <paal.drange@gmail.com> wrote:
I don't know what you mean by "programatically instead of hard-coded time units". In my code, I've never felt the need to be able to write something like "5min" rather than "timedelta(minutes=5)". Far from it - I find the former jarring, whereas the latter is perfectly clear, so even if the literal form were available I probably wouldn't use it much. I don't see what problem they solve, I've *never* heard anyone claim that using the constructor to create a timedelta object was a problem - even in your proposal you don't explicitly claim that (although I presume you do think that, otherwise why bother making the proposal?). The point of my original comment that you quoted was more to say that *if* a specialist community with expertise in a particular area exists, and they claim that there is benefit to a notation, then their expert knowledge can override the more general view (which is typically pretty conservative, for good reasons). In this case, there's no such expert community, so there's no reason not to go with the default position (which in this case could be summarised as "if none of decimals, scale suffixes or physical units managed to make a case for dedicated literal syntax, timedelta doesn't stand a chance"). Paul

On Mon, Jun 4, 2018 at 12:58 AM, Paul Moore <p.f.moore@gmail.com> wrote:
I think he means essentially: Do you use timedelta with literal arguments? -- as opposed to having it be the result of a calculation or read from a file or ...
I'm the opposite - I use timedelta a fair bit, and find writing: timedelta(hours=24) Pretty awkward. To the point that I make part of my "scripting" API take integer seconds (or floating point hours, or...) rather than a timedelta objects, to save my users from having to do: from datetime import timedelta call_a_function(... timestep = timedelta(seconds=10) ... ) Rather than: call_a_function(... timestep = 10 ... ) The latter of which requires more typing, an extra import, and, most importantly, a knowledge of datetime and the timedelta API. So yeah -- I have a need for it. All that being said, there are an number of things one might want a literal for, and adding a huge pile of them is a bad idea, so I'm -1 on this proposal anyway. It does make me think that I may want to add my own utilities to make this easier: def seconds(s) return timedelta(seconds=s) etc. Then I could add them to my "scripting" library, and my users could do: call_a_function(... timestep = seconds(10) ... ) Not so bad. In fact, maybe it would be a good idea to add such utilities to the datetime module... ANOTHER NOTE: The interface to timedelta is hard to discover because the docstring is incomplete: In [2]: timedelta? Init signature: timedelta(self, /, *args, **kwargs) Docstring: Difference between two datetime values. File: ~/miniconda2/envs/py3/lib/python3.6/datetime.py Type: type Ouch! no idea what keyword argument it takes! [but that's an argument for better docstrings, not adding literals...] -CHB -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

For the general user-defined literals, here are some example use cases: Here is how to expose literals: __literals__ = ['__literal_h__', '__literal_min__', '__literal_s__'] And here is how to import them: from mymodule import _* # imports all literals from mymodule # could also be *_ or ** or ~ or __literals__ ? 90_deg # 1.0 in rad 20_°C # 293.15 Kelvin units.to(20.0_mi, 'km') e = 1_X + 4_G + 13_M + 180_k # SI scale factors can easily be implemented p = 3_x - 2_y + 1_z # Coordinate in 3D M = 4_I # The 4x4 Identity Matrix p * 2_pi # because it's simpler than 2 * np.pi p * 11_π # why not d = 2_sqrt # sqrt(2), or simply 2**(1/2), not a killer argument h1 = 30_px h2 = 4_em # sizes for typography c = 'ffff00'_color # the color yellow h = 'My header'_h3 # renders <h3>My header</h3> or something g = 9.81_m_div_ss # that was ugly ... 'More weird examples'_info # log.info(msg) '2018-06-04'_AD # is a date '192.168.0.42'_ip4 # why not? 'USER'_os # = os.environ.get(x, '') # Can have state? initialize(80) # set default string width to 80 chars 'my string'_c # will now be centralized in 80 chars 'my string'_l # will now be left aligned in 80 chars 'my string'_r # will now be right aligned in 80 chars If we used, e.g. tilde, we could even use it on function calls y = fun(x)~dx # differentiations! Like decorators, but on "call time" I accept that introducing a new symbol has a very high threshold, and will not go through. I just wanted to mention it. Yes, we could write all these as easily as function calls, deg(90) celsius(20) center('my string') # or 'my string'.center(80) But somehow it seems nicer to write 42_km than 12 * pint.UnitRegistry().km - Pål

On Mon, Jun 4, 2018 at 1:59 PM, Pål Grønås Drange <paal.drange@gmail.com> wrote:
For the general user-defined literals, here are some example use cases:
I kind of like the idea of user-defined literals, but:
how about? from pint import km 42*km still not as nice as 42_km, though only by a bit.... So maybe you could propose adding: seconds minutes hours days to the datetime module, and then we could write: 60*seconds == 1*minutes Without any changes to the language at all. -CHB
-- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

On 4 June 2018 at 22:50, Chris Barker via Python-ideas <python-ideas@python.org> wrote:
This strikes me as more of a personal/team style issue, so probably fits better as local definitions in a project's "utilities" module. Putting it in the stdlib implies a certain level of approval of this as a standard idiom, and I'm not sure it's common enough practice to warrant that. Paul

On Mon, Jun 4, 2018 at 3:21 PM, Paul Moore <p.f.moore@gmail.com> wrote:
probably, yes -- that's why I'm not going to push for it. But the point is that we can "solve" this problem in Python by: * Doing nothing, and letting people roll their own simiple solution or * Adding a few names to the datetime module Which is a pretty small lift, at least compared to adding new literal syntax. -CHB -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov

I'd say seconds(), minutes() etc functions in the datetime module might be good. Same for better docstrings. I dont really think it's worth adding literals though. @Pål You can't import literals. They're syntax, not just bound names. (Unless we're counting future imports, but those aren't real imports.)

You can't import literals. They're syntax, not just bound names.
I'm way out of my comfort zone now, but the parser could for `123.45_f` give `__literal_f__(123.45)` and then that function should be imported. I'm sure this idea has many shortcomings that I don't see, but that was the reason why I wanted to import stuff. Pål

2018-06-05 10:08 GMT+02:00 Pål Grønås Drange <paal.drange@gmail.com>:
Before your code is executed, python scans your entire file for syntax errors. Since 123.45_f is currently not a valid literal, it'll just print a syntax error without even looking at your imports. To change that, the very core of python would need to look completely different. It'd be a metric fuckton of work for a whole lot of people. Im not a core dev myself or anything, but i'm pretty confident that this isn't going to happen for a rather minor need like this.

second, minute, hour (singular) timedelta objects in the module are a good idea, one could do 5 * minute to get a timedelta or one could do value / minute to get a float. a = datetime.now() b = datetime(2018, 2, 3) + 5 * minute print((a - b).total_seconds()) print((a - b) / minute) Le mar. 5 juin 2018 à 10:23, Jacco van Dorp <j.van.dorp@deonet.nl> a écrit :

i'd also be pretty simple to implement.... Just list: minute = timedelta(minutes=1) hour = timedelta(hours=1) etc... and you could import and use them like that. Or if you really want to write 5*m, the just from datetime import minute as m

For closure, I've added a package, timeliterals (env) [pgdr@hostname ~]$ pip install timeliterals (env) [pgdr@hostname ~]$ python
The source code is at https://github.com/pgdr/timeliterals I'm not going to submit a patch to datetime at this time, but I will if people would be interested. - Pål On 5 Jun 2018 13:56, "Jacco van Dorp" <j.van.dorp@deonet.nl> wrote:

I found it fun to be able to write minutes(50) alongside with 50 * minutes so I did that : from datetime import date, time, datetime, timedelta class CallableTimedelta(timedelta): def __call__(self, x): return self * x seconds, milliseconds, microseconds, days, hours, minutes, weeks = (CallableTimedelta(**{x:1}) for x in ('seconds', 'milliseconds', 'microseconds', 'days', 'hours', 'minutes', 'weeks')) print(minutes(50) / seconds) # 3000.0 print(50 * minutes / seconds) # 3000.0 print(minutes(50).total_seconds()) # 3000.0 2018-06-07 13:34 GMT+02:00 Pål Grønås Drange <paal.drange@gmail.com<mailto:paal.drange@gmail.com>>: For closure, I've added a package, timeliterals (env) [pgdr@hostname ~]$ pip install timeliterals (env) [pgdr@hostname ~]$ python
The source code is at https://github.com/pgdr/timeliterals I'm not going to submit a patch to datetime at this time, but I will if people would be interested. - Pål On 5 Jun 2018 13:56, "Jacco van Dorp" <j.van.dorp@deonet.nl<mailto:j.van.dorp@deonet.nl>> wrote: i'd also be pretty simple to implement.... Just list: minute = timedelta(minutes=1) hour = timedelta(hours=1) etc... and you could import and use them like that. Or if you really want to write 5*m, the just from datetime import minute as m _______________________________________________ Python-ideas mailing list Python-ideas@python.org<mailto:Python-ideas@python.org> https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org<mailto:Python-ideas@python.org> https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/

Pål Grønås Drange writes:
Speaking for myself, not for Paul, I guess my big objection would be that there would be too many collisions with other interpretations. Eg, does "5m" mean "5 minutes", "5 meters", or "the number 0.005"? Or perhaps "5 minutes of arc"? If you want something close to a literal, you can take the approach used by Decimal of initializing from a string: def make_timedelta(s): if s[-1] == "m": return timedelta(0, 60*int(s[:-1]), 0) # and so on TD = make_timedelta # alias for brevity td = TD("5m") This is more to type, and less readable (if you have a dominant interpretation for the "unit"!), but more flexible, and more readable (if you have multiple dimensioned types around -- if you forget what the unit means in this context, at least you still know the type of the object). It occurs to me that when we have more experience with typed variables, it might be worth revisting this, because then you could write td: datetime.timedelta td = 5m I personally would still be against it because there's too much potential for collision and I don't see why we'd privilege time units over other units or SI multipliers. I'm also sure that you'd get some degree of opposition from the people who really want types to be optional and fear any other feature that depends on typing as a "Trojan horse" to turn Python into a strictly-typed language. Steve

On 4 June 2018 at 09:44, Pål Grønås Drange <paal.drange@gmail.com> wrote:
... and the fact that it's easy to forget that it's 5min rather than 5m (is it 1h or 1hr, 2s or 2sec?) is a problem with the proposal. Of course there's a similar problem with timedelta(minutes=5) or timedelta(minute=5) (timedelta uses minutes, datetime uses minute, I had to look in the docs to check), so it's not unique to the literal notation, but it is an issue nevertheless. Paul

One thing that could solve both this proposal and the aforementioned SI-proposition by Ken Kundert, could be supporting user-defined literals. Suppose that __litXXX___ would make XXX a literal one could use as a suffix for numbers and strings (and lists, dicts, sets?). A user-defined literal could be defined as __lit<insert literal>__, though I don't know how to import it. Anything without leading underscore could be preserved for the standard library: # In the standard library: def __litj__(x): return complex(0, x) # The above example would make 2+3j work as expected # In the datetime module def __lit_h__(x): return timedelta(hours=x) def __lit_min__(x): return timedelta(minutes=x) # In the Pint (units) module for dimensional analysis def __lit_km__(x): return x * pint.UnitRegistry().km # It wouldn't be limited to numbers def __lit_up__(x): return x.upper() s = 'literal'_up # s = LITERAL # The _(x) from Django could be written def __lit_T__(x): return translate(x) s = 'literal'_T # s = translate('literal') # Heck, it could even be written as (abusing(?) notation) def __lit___(x): return translate(x) s = 'literal'_ # s = translate('literal') If we want to abuse the literals more, one could make def __lit_s__(lst): """Makes a list into a sorted list""" return sortedlist(lst) heap = []_s # heap is of type sortedlist - Pål

On 4 June 2018 at 11:59, Pål Grønås Drange <paal.drange@gmail.com> wrote:
The killer would be putting together a full proposal, though. Unless you mean something very different from the norm when you say "literal", literals are evaluated very early in the parsing process, long before user-defined functions are accessible. If what you actually mean is a specialised function calling syntax (where NNN_suf is parsed as the a "literal call" of "suf" with the number NNN as an argument, so it translates to a call to (something like) __lit_suf__(NNN) at runtime, then that's probably possible, but it's extremely unclear why that function-call syntax has any significant advantage over the standard syntax suf(NNN). "readability" is notoriously difficult to argue, and "allows construction of domain-specific languages" is pretty much an anti-goal in Python. So what's left to justify this? Paul

I like the feel of it. In [1]: import pint In [2]: reg = pint.UnitRegistry() In [3]: from extradict import MapGetter In [4]: with MapGetter(reg): ...: from reg import cm, min, hour, km ...: In [5]: km Out[5]: <Unit('kilometer')> In [6]: 10 * km / hour Out[6]: <Quantity(10.0, 'kilometer / hour')> As for the request that started the thread - there was a thread about this not long ago - less than 1 year, for sure. Please, whoever intend to support it, check the arguments there. On Sun, 3 Jun 2018 at 07:53, Pål Grønås Drange <paal.drange@gmail.com> wrote:

On Sat, Jun 2, 2018 at 2:21 PM, Pål Grønås Drange <paal.drange@gmail.com> wrote:
IMO datetimes are not common enough to deserve their own literals. It would make the language more complex and harder to learn for a relatively little benefit. This would probably make more sense as a third party lib:
Both the string and the possibility to specify function arguments would give you way more expressiveness than language literals. -- Giampaolo - http://grodola.blogspot.com

Agreed. I'll add that interpretstr probably isn't necessary; the constructor for timedelta already lets you write >>> datetime.timedelta(hours=2.5, minutes=-14, milliseconds=9300) datetime.timedelta(0, 8169, 300000) Further, I'd argue that such involved timedelta instances are rarely instantiated explicitly, resulting instead from datetime arithmetic. -- Clint

Pål Grønås Drange, I do like the idea of literals typed with scientific units, but I often get short variable names mixed up, so I am not sure if I could use them without a cheat sheet. Formatting datetime is a good example of how confusing a collection of short names can get: Is month %m or %M? Is minute %m or %i? https://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavi... In case you are thinking, "Kyle, how can you even *think* "%i" means minutes?!", please see https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#functio... :) I made a class, with magic methods, to get close to what you want: (2.5*HOUR - 14*MINUTE + 9300*MILLISECOND).total_seconds() I used full names for less confusion, but you can do the same with shorter names: (2.5*h- 14*min + 9300*ms).total_seconds() Maybe the Python parser can be made to add an implied multiplication between a-number-followed-directly-by-a-variable-name. If so, then I could write: (2.5HOUR - 14MINUTE + 9300MILLISECOND).total_seconds() You can define your short, domain specific, suffixes: (2.5h - 14m + 9300ms).total_seconds() On 2018-06-02 08:21, Pål Grønås Drange wrote:

On 04/06/2018 19:50, Kyle Lahnakoski wrote:
This strikes me as quite a nifty idea, if the implied multiplication calls (by default) __rmul__ on the second operand. A ridiculously simple example:
There would, sadly, be a conflict with floating literals such as "2e3" hex literals such as 0XB complex literals such as 4j numeric literals such as 1_234 any others I haven't thought of So the parser would have to give priority to such existing, valid forms. Rob Cliffe
participants (13)
-
Chris Barker
-
Clint Hepner
-
Giampaolo Rodola'
-
Jacco van Dorp
-
Joao S. O. Bueno
-
Kyle Lahnakoski
-
Paul Moore
-
Pål Grønås Drange
-
Rob Cliffe
-
Robert Vanden Eynde
-
Robert Vanden Eynde
-
Sebastian Kreft
-
Stephen J. Turnbull