[Python-Dev] Re: PEP 618: Add Optional Length-Checking To zip

2020-06-01 Thread Steven D'Aprano
On Mon, Jun 01, 2020 at 09:36:40PM +1000, Nick Coghlan wrote:

> zip() can be used to combine iterables of different lengths, including
> combining finite iterables with infinite iterators. By default, the output
> iterator is implicitly truncated to produce the same number of items as the
> shortest input iterable. Setting *truncate* to false disables this implicit
> truncation and raises ValueError instead.

It's not really *implicit* if there's an explicit flag controlling the 
behaviour, even with a default value. We don't use that sort of language 
elsewhere. For example, help(sorted) doesn't say:

"Return a new list containing all items from the iterable implicitly in 
ascending order. Pass reverse=True to disable this implicit order."

help(int) doesn't say that the base is implicitly decimal; help(print) 
doesn't talk about "implicit spaces between items, implicit newline at 
the end of the output" etc. It just states the behaviour controlled by 
the parameter.

This is accurate, non-judgemental, and avoids being over-wordy:

"By default, the output iterator is truncated at the shortest input 
iterable."


> The conceptual idea here is that the "truncate" flag name would technically
> be a shorter mnemonic for "truncate_silently", so clearing it gives you an
> exception rather enabling padding behaviour.
>
> Flipping the sense of the flag also means that "truncate=True" will appear
> in IDE tooltips as part of the function signature, providing significantly
> more information than "strict=False" would.

"Significantly" more? I don't think so.

Truncate at what?

- some maximum length;
- some specific element;
- at the shortest input.

At some point people have to read the docs, not just the tooltips. If 
you didn't know what zip does, seeing truncate=True won't mean anything 
to you. If you do know what zip does, then the parameter names are 
mnemonics, and strict=False and truncate=True provide an equal hint for 
the default behaviour:

* if it's not strict, it is tolerant, stopping at the shortest;
* if it truncates, it truncates at the shortest input.

For the default case, strict=False and truncate=True are pretty much 
equal in information.

But for the case of non-default behaviour, strict=True is a clear 
winner. It can pretty much only mean one thing: raise an exception. 
Whereas truncate=False is ambiguous:

- pad the output;
- skip items as they become empty;
- raise an exception.

All three of these are useful behaviour, and while the middle one is not 
part of this PEP, it was requested in the discussions on Python-Ideas.


> That improved self-documentation then becomes what I would consider the
> strongest argument in favour of the flag-based approach:

I don't think that "truncate=False" (which can mean three different 
things) is more self-documenting than `zip(*items, mode='strict')` or 
`zip_strict()` (either of which can only mean one thing).



-- 
Steven
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/37TI3ZEU7JZUJBERFDRRCMNI5Y3CGP7T/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: type() does not call __prepare__?

2020-06-01 Thread Joao S. O. Bueno
But on calling `type` in this way, you are passing the namespace as a
ready object, as the
3rdy parameter - what would `__prepare__` even do, besides print it's
been called?

For sometime (maybe Python 3.3, I forgot), some helper
callables where added to the `types` module to allow
one to have more freedom in imperatively created classes:

 python

In [20]: types.prepare_class?
Signature: types.prepare_class(name, bases=(), kwds=None)
Docstring:
Call the __prepare__ method of the appropriate metaclass.

Returns (metaclass, namespace, kwds) as a 3-tuple

*metaclass* is the appropriate metaclass
*namespace* is the prepared class namespace
*kwds* is an updated copy of the passed in kwds argument with any
'metaclass' entry removed. If no kwds argument is passed in, this will
be an empty dict.
File:  ~/projetos/terminedia/env/lib64/python3.7/types.py
Type:  function

In [21]: types.new_class?
Signature: types.new_class(name, bases=(), kwds=None, exec_body=None)
Docstring: Create a class object dynamically using the appropriate metaclass.
File:  ~/projetos/terminedia/env/lib64/python3.7/types.py
Type:  function

In [22]: types.resolve_bases?
Signature: types.resolve_bases(bases)
Docstring: Resolve MRO entries dynamically as specified by PEP 560.
File:  ~/projetos/terminedia/env/lib64/python3.7/types.py
Type:  function
```

On Mon, 1 Jun 2020 at 16:28, Ethan Furman  wrote:
>
>  From stackoverflow [1]
>
># metaprepare.py
>class Meta1(type):
>@classmethod
>def __prepare__(mcs, name, bases):
>print('call prepare')
>return {}
>def __new__(mcs, name, bases, parameters):
>return super().__new__(mcs, name, bases, parameters)
>
>class A(metaclass=Meta1):
>pass
>
>type('C', (A, ), {})
>
> output is:
>
>call prepare
>
> (just the once, not twice)
>
> The behavior of `type()` not calling `__prepare__()` has been constant since 
> 3.3.
>
> Is it a bug?
>
>
> --
> ~Ethan~
>
>
> [1] https://stackoverflow.com/q/62128254/208880
> ___
> Python-Dev mailing list -- python-dev@python.org
> To unsubscribe send an email to python-dev-le...@python.org
> https://mail.python.org/mailman3/lists/python-dev.python.org/
> Message archived at 
> https://mail.python.org/archives/list/python-dev@python.org/message/CQTD7WFOIDBF5PSD77AALBTWJQ67UPM5/
> Code of Conduct: http://python.org/psf/codeofconduct/
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/IPBWREEI6JYGWROTSXGHBWLG53Q4A23A/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Repr: where to place the address and additional info?

2020-06-01 Thread Brett Cannon
Serhiy Storchaka wrote:
> The default repr of Python object is formatted using the following pattern:
>  <{typename} object at {address:#x}>
> 
> And many custom reprs use similar patterns, but add some additional type 
> specific information. The type name first, followed by details and 
> address. All is surrounded by angle quotes. The question is in what 
> order to show address and other details? Should the address be at rear 
> or in the middle?
>  <{typename} {details} at {address:#x}>
>  <{typename} at {address:#x} {details}>

I prefer the address at the rear. Chances are you care more about the details 
than the address, so put the more useful information earlier.

> 
> There are examples of both ways in the stdlib. I am going to add new 
> custom reprs [1] and need to know what pattern looks better.
> Also, is "object" mandatory after the type name?
>  <{typename} object {details} at {address:#x}>
>  <{typename} object at {address:#x} {details}>

I don't think it's mandatory. Whatever reads best.

-Brett

> 
> [1] https://bugs.python.org/issue24391
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/OY7UZGXEU32ACL7HOPI7RLGCOJEMTF5K/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] type() does not call __prepare__?

2020-06-01 Thread Ethan Furman

From stackoverflow [1]

  # metaprepare.py
  class Meta1(type):
  @classmethod
  def __prepare__(mcs, name, bases):
  print('call prepare')
  return {}
  def __new__(mcs, name, bases, parameters):
  return super().__new__(mcs, name, bases, parameters)

  class A(metaclass=Meta1):
  pass

  type('C', (A, ), {})

output is:

  call prepare

(just the once, not twice)

The behavior of `type()` not calling `__prepare__()` has been constant since 
3.3.

Is it a bug?


--
~Ethan~


[1] https://stackoverflow.com/q/62128254/208880
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/CQTD7WFOIDBF5PSD77AALBTWJQ67UPM5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: PEP 618: Add Optional Length-Checking To zip

2020-06-01 Thread Ethan Furman

On 06/01/2020 04:36 AM, Nick Coghlan wrote:


Reading this thread and the current PEP, the main question I had was whether it might be 
better to flip the sense of the flag and call it "truncate".

So the status quo would be "truncate=True", while the ValueError could be requested by 
passing an explicit "truncate=False".


I like this a lot.

+1

--
~Ethan~
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/TQWWOQM4MQZ4T5CPZB3D4XPQ5HKIVFEP/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: PEP 618: Add Optional Length-Checking To zip

2020-06-01 Thread Nick Coghlan
On Thu., 21 May 2020, 4:09 am Jim J. Jewett,  wrote:

> David Mertz wrote:
>
> > Fwiw, I don't think it changes my order, but 'strict' is a better word
> than
> > 'equal' in all those places. I'd subtract 0.1 from each of those votes if
> > they used "equal".
>
> I would say that 'equal' is worse than 'strict'. but 'strict' is also
> wrong.
>
> Zipping to a potentially infinite sequence -- like a manual enumerate --
> isn't wrong.  It may be the less common case, but it isn't wrong.  Using
> 'strict' implies that there is something sloppy about the data in, for
> example, cases like Stephen J. Turnbull's lagged time series.
>
> Unfortunately, the best I can come up with is 'same_length', or possibly
> 'equal_len' or 'equal_length'.  While those are better semantically, they
> are also slightly too long or awkward.  I would personally still consider
> 'same_length' the least bad option.
>

Reading this thread and the current PEP, the main question I had was
whether it might be better to flip the sense of the flag and call it
"truncate".

So the status quo would be "truncate=True", while the ValueError could be
requested by passing an explicit "truncate=False".

Draft documentation paragraph:

==
zip() can be used to combine iterables of different lengths, including
combining finite iterables with infinite iterators. By default, the output
iterator is implicitly truncated to produce the same number of items as the
shortest input iterable. Setting *truncate* to false disables this implicit
truncation and raises ValueError instead. Note that if this ValueError is
raised an additional item will have been consumed from any iterators listed
before the shortest iterator (or from the second listed iterator if the
first iterator is the shortest one).

To pad shorter input iterables rather than truncating the output or raising
ValueError, see itertools.zip_longest.
==

The conceptual idea here is that the "truncate" flag name would technically
be a shorter mnemonic for "truncate_silently", so clearing it gives you an
exception rather enabling padding behaviour.

Flipping the sense of the flag also means that "truncate=True" will appear
in IDE tooltips as part of the function signature, providing significantly
more information than "strict=False" would.

That improved self-documentation then becomes what I would consider the
strongest argument in favour of the flag-based approach: providing more
information up-front to users regarding the actual behaviour of the
builtin, rather than having them incorrectly assume that mismatched input
iterator lengths will raise an exception.

Side note: this idea pairs nicely with the "zip(itr, itr, ir)" idiom for
non-overlapping data windows, as it makes it straightforward to request an
exception if the last data tuple has values missing (without the flag, the
idiom silently discards incomplete trailing data).

Cheers,
Nick.

P.S. I had the opportunity to read the thread from beginning to end after
belatedly catching some of the messages out of context, and FWIW, I started
out assuming I would strongly favour the itertools function option, and
surprised myself by favouring the flag option (albeit inverted) by the time
I reached the end.


>
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/MBLEKMSJOVHPH64GDTE4LZA2Z5TTZKAC/
Code of Conduct: http://python.org/psf/codeofconduct/