Re: [Python-ideas] Proposal: Query language extension to Python (PythonQL)

2017-03-27 Thread Brice PARENT



Le 27/03/17 à 10:55, Pavel Velikhov a écrit :

Hi Brice,

On 27 Mar 2017, at 10:17, Brice PARENT <cont...@brice.xyz 
<mailto:cont...@brice.xyz>> wrote:


I prefer this a lot to the original syntax, and I really think this 
has much better chances to be integrated (if such an integration had 
to be done, and not kept as a separate module).


Also, maybe managing this with classes instead of syntax could also 
be done easily (without any change to Python), like this:


from pyql import PQL, Select, For, Where, GroupBy, Let

result = PQL(
Select("x", "sum_y"),
For("x", range(1,8)),
For("y",range(1,7)),
Where(lambda x, y: x %2==0andy %2!=0andx >y, "x", "y"),  # 
function, *[arguments to pass to the function]

Where("sum_y", lambda sum_y: sum_y %2!=0)
GroupBy("x"),
Let("sum_y", lambda y: sum(y), "y")
)




So here’s the deal: small queries will look pretty decent in pretty 
much all paradigms, ORM, or PythonQL or your proposal.
Once they get bigger and combine multiple pain points (say outerjoins, 
grouping and nested data) - then unless you have a

really clear and minimal language, folks will get confused and lost.

We’ve gone through a few query languages that failed, including XQuery 
and others, and the main reason was the need to learn
a whole new language and a bunch of libraries, nobody wanted to do it. 
So the main selling point behind PythonQL is: its Python

that folks hopefully know already, with just a few extensions.
I get it, but it's more a matter of perception. To me, the version I 
described is just Python, while yours is Python + specific syntax. As 
this syntax is only used in PyQL sub-language, it's not really Python 
any more...
Also, what I like with what I used, is that it is object-based, which 
allows any part of the query to be reusable or built dynamically. We 
might also extend such a PQL object's constructor to embed automatically 
whatever default parameters or database connection we want, or shared 
behaviours, like:


class MyPQL(PQL):
def get_limit(self):
if self.limit is not None:
return self.limit

return 10

def __init__(self, *args):
args.append(Let("sum_y", lambda y: sum(y), "y"))
args.append(GroupBy("x"))
super().__init__(*args)

result = MyPQL(
Select("x", "sum_y"),
For("x", range(1,8)),
For("y",range(1,7)),
Where(lambda x, y: x %2==0andy %2!=0andx >y, "x", "y"),
Where("sum_y", lambda sum_y: sum_y %2!=0)
)

Big queries, this way, may be split into smaller parts. And it allows 
you to do the following in a single query, instead of having to write 
one big for each condition


where_from = [For("x", range(1,8)),For("y",range(1,7))]
where = [Where(lambda x, y: x %2==0andy %2!=0andx >y, "x", "y")]
if filter_sum_y:
where.append(Where("sum_y", lambda sum_y: sum_y %2!=0))

if group_by is not None:
grouping = GroupBy("x")

result = MyPQL(Select("x", "sum_y"), *where_from, *where, *grouping)

Side note : I'm not a big database user, I mostly use ORMs (Django's and 
PonyORM depending on the projects) to access PgSQL and SQLite (for unit 
testing), so I might not even have use cases for what you're trying to 
solve. I just give my point of view here to explain what I think could 
be more easily integrated and (re)used. And as I'm a big fan of the DRY 
mentality, I'm not a fan of the syntax-chaining things (as well as I 
don't really like big nested comprehensions).


-Brice
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Way to repeat other than "for _ in range(x)"

2017-03-30 Thread Brice PARENT


Le 30/03/17 à 15:51, Mark E. Haase a écrit :
I'm not picking on your specific example. I am only pointing out that 
Python gives you the tools you need to build nice APIs. If repetition 
is an important part of something you're working on, then consider 
using itertools.repeat, writing your own domain-specific repeat() 
method, or even override * like list() does. One of the coolest 
aspects of Python is how a relatively small set of abstractions can be 
combined to create lots of useful behaviors.


For students, the lack of a "repeat" block might be confusing at 
first, but once the student understands for loops in general, it's an 
easy mental jump from "using the loop variable in the body" to "not 
using the loop variable in the body" to "underscore is the convention 
for an unused loop variable". In the long run, having one syntax that 
does many things is simpler than having many syntaxes that each do one 
little thing.

+1
I would add that it is even the convention for all unused variables, not 
only in loops, as it is also used in other cases, like this for example :

key, _, value = "foo:date:bar".split(":")
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Way to repeat other than "for _ in range(x)"

2017-03-30 Thread Brice PARENT


Le 30/03/17 à 19:06, Markus Meskanen a écrit :
And like I said before, for loop is just another way of doing while 
loop, yet nobody's complaining. There's nothing wrong with having two 
different ways of doing the same thing, as long as one of them is 
never the better way. If we add `repeat`, there's never a reason to 
use `for _ in range` anymore.

It doesn't always creates something easier to use, like for example :
`for _ in range(x, y, z)` (fixed or variable parameters)
`for _ in one_list` (saves a call to len() with your solution)
`for _ in any_other_kind_of_iterable` (we don't need to know the length 
here, we may even use a generator)


___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] For/in/as syntax

2017-03-03 Thread Brice PARENT

Thanks Matthias for taking the time to give your opinion about it.

Just to set the focus where I may have failed to point it:
the main purpose of this proposal is the creation of the object itself, 
an object representing the loop. What we can do with it is still a 
sub-level of this proposal, as the presence of this object is what 
allows all these simplifications, and offers a lot of possibilities.


Le 03/03/17 à 17:21, Matthias Bussonnier a écrit :



A word about compatibility and understandability before:
"as" is already a keyword, so it is already reserved and easy to parse. It
couldn't be taken for its other uses (context managers, import statements
and
exceptions) as they all start with a specific and different keyword. It
would
have a really similar meaning, so be easy to understand. It doesn't imply
any
incompatibility with previous code, as current syntax would of course still
be
supported, and it doesn't change anything anywhere else (no change in
indentation levels for example, no deprecation).

That still would make it usable only on Python 3.7+ It seem to me you ca already
do that now with helper object/function:
Yes, what I meant is that it wouldn't break any code from previous 
versions. But as with any new keyword or syntax evolution, newer code 
wouldn't get well in older versions.

In  [1]: class Loop:
 ...:
 ...: def __init__(self, iterable):
 ...: self._it = iter(iterable)
 ...: self._break = False
 ...:
 ...: def __next__(self):
 ...: if self._break:
 ...: raise StopIteration
 ...: return (self, next(self._it))
 ...:
 ...: def __iter__(self):
 ...: return self
 ...:
 ...: def brk(self):
 ...: self._break = True
 ...:
 ...:

In  [2]: for loop,i in Loop(range(10)):
 ...: if i==5:
 ...: loop.brk()
 ...: print(i)
1
2
3
4
5

A 2 level breaking would have to be used this way :

for outerloop, i in Loop(range(4)):
for innerloop, j in Loop(range(3)):
if i==2 and j==1:
outerloop.brk()
break  # this

print(i, j)

if outerloop.broken:
break  # or continue. For the code following this line not to 
be executed


break() and continue() methods (which I named this way to reflect the 
behaviour of the statements, but I'm not sure whether it could or should 
be kept this way) are only an improvement on the keywords in nested 
loops or complex situations where explicitly exiting a certain loop 
helps readability by removing a bunch of conditions and assignments. 
It's also allowing to pass this function as a callback to something 
else, but I'm not sure it would be that useful.



Syntax:
for element in elements as elements_loop:
 assert type(elements_loop) is ForLoopIterationObject

Properties and methods (non exhaustive list, really open to discussion and
suggestions):
for element in elements as elements_loop:
 elements_loop.length: int  # len ? count ?
 elements_loop.counter: int  # Add a counter0, as in Django? a counter1 ?
 elements_loop.completed: bool
 elements_loop.break()
 elements_loop.continue()
 elements_loop.skip(count=1)

I did not implement these, but they are as feasible. (break is keyword
though, so you need to rename it)
Length, counter and completed (and .skip() in some ways) are just 
helping the readability while allowing a more concise code, as they 
don't force to create temporary variables. But they belong here as a 
part of the whole thing. If the object had to be integrated, for 
.break(), .continue(), .skip() and any other useful method, those little 
improvements would be nice to have and help to keep your code look more 
like the design in your head.
.break() and .continue() help as they modify the flow. They roughly do 
this :


LOOP1
LOOP2
if something:
LOOP1.break()  # similar to calling break repeatedly once 
for every loop until LOOP1 included, so going straight to 
"something_after_every_loop"


something_else

yet_another_thing

something_after_every_loop

The same goes for .continue(), calling break in every inner loop, and 
continue in the one the method is called from.



Examples of every presented element (I didn't execute the code, it's just to
explain the proposal):

##
# forloop.counter and forloop.length

for num, dog in enumerate(dogs):
 print(num, dog)

print("That's a total of {} dogs !".format(len(dogs)))

# would be equivalent to

for dog in dogs as dogs_loop:
 print(dogs_loop.counter, dog)

I find it less readable than enumerate.
Well, I find both readable, but the second only requires to focus on the 
loop itself, not on how enumerate and unpacking work.
The second syntax however allows the exact same syntax when you work 
with dicts, lists or tuples. Here, dogs may be of any type of iterable, 
while in 

Re: [Python-ideas] namedtuple literals [Was: RE a new namedtuple]

2017-07-20 Thread Brice PARENT

If the type is a data, it probably belongs to the inside of the tuple:

smart = (type="Car", cost=18_900, hp=89, weight=949)
harley = (type="Motorcycle", cost=18_900, hp=89, weight=949)
both_vehicles = (type(smart) == type(harley)) # True - 
type+cost+hp+weight on both sides
same_vehicles = (smart == harley)  # False - cost, hp and weight are 
identical, but not type



Le 20/07/17 à 07:12, David Mertz a écrit :
I'm concerned in the proposal about losing access to type information 
(i.e. name) in this proposal.  For example, I might write some code 
like this now:


>>> from collections import namedtuple
>>> Car = namedtuple("Car", "cost hp weight")
>>> Motorcycle = namedtuple("Motorcycle", "cost hp weight")
>>> smart = Car(18_900, 89, 949)
>>> harley = Motorcyle(18_900, 89, 949)
>>> if smart==harley and type(smart)==type(harley):
... print("These are identical vehicles")

The proposal to define this as:

>>> smart = (cost=18_900, hp=89, weight=949)
>>> harley = (cost=18_900, hp=89, weight=949)

Doesn't seem to leave any way to distinguish the objects of different 
types that happen to have the same fields.  Comparing 
`smart._fields==harley._fields` doesn't help here, nor does any type 
constructed solely from the fields.


Yes, I know a Harley-Davidson only weighs about half as much as a 
SmartCar, although the price and HP aren't far off.


I can think of a few syntax ideas for how we might mix in a "name" to 
the `ntuple` objects, but I don't want to bikeshed.  I'd just like to 
have the option of giving a name or class that isn't solely derived 
from the field names.



On Wed, Jul 19, 2017 at 9:06 PM, Nick Coghlan > wrote:


On 20 July 2017 at 11:35, Alexander Belopolsky
> wrote:
> On Wed, Jul 19, 2017 at 9:08 PM, Guido van Rossum
> wrote:
>> The proposal in your email seems incomplete
>
> The proposal does not say anything about type((x=1, y=2)).  I assume
> it will be the same as the type currently returned by
namedtuple(?, 'x
> y'), but will these types be cached? Will type((x=1, y=2)) is
> type((x=3, y=4)) be True?.

Right, this is one of the key challenges to be addressed, as is the
question of memory consumption - while Giampaolo's write-up is good in
terms of covering the runtime performance motivation, it misses that
one of the key motivations of the namedtuple design is to ensure that
the amortised memory overhead of namedtuple instances is *zero*, since
the name/position mapping is stored on the type, and *not* on the
individual instances.

From my point of view, I think the best available answers to those
questions are:

- ntuple literals will retain the low memory overhead characteristics
of collections.namedtuple
- we will use a type caching strategy akin to string interning
- ntuple types will be uniquely identified by their field names
and order
- if you really want to prime the type cache, just create a module
level instance without storing it:

(x=1, y=2) # Prime the ntuple type cache

A question worth asking will be whether or not
"collections.namedtuple" will implicitly participate in the use of the
type cache, and I think the answer needs to be "No". The problem is
twofold:

1. collections.namedtuple accepts an additional piece of info that
won't be applicable for ntuple types: the *name*
2. collections.namedtuple has existed for years *without* implicit
type caching, so adding it now would be a bit weird

That means the idiomatic way of getting the type of an ntuple would be
to create an instance and take the type of it:type((x=1, y=2))

The could still be the same kind of type as is created by
collections.namedtuple, or else a slight variant that tailors repr()
and pickling support based on the fact it's a kind of tuple literal.

Cheers,
Nick.

--
Nick Coghlan   | ncogh...@gmail.com  
 |  Brisbane, Australia

___
Python-ideas mailing list
Python-ideas@python.org 
https://mail.python.org/mailman/listinfo/python-ideas

Code of Conduct: http://python.org/psf/codeofconduct/





--
Keeping medicines from the bloodstreams of the sick; food
from the bellies of the hungry; books from the hands of the
uneducated; technology from the underdeveloped; and putting
advocates of freedom in prisons.  Intellectual property is
to the 21st century what the slave trade was to the 16th.


___
Python-ideas mailing list
Python-ideas@python.org

Re: [Python-ideas] π = math.pi

2017-06-07 Thread Brice PARENT


Le 07/06/17 à 07:34, Greg Ewing a écrit :


Yes, there are a few symbols it would be nice to have.
A proper ≠ symbol would have avoided the wars between
<> and !=. :-)


I'm not sure it's worth any change in the language, it's already really 
easy to read and write as is.


But I agree this can be great to have for example for reviewers (Python 
being what it is, you can have reviewers who are not really pythonistas 
but just here to check the logic and maths for example). And it's 
already available by using some fonts that provide a good ligature 
support, like Fira Code 
(https://twitter.com/pycharm/status/804786040775045123?lang=fr). I'm not 
sure about the support in other editors/terminals tho.



___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] π = math.pi

2017-06-01 Thread Brice PARENT

Why not simply use

from math import pi as π

and so on? It makes your math formulas more readable (taking out the 
"math." entirely), without requiring any change to the module.



Le 01/06/17 à 08:47, Serhiy Storchaka a écrit :
What you are think about adding Unicode aliases for some mathematic 
names in the math module? ;-)


math.π = math.pi
math.τ = math.tau
math.Γ = math.gamma
math.ℯ = math.e

Unfortunately we can't use ∞, ∑ and √ as identifiers. :-(

___
Python-ideas mailing list
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
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] PEP 572: about the operator precedence of :=

2018-05-14 Thread Brice Parent


Le 10/05/2018 à 09:32, Terry Reedy a écrit :

On 5/9/2018 11:33 PM, Guido van Rossum wrote:

I now think that the best way out is to rule `:=` in the top level 
expression of an expression statement completely 


I would like to be able to interactively enter

>>> a: = f(2,4)

to have 'a' echoed as well as bound.


This behaviour is reachable by using:

print(a := f(2,4))

Otherwise, I can see coming a new wave of devs saying assignment with 
"=" is outdated, and we should use the new assignment operator ":=". 
Cleaning code by removing useless prints is something, having to go 
through the syntax to remove the ":" of some ":=", and having to test 
everything again to be sure we didn't break anything is another matter 
(long live unit tests! But still...).


Side note: I wouldn't recommand to use `print(a := f(2,4))` either, as 
it would be a print-with-a-side-effect. Which would then also break the 
code if removed the usual way by deleting the line (or commenting it if 
you like messy). Maybe, for another proposal of course, we could allow 
the shell to be more verbose, by printing any explicit assignment 
(whether we used "=" or ":=" wouldn't matter)?


-Brice
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Specifying Python version

2018-05-17 Thread Brice Parent
It's in the same spirit as what I proposed in my answer to Guido's crazy 
idea, but set at a different level (in code instead of in setup.py in my 
proposal).


I think it belongs to the same discussion, so I'm copying it here (sorry 
for the inconvenience if you already read it there):



Le 16/05/2018 à 11:22, Brice Parent a écrit :


Why not have the compatibility be done at setup.py's level?

Like having something like this:

setup(
    name="LegacyLib",
    version="8.4.1",
    ...
    max_compatibility="3.4"  # Defining here Python's max version for 
which this lib has been upgraded

)

Of course, we may use any other word instead of "max_compatibility", 
like "designed_for", "python_version", or anything a better English 
speaker could think of.


The point is, it would either:

- when you install the library, rename all variables that are now 
keywords (we'd know the exact list thanks to max_compatiblity) by 
suffixing them with "_"


- or set a flag that will do that when creating the *.pyc files.

Possible problems/limitations I can already find:

- There would still be possible errors when using variable names that 
are generated on the fly (I have no clue how this could ever be addressed)
- It might get complicated at some point to know what to do, like when 
we have lib_a in some version depending on lib_b (with or without a 
max_compatibility version number), it is obvious that lib_a will use 
lib_b's original variable names (without the appended "_"), but our 
code which might also want to interact with lib_b would have to.





-Brice

Le 16/05/2018 à 21:02, MRAB a écrit :
Instead of verbatim identifiers, how about a special comment giving 
the Python version in which the file was written?


There could then be a tool similar to 2to3 that converts the file to a 
more recent version of Python that might have new reserved words. In 
most cases the new file would merely be a copy of the original, but 
with an updated Python version.

___
Python-ideas mailing list
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
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] PEP 505: None-aware operators

2018-07-29 Thread Brice Parent

Le 29/07/2018 à 08:02, Steven D'Aprano a écrit :

On Sun, Jul 29, 2018 at 12:49:13PM +1200, Greg Ewing wrote:

Abe Dillon wrote:

others countering that `person.name ` is not how
periods are used in natural languages, so using other symbols in
unintuitive ways is perfectly fine.

Dots have been used for attribute access in so many languages
for so long that it has become the normal and expected syntax
to use. ?. is much more recent. Maybe in another 30 years, if
it has stood the test of time, it could be argued for on the
basis of familiarity, but not now.

You're talking like the syntax is used only by a handful of experimental
languages with a total user-base measured in the dozens.

?. is used by some of the most commonly used languages in the world,
such as C++, Objective C and PHP, as well as up-and-coming "cool"
languages getting lots of industry buzz, like Swift and Dart.

Its certainly more familiar now than Python's slicing syntax was when
Python first started.
But it's certainly less familiar than using 'foo++' and '++foo', but 
we're still writing 'foo += 1'. So familiarity in other languages is not 
the only point (alos, I'm convinced that if we were making a poll with 
average and advanced users of these languages about what these syntax 
were doing, a big proportion wouldn't know).


I believe the evolution should be done if the benefice are big enough 
(which I doubt, but I don't use Python in all the ways possible), not 
because others do it (whatever their reasons were). Then, when we know 
there's definitely a need to solve a problem, the solution should be 
chosen (??, but anything else could enter the discussion).


Here, we're mixing arguments about (and I confess I've done it too):
- how usefull it would be to have a solution to this problem
- if it should be solved by Python's syntax or by libraries (if it may 
be done, and if it may not, how spread are the use cases that can't be 
done this way)
- if other languages support something like that, if some are, how well 
this was accepted and if it's getting used in new code

- the proposed syntax itself
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] PEP 505: None-aware operators

2018-07-29 Thread Brice Parent

Le 29/07/2018 à 09:12, Abe Dillon a écrit :




[...]


NO! I'm proposing:

spam.eggs.cheese.aardvark?

A single POSTFIX operator that has a high priority in the order of 
operations.
I don't believe we need spam?.eggs.cheese?.aardvark, because I don't 
think it is justified by the small benefits it gets us.
For the same reason, I don't believe we need spam.eggs.cheese.aardvark? 
(there is exactly the same number of use cases). We win a bit in 
readability as it's closer to most spoken languages, but we lose in 
granularity as we're hiding any error that would raise if spam.eggs 
returned None, so it's not a full win on this side either...


___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Jump to function as an an alternative to call function

2018-08-16 Thread Brice Parent
If I understand well the need (I'm unsure I've had it myself), it would 
be easier to be able to import the function in the active context, like 
this:


def foo(a):
    return a + c

def bar(a, c):
    return foo(a)

def bazz(a, c):
    import __file__.foo
    return foo(a)

c = 5
call = bar(1, 3)  # -> 6
jump = bazz(1, 3)  # -> 4

bazz would be equivalent to:

def bazz(a, c):
    def foo(a):
    return a + c
    return foo(a)

I'm not saying the syntax is the one that should be used (__file__ 
possibly not existing may be a problem), not even saying that we should 
have this. I'm just showing a way to do the same thing with an easier 
(to me) syntax.



- Brice
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Python docs page: In what ways is None special

2018-08-14 Thread Brice Parent

Nice work, very usefull.

Is it interesting enough to note that the negation of None is True? 
(probably just because when it's casted to bool, it becomes False).


Also,  even if None is seen as the value for the lack of value, it is 
still hashable and can be used as a key to a dict (I'm not saying it's 
always good practice!):


>>> def f():
... pass
...
>>> {None: 5, not None: 6}
{None: 5, True: 6}
>>> {f(): 4}
{None: 4}

Also, I would probably have added an example of how (and why) to use a 
sentinel value for when None has a meaning other than [not provided] in 
a function's signature.



You'll see if any of these is worth integrating.

- Brice


Le 14/08/2018 à 12:28, Jonathan Fine a écrit :

Hi

I'm pleased to announce that I've completed the first draft of my
page. It's viewable on gitub.
https://github.com/jfine2358/py-jfine2358/blob/master/docs/none-is-special.md

To quote from that page:

This page arose from a thread on the python-ideas list. I thank Steve
Dower, Paul Moore, Steve D'Aprano, Chris Barker, David Mertz, Jörn
Heissler, Anthony Risinger, Michael Selik, Chris Angelico for their
contributions and encouragement.

Apologies for anyone I've missed. Comments either on python-ideas, or
perhaps better, by raising an issue on github.



___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Jump to function as an an alternative to call function

2018-08-17 Thread Brice Parent



Le 16/08/2018 à 20:34, Steven D'Aprano a écrit :

On Thu, Aug 16, 2018 at 10:31:28AM +0200, Brice Parent wrote:


If I understand well the need (I'm unsure I've had it myself), it would
be easier to be able to import the function in the active context, like
this:

def foo(a):
     return a + c

def bar(a, c):
     return foo(a)

def bazz(a, c):
     import __file__.foo
     return foo(a)

[...]

I'm not saying the syntax is the one that should be used (__file__
possibly not existing may be a problem), not even saying that we should
have this. I'm just showing a way to do the same thing with an easier
(to me) syntax.

The problem isn't that __file__ doesn't exist, it is that import DOES
exist and does something completely unsuitable:

[...]
That's what I was trying to say: I'm not proposing a syntax or any 
specific solution.
It was more about graphically explaining that I found more explicit 
(that word again...) to import (or copy, or insert) the target function 
inside the active scope than to have it done when you call the function 
itself.


___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Pre-conditions and post-conditions

2018-08-29 Thread Brice Parent
I've never used contracts, so excuse me if I didn't get how they would 
work, and what they should do.


The last example is about pre-post conditions in a class constructor. 
But I imagine this is not the only case where one would want to define 
contracts, like probably: within methods that return something and to 
functions (and in both cases, we'd want to contractually state some of 
the return's specificities).


Is the following function something someone used to work with contracts 
would write?


def calculate(first: int, second: int) -> float:
    def __require__():
    first > second
    second > 0
    # or first > second > 0 ?

    def __ensure__(ret):  # we automatically pass the return of the 
function to this one

    ret > 1

    return first / second

If so, having a reference to the function's output would probably be 
needed, as in the example above.


Also, wouldn't someone who use contracts want the type hints he provided 
to be ensured without having to add requirements like `type(first) is 
int` or something?


- Brice

Le 29/08/2018 à 07:52, Greg Ewing a écrit :

Wes Turner wrote:

    I'm going to re-write that in a pseudo-Eiffel like syntax:


Maybe some magic could be done to make this work:

 def __init__(self, img: np.ndarray, x: int, y: int, width: int,
 height: int) -> None:

 def __require__():
 x >= 0
 y >= 0
 width >= 0
 height >= 0
 x + width <= pqry.opencv.width_of(img)
 y + height <= pqry.opencv.height_of(img)

 def __ensure__():
 (self.x, self.y) in self
 (self.x + self.width - 1, self.y + self.height - 1) in self
 (self.x + self.width, self.y + self.height) not in self

 # body of __init__ goes here...



___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] A simple proposal concerning lambda

2018-08-22 Thread Brice Parent

Le 22/08/2018 à 04:12, MRAB a écrit :

On 2018-08-22 02:38, Elazar wrote:
I don't think this change makes sense, but if it's done, there should 
be another change, with actual implications:
There is no way to express the types of the parameters in a lambda - 
`lambda x: int : x` is obviously a syntax error. Replacing the colon 
with a different symbol, such as "=>" will make this possible:


     def x => x
     def x: int => x
     def x: int -> int => x

It will also give one less meaning to the colon.


The examples I showed had parens, so your examples could be:

    def (x): x
    def (x: int): x
    def (x: int) -> int: x
With or without allowing `def` to be an equivalent of `lambda`, I think 
this is a great idea.

To me, someone who understands the following 2 lines:

lambda x, y: x * y
def a(x: float, y: int) -> float: ...

would probably understand the following:

lambda(x: float, y: int) -> float: x * y

(more type hints, I like it!)

___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Idea: Deferred Default Arguments?

2018-07-20 Thread Brice Parent

It might be stupid, but how about solving this problem using the following:

from . import other_func, SomeClass

def my_func(a=other_func.defaults.a, b=other_func.defaults.b, 
c=SomeClass.some_method.defaults.c):

    ...

or
def my_func(a=None, b=None, c=None):  # or use some sentinel value 
instead of None

    if a is None:
    a = other_func.defaults.a
    if b is None:
    b = other_func.defaults.b
    if c is None:
    c = SomeClass.some_method.defaults.c
    ...

or even
def my_func(a=None, b=None, c=None):
    if a is None:
    a = default(other_func, "a")
    if b is None:
    b = default(other_func, "b")
    if c is None:
    c = default(SomeClass.some_method, "c")
 ...

I used *.defaults.* but it might be something else, as well as the 
function I named 'default' which might be anything else.


I prefer the first, as it's both short and easy to read, but I'm not 
sure about the implications about such a thing. And it probably has 
already been proposed for other use cases.
The second and third versions are more verbose, but probably easier to 
implement, specially the third which should already be doable using 
something like


import inspect

def default(function, argument):
    return inspect.signature(function).parameters[argument].default.
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] A better (simpler) approach to PEP 505

2018-07-23 Thread Brice Parent

Le 23/07/2018 à 18:00, David Mertz a écrit :
On Mon, Jul 23, 2018 at 11:26 AM Paul Moore > wrote:


* Library solution works with all versions of Python
* The need for unbox is a little ugly, but arguably less so than ?.
(conceded that's a subjective view)
* Mixing ?. and . is terser than unboxing and reboxing - but are there
any real examples where that's needed?
* Having the default at the beginning rather than at the end doesn't
follow natural reading order (but again that's pretty subjective)


On the last point, it would be easy enough to change the API to make 
it `NoneAware(obj).a.b.c.unbox(sentinel)` if that was thought a better 
API than `NoneAware(obj, sentinel).a.b.c.unbox()`.
Oh... and the production code should DEFINITELY spell 'sentinel' 
correctly if that's a part of the API. :-)


One of the good things about this proposal, is that it may be expanded 
to other sentinels:


ret = NoneAware(obj, sentinel=0).a.unbox(42)

# would be equivalent to
ret = obj.a if obj.a is not 0 else 42

# or, if tmp_a had side effects
tmp_a = obj.a
ret = tmp_a if tmp_a is not 0 else 42

One thing that this proposal doesn't cover easily (although I'd refactor 
the code before using this syntax, as excepted in some very specific 
cases like when using ORMs, I don't like chaining methods and properties 
like this):


favorite = cfg?.user.profile?.food ?? "Spam"  # every user should have a 
profile


favorite = NoneAware(cfg).user.profile.food.unbox("Spam") # We're 
silencing an exception that I didn't plan to silence


Another thing is that both parts are executed. If instead of "Spam" we 
had any variable with a side effect, it would have been executed even if 
cfg.user.profile.food existed. We're missing the lazy evaluation of this 
part here.


That being said, I deeply believe the use case for that are not spread 
enough to justify such a change. And I can see from here code where 
every '.' is prepended by a '?' just in case (the same way we encounter 
frequently exceptions catching for Exception).


___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] PEP 505: None-aware operators

2018-07-24 Thread Brice Parent

Le 24/07/2018 à 00:39, Chris Angelico a écrit :

On Tue, Jul 24, 2018 at 8:22 AM, Thomas Jollans  wrote:

...

What about:

5 < x < 10

Can you add parentheses to that to "make precedence and evaluation order clear"?
Correct me if I'm wrong, but to my knowledge, this is just a shorthand 
to `5 < x and x < 10`.


Making the precedence and evaluation order clear doesn't necessarily 
rely on adding parenthesis, it may rely on expanding the expression (you 
may add parenthesis after that, if you think it's needed).

___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] PEP 505: None-aware operators

2018-07-19 Thread Brice Parent

Hi,
I think this breaks one of the most important aspect which makes Python 
more and more popular every year: its readability. Until now, you didn't 
need to know the language very well to have some understanding about 
what a script did. Some parts of it could be a bit complicated (like 
comprehensions, which we only allow in unit tests and small utilities 
where I work, exactly for this reason) or non-obvious (like the new := 
operator, but even if you don't get exactly what it does, it's not 
really cryptic either. It's still some-kind-of-assignment).
We've been teaching for years that there is virtually no cost in adding 
a few simple lines of code from times to times, and that it has to be 
done to improve readability, while this proposal's goal seems to be the 
opposite (make the code more compact at the cost of readability), which 
complicates the understanding of the whole language to save a few `if x 
is None:`. To me, it goes the opposite of most of the first lines of the 
zen of Python.


Where I work, we update most of our projects to use new Python versions 
almost immediately when a new one is out and the libs we use are 
compatible. This would probably change that; we would likely wait for a 
few years, the time for the feature to be used widely in other projects, 
and for us to have some real-life feedback to know what to do about it, 
like: are there some cases it really is justified? Does it make it 
harder/simpler to read/write python code for experts and non-experts? 
Should we add a rule to enforce its use, or to prevent it?


The biggest drawback of this, is that (if I understand it well), it may 
be done quite easily without any change to the language:


def first_set(*elements):  # please don't mind the name of the function, 
it's not the purpose here

    """ Will return the first element that is not None """
    for element in elements:
    if element is not None:
    return element

    raise AllNoneException()

first_set(3, 5)  # -> 3
first_set(None, 5)  # -> 5
first_set(None, None, 8, 10)  # -> 8
first_set(None, Car(model="sport")).buy()  # calling 
Car(model="sport").buy()

first_set(None, ["a", "b", "c"])[1]  # -> "b"
first_set(None, None)  # -> exception is raised

(note that such function could even accept a "rejected_values" kwarg, 
like `rejected_values=(None, [], "")`, just by replacing the `if` clause 
by `if element not in rejected_values:`)


I might have missed some implications, or even not understood the PEP at 
all, though!


- Brice




___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] A better (simpler) approach to PEP 505

2018-07-25 Thread Brice Parent

Le 25/07/2018 à 08:49, Grégory Lielens a écrit :
BTW, I did (very quickly, so it's rough, my regexps are not really 
catching everything) the same or our code base:


"is None"+ "is not None": 5600
AttributeError: 160
3 args getattr: 60
hasattr: 1800

So very similar to your patternexcept for hasattr, which is much 
more common in my case...
(I'm not going to speak about how nice or ugly the syntax is, just about 
the use cases and statistics)


I think the use case here is not really the simple 'is  None' + 'is not 
None'.


if the case is just to replace one None value by something else, it's 
not really an improvement as we just save one short line, and once the 
new syntax is accepted and we're used to it, both solution are quite 
explicit with what they do, so I'd prefer the status quo over a second 
way of doing the same thing.


It gets interesting in json-like cases, when we traverse a deep tree in 
which we might encounter None at multiple levels. To me, the new 
functionality is better by than the status quo as it shortens the code 
drastically and makes it more readable (you may see the entire traversal 
at once).


So for the statistics, what's getting interesting is knowing when we are 
in that second case (but of course, it's harder to get them using simple 
regexes). Maybe we could find when there are more than one "is None" or 
"is not None" within the small block of code, and then manually check a 
few random dozens of those cases to see how many of them would be improved?
Another thing that would be interesting in those statistics, but 
probably even harder to get automatically, is the proportion of cases 
where there is a side effect involved (so the cases when we also need a 
temporary variable not to have the side effect executed twice). Those 
cases benefit more of the new syntax than from the old one, until 
there's a good solution with ':=' or a lazy evaluation syntax, or some 
function return caching from the caller side.
Also, it would be nice to know if those use cases come from specific 
libraries/functionalities (like json tree traversal or use of ORM) or 
from many different situations. In the first case, the improvement would 
probably belong inside the libraries themselves, or helpers could be 
created for this very purpose, and in the second case, if the problem is 
quite common, it gets interesting finding a solution inside Python 
itself, like what's described in this PEP.


I didn't automate those search in my codebase, I just searched for 'is 
None' and looked if it was complicated enough to justify a new syntax, 
and over 937 'is None', 43 were close to another 'is None', so I looked 
them up manually, and 13 could have benefited from the proposal, in the 
sense I described sooner, so more than 1 'is None' removed in a single 
line of code, and/or there was a side effect (I needed a temp variable 
not to have the side effect repeated). But I didn't check for side 
effects in the other 894 'is None', so this number should probably be a 
bit bigger.
Side note: we're not big JSON users, and when we do use some, we catch 
exceptions as it's never (here) normal not have well formed json 
strings, so the code never continues and we never have to use default 
values given by the backend.


___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Fixing class scope brainstorm

2018-03-29 Thread Brice Parent


Hm, so maybe we shouldn't touch lambda, but we can at least fix the 
scope issues for comprehensions and genexprs.


There may still be breakage, when the code defines a global x that is 
overridden by a class-level x, and a class-level comprehension 
references x assuming it to be the global. So we need to tread 
carefully even here -- but this case is weird already:


x = 42
class C:
    x = [1, 2, 3]
    z = [x+y for y in x]  # [43, 44, 45]


Wow!
I had to try it myself!

If I had came across something like the following in a code review :
x = [1, 2]
class C:
    x = [3, 4, 5]
    z = [x for _ in x]

I would have expected C.z to equal either `[[1, 2], [1, 2]]` or `[[3, 4, 
5], [3, 4, 5], [3, 4, 5]]`, but surely not `[[1, 2], [1, 2], [1, 2]]`!


Is that intentional, or the result of other way-more-logical decisions?

- Brice
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Add .= as a method return value assignment operator

2018-09-28 Thread Brice Parent


Le 27/09/2018 à 12:48, Ken Hilton a écrit :

Hi Jasper,
This seems like a great idea! It looks so much cleaner, too.

Would there be a dunder method handling this? Or since it's explicitly 
just a syntax for "obj = obj.method()" is that not necessary?
My only qualm is that this might get PHP users confused; that's really 
not an issue, though, since Python is not PHP.


Anyway, I fully support this idea.


What would the following evaluate to?
a .= b + c(d)

1: a = a.b + a.c(a.d)  # everything is prepended an "a."
it means we dn't have access to any external elements, making the 
functionality only useful in a few cases


2: a = a.b + a.c(d)  # every first level element (if that means 
something) is prepended an "a."
We still lose some of the ability to access anything outside of `a`, but 
a bit less than in #1. The effort to understand the line as grown a bit, 
though.


3: a = a.(b + c(d))  # everything is evaluated, and an "a." is prepended 
to that result

(the same way `a *= 2 + 3` is equivalent to `a *= 5`)
I believe in most cases, this wouldn't mean anything to evaluate `b + 
c(d)` on their own, and expect a return that can be used as an attribute 
of `a`.


4: a = a.b + c(d)  # "a." is prepended to the first element after the `=`
It is probably quite easy to read and understand, but it removes the 
transitivity of the operators we have on the right, and is a bit limiting.


5: SyntaxError: Can only use the [whatever the name] augmented operator 
with a single expression

Why not, it's a bit limiting, but is clear enough to me.

Maybe, a simpler thing to do for this problem would be to make something 
like this:

a = .b(5) + c(.d) + 3
being the equivalent of
a = a.b(5) + c(a.d) + 3

I don't see any ambiguity anymore, it shortens the code a lot, and I 
guess it wouldn't be hard for the compiler to recompose the line as a 
first parsing step, and create the same AST with both syntaxes.
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] "while:" for the loop

2018-09-26 Thread Brice Parent



Le 26/09/2018 à 05:36, Chris Angelico a écrit :

On Wed, Sep 26, 2018 at 1:29 PM Mikhail V  wrote:

On Wed, Sep 26, 2018 at 5:38 AM Chris Angelico  wrote:


I like saying while "something": where the string describes the loop's
real condition. For instance, while "moar data": if reading from a
socket, or while "not KeyboardInterrupt": if the loop is meant to be
halted by SIGINT.

ChrisA

if doing so, would not it be more practical
to write is as an in-line comment then?
with new syntax it could be like this:
"""
while:  # not KeyboardInterrupt
 asd asd asd
 asd asd asd
 asd asd asd
"""
Similar effect, but I would find it better at least because it would
be highlighted as a comment and not as a string, + no quotes noise.

A comment is not better than an inline condition, no. I *want* it to
be highlighted as part of the code, not as a comment. Because it isn't
a comment - it's a loop condition.
For what it's worth, I'm not a fan of either solutions. In both cases 
(string or comment), KeyboardInterrupt seems to be the only way to get 
out of the loop, which which even if it were the case, it would breaks 
the DRY idea, because if you add in the future a reason to break out of 
the loop, you'd have to write the condition+break and update the 
comment/string to still be consistent.


I don't think `while True:` is not explicit. If you think about it, True 
will always evaluate positively (right??), so it can't be the hardest 
part of learning Python.
But in some cases, mostly when I work on tiny microptyhon projects that 
I share with non-python experts, where I avoid complicated code 
fragments like comprehensions, I usually use `while "forever":` or 
`while FOREVER:` (where forever was set to True before). In these case, 
I don't need them to understand exactly why the loop is indeed infinite, 
I just want them to know it is. But all these solutions are available 
right now, without any syntax change.


About the original proposal, even though I'm not a native English 
speaker, writing `while:` seems like an wobbling sentence, we are 
waiting for it to be completed. My mind says "while what?" and tries to 
find out if it's infinite or if it has to find the condition elsewhere 
in the code (like some kind of do...until or do...while loop).


-Brice
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] __iter__(), keys(), and the mapping protocol

2018-09-13 Thread Brice Parent

Le 13/09/2018 à 10:07, Jonathan Fine a écrit :

Now for my opinions. (Yours might be different.)

First, it is my opinion that it is not reasonable to insist that the
argument after ** must be a mapping. All that is required to construct
a dictionary is a sequence of (key, value) pairs. The dict(iterable)
construction proves that point.

Second, relaxing the ** condition is useful. Consider the following.

 >>> class NS: pass
 >>> ns = NS()

 >>> ns.a = 3
 >>> ns.b = 5

 >>> ns.__dict__
 {'b': 5, 'a': 3}

 >>> def fn(**kwargs): return kwargs

 >>> fn(**ns)
 TypeError: fn() argument after ** must be a mapping, not NS

 >>> fn(**ns.__dict__)
 {'b': 5, 'a': 3}
I don't know about namespaces. It's probably been a concept I've often 
used without putting a name on it.

But for dataclasses, I'd find it quite useful to have
{**my_data_class}
be a shortcut to
{**dataclasses.asdict(my_data_class)}

It's most likely what we'd want to achieve by unpacking a dataclass (or 
at least, to my opinion). I'm not sure about the internals and the 
weight of such a feature, but I guess a toy implementation would just 
be, whenever we should raise a TypeError because the variable is not a 
mapping, to check whether it's a dataclass instance, and if so, call 
asdict on it, and return its result.

I'm not sure I'm not off-topic though...
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] PEP: Dict addition and subtraction

2019-03-06 Thread Brice Parent



Le 06/03/2019 à 13:53, Chris Angelico a écrit :

On Wed, Mar 6, 2019 at 11:18 PM Brice Parent  wrote:

The major implication to such a
modification of the Dict.update method, is that when you're using it
with keyword arguments (by opposition to passing another dict/iterable
as positional), you're making a small non-backward compatible change in
that if in some code, someone was already using the keyword that would
be chosing (here "on_collision"), their code would be broken by the new
feature.
Anyway, if
the keyword is slected wisely, the collision case will almost never
happen, and be quite easy to correct if it ever happened.

You can make it unlikely, yes, but I'd dispute "easy to correct".
Let's suppose that someone had indeed used the chosen keyword (and
remember, the more descriptive the argument name, the more likely that
it'll be useful elsewhere and therefore have a collision). How would
they discover this? If they're really lucky, there MIGHT be an
exception (if on_collision accepts only a handful of keywords, and the
collision isn't one of them), but if your new feature is sufficiently
flexible, that might not happen. There'll just be incorrect behaviour.

As APIs go, using specific keyword args at the same time as **kw is a
bit odd. Consider:

button_options.update(button_info, on_click=frobnicate, style="KDE",
on_collision="replace")

It's definitely not obvious which of those will end up in the
dictionary and which won't. Big -1 from me on that change.
That's indeed a good point. Even if the correction is quite easy to make 
in most cases. With keyword only changes:


button_options.update(dict(on_click=frobnicate, style="KDE", on_collision="replace"))  # or 
button_options.update(dict(on_collision="replace"), on_click=frobnicate, style="KDE")

In the exact case you proposed, it could become a 2-liners:

button_options.update(button_info)
button_options.update(dict(on_click=frobnicate, style="KDE", 
on_collision="replace"))

In my code, I would probably make it into 2 lines, to make clear that we 
have 2 levels of data merging, one that is general (the first), and one 
that is specific to this use-case (as it's hard written in the code), 
but not everyone doesn't care about the number of lines.


But for the other part of your message, I 100% agree with you. The main 
problem with such a change is not (to me) that it can break some edge 
cases, but that it would potentially break them silently. And that, I 
agree, is worth a big -1 I guess.

___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] PEP: Dict addition and subtraction

2019-03-06 Thread Brice Parent



Le 06/03/2019 à 10:50, Rémi Lapeyre a écrit :

Le 05/03/2019 à 23:40, Greg Ewing a écrit :

Steven D'Aprano wrote:

The question is, is [recursive merge] behaviour useful enough and
common enough to be built into dict itself?

I think not. It seems like just one possible way of merging
values out of many. I think it would be better to provide
a merge function or method that lets you specify a function
for merging values.


That's what this conversation led me to. I'm not against the addition
for the most general usage (and current PEP's describes the behaviour I
would expect before reading the doc), but for all other more specific
usages, where we intend any special or not-so-common behaviour, I'd go
with modifying Dict.update like this:

foo.update(bar, on_collision=updator) # Although I'm not a fan of the
keyword I used

This won’t be possible update() already takes keyword arguments:


foo = {}
bar = {'a': 1}
foo.update(bar, on_collision=lambda e: e)
foo

{'a': 1, 'on_collision':  at 0x10b8df598>}

I don't see that as a problem at all.
Having a function's signature containing a **kwargs doesn't disable to 
have explicit keyword arguments at the same time:
`def foo(bar="baz", **kwargs):` is perfectly valid, as well as `def 
spam(ham: Dict, eggs="blah", **kwargs):`, so `update(other, 
on_collision=None, **added) is too, no? The major implication to such a 
modification of the Dict.update method, is that when you're using it 
with keyword arguments (by opposition to passing another dict/iterable 
as positional), you're making a small non-backward compatible change in 
that if in some code, someone was already using the keyword that would 
be chosing (here "on_collision"), their code would be broken by the new 
feature.
I had never tried to pass a dict and kw arguments together, as it seemed 
to me that it wasn't supported (I would even have expected an exception 
to be raised), but it's probably my level of English that isn't high 
enough to get it right, or this part of the doc that doesn't describe 
well the full possible usage of the method (see here: 
https://docs.python.org/3/library/stdtypes.html#dict.update). Anyway, if 
the keyword is slected wisely, the collision case will almost never 
happen, and be quite easy to correct if it ever happened.


___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] PEP: Dict addition and subtraction

2019-03-06 Thread Brice Parent


Le 05/03/2019 à 23:40, Greg Ewing a écrit :

Steven D'Aprano wrote:

The question is, is [recursive merge] behaviour useful enough and

> common enough to be built into dict itself?

I think not. It seems like just one possible way of merging
values out of many. I think it would be better to provide
a merge function or method that lets you specify a function
for merging values.

That's what this conversation led me to. I'm not against the addition 
for the most general usage (and current PEP's describes the behaviour I 
would expect before reading the doc), but for all other more specific 
usages, where we intend any special or not-so-common behaviour, I'd go 
with modifying Dict.update like this:


foo.update(bar, on_collision=updator)  # Although I'm not a fan of the 
keyword I used


`updator` being a simple function like this one:

def updator(updated, updator, key) -> Any:
    if key == "related":
    return updated[key].update(updator[key])

    if key == "tags":
    return updated[key] + updator[key]

    if key in ["a", "b", "c"]:  # Those
    return updated[key]

    return updator[key]

There's nothing here that couldn't be made today by using a custom 
update function, but leaving the burden of checking for values that are 
in both and actually inserting the new values to Python's language, and 
keeping on our side only the parts that are specific to our use case, 
makes in my opinion the code more readable, with fewer possible bugs and 
possibly better optimization.



___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-06 Thread Brice Parent
Why not simply propose an external lib with FluentDict and other 
Fluent[Anything] already packaged? I don't know if I'd use it 
personnally, but it definitely could have some users.



Le 05/03/2019 à 09:48, Jonathan Fine a écrit :

SUMMARY
Instead of using dict + dict, perhaps use dict.flow_update. Here,
flow_update is just like update, except that it returns self.

BACKGROUND
There's a difference between a sorted copy of a list, and sorting the
list in place.

 >>> items = [2, 0, 1, 9]
 >>> sorted(items), items
 ([0, 1, 2, 9], [2, 0, 1, 9])
 >>> items.sort(), items
(None, [0, 1, 2, 9])

In Python, mutating methods generally return None. Here, this prevents
beginners thinking their code has produced a sorted copy of a list,
when in fact it has done an in-place sort on the list. If they write
 >>> aaa = my_list.sort()
they'll get a None error when they use aaa.

The same goes for dict.update. This is a useful feature, particularly
for beginners. It helps them think clearly, and express themselves
clearly.

THE PROBLEM
This returning None can be a nuisance, sometimes. Suppose we have a
dictionary of default values, and a dictionary of use supplied
options. We wish to combine the two dictionaries, say into a new
combined dictionary.

One way to do this is:

combined = defaults.copy()
combined.update(options)

But this is awkward when you're in the middle of calling a function:

   call_big_method(
   # lots of arguments, one to a line, with comments
   arg = combined, # Look up to see what combined is.
  # more arguments
 )

USING +
There's a suggestion, that instead one extends Python so that this works:
 arg = defaults + options # What does '+' mean here?

USING flow_update
Here's another suggestion. Instead write:
 dict_arg = defaults.copy().flow_update(options) # Is this clearer?

IMPLEMENTATION
Here's an implementation, as a subclass of dict.

 class mydict(dict):

 def flow_update(self, *argv, **kwargs):
 self.update(*argv, **kwargs)
 return self

 def copy(self):
 return self.__class__(self)

A DIRTY HACK
Not tested, using an assignment expression.
dict_arg = (tmp := defaults.copy(), tmp.update(options))[0]
Not recommend.



___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Sorted lists

2019-04-10 Thread Brice Parent

It surely would be helpful. I still find it a bit too single-case oriented.

Maybe having an equivalent __sorting_algo__ property with a value of the 
current sorting algorithm would be more general?
There could be a SortingAlgo base class, which could be extended into 
classes like:
 - SortingAlgoNone(SortingAlgo) or SortingAlgoUnsorted(SortingAlgo) 
which would be the default for non-sorted lists (or just the value None)

 - SortingAlgoAscending(SortingAlgo)
 - SortingAlgoAscendingNumbers(SortingAlgoAscending)
 - MyCustomSortingAlgo(SortingAlgo)
 - ...
It would allow to mark a list as sorted with any algorithm, and of 
course, any code that would use these lists would be able to read/write 
this __sorting_algo__.


And complementary idea, we could have an extra arg to sort() (and other 
functions like this one) like `trust_declared_algo=True`, that if set 
would only sort the list if its list.__sorting_algo__ is compatible (a 
subclass of the sorting algo it uses, or the class itself).


The rest of the behaviours (when the __sorting_algo__ would be set or 
reset) would be as described by Steven in the original proposal.


-Brice


Le 8/4/19 à 4:32, Steven D'Aprano a écrit :

There are quite a few important algorithms which require lists to be
sorted. For example, the bisect module, and for statistics median and
other quantiles.

Sorting a list is potentially expensive: while Timsort is very
efficient, it is still ultimately an O(N log N) algorithm which limits
how efficient it can be. Checking whether a list is sorted is O(N).

What if we could check that lists were sorted in constant time?

Proposal: let's give lists a dunder flag, __issorted__, that tracks
whether the list is definitely sorted or not:

- Empty lists, or lists with a single item, are created with
   __issorted__ = True; lists with two or more items are created
   with the flag set to False.

- Appending or inserting items sets the flag to False.

- Deleting or popping items doesn't change the flag.

- Reversing the list doesn't change the flag.

- Sorting it sets the flag to True. (The sort method should NOT
   assume the list is sorted just because the flag is set.)

Functions that require the list to be sorted can use the flag as a
quick check:

 if not alist.__issorted__:
 alist.sort()
 ...

The flag will be writable, so that functions such as those in
bisect can mark that they have kept the sorted invariant:


 bisect.insort(alist, x)
 assert alist.__issorted__


Being writable, the flag is advisory, not a guarantee, and "consenting
adults" applies. People can misuse the flag:

 alist = [1, 4, 2, 0, 5]
 alist.__issorted__ = True

but then they have nobody to blame but themselves if they shoot
themselves in the foot. That's no worse than the situation we have now,
were you might pass an unsorted list to bisect.

The flag doesn't guarantee that the list is sorted the way you want
(e.g. biggest to smallest, by some key, etc) only that it has been
sorted. Its up to the user to ensure they sort it the right way:

 # Don't do this and expect it to work!
 alist.sort(key=random.random)
 bisect.insort(alist, 1)


If you really want to be sure about the state of the list, you have to
make a copy and sort it. But that's no different from the situation
right now. But for those willing to assume "consenting adults", you
might trust the flag and avoid sorting.

Downsides:

- Every list grows an extra attribute; however, given that lists are
   already quite big data structures and are often over-allocated, I
   don't think this will matter much.

- insert(), append(), extend(), __setitem__() will be a tiny bit
   slower due to the need to set the flag.



Thoughts?





___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/