Some "pythonic" suggestions for Python

  • Thread starter Frank Samuelson
  • Start date
F

Frank Samuelson

I love Python, and it is one of my 2 favorite
languages. I would suggest that Python steal some
aspects of the S language.

-------------------------------------------------------
1. Currently in Python
def foo(x,y): ...
assigns the name foo to a function object. Is this pythonic?

Why not use the = operator like most other assignments?
Define function objects as "function"s, let users put them where
they want to. Get rid of lambda, get rid of def, only use =
for assignments.

foo = function(x,y) x+y*2 # Example S language code
bar = foo
bar(3,4)
m = lapply( s, foo )
bb = lapply(s, function(t) t[3]*4 )

foo = func(x,y): x+y*2 # Possible python code
bar = foo
bar(3,4)
m = barf( s, foo )
bb = barf(s, func(t): t[3]*4 )

-------------------------------------------------------
2. Allow sequences to be indices:
>>> s=["hello", 6, 33, "none"]
>>> x= [1,3]
>>> [ s[y] for y in x] # Current verbose version [6, 'none']
>>> s[x] # Simpler, clearer, more productive

To quote a poster at http://www.thescripts.com/forum/thread22741.html,
"While we are at it, I also don't understand why sequences can't be
used as indices. Why not, say, l[[2,3]] or l[(2, 3)]? Why a special
slice concept? " Isn't that unpythonic?

--------------------------------------------------------
3. When I first started using python, I frequently used
map, because I didn't want to have to learn the
additional syntax of list comprehensions, which
appeared very nonstructured.

# Is this readable?
b= [x+y for x in vec1 if x>0 for y in vec2 if y>x ]

Perhaps a list comprehension syntax more like the rest
of python. "for" could return a list given by continue
arguments:

b= for x in vec1 :
if (x>0): continue # "returns" nothing
continue for y in vec2:
if (x>y): continue(x+y)

Note that my code would actually return a list of lists
rather than a single list like the list comprehension.
More structured syntax opens the door to having much
more complicated, yet still comprehensible (thus more
pythonic), list comprehensions.

--------------------------------------------------------

I know these ideas are not perfect, but I think they
may be better... Fire away.

-Frank
 
R

Raymond Hettinger

def foo(x,y): ...
assigns the name foo to a function object.

Why not use the = operator like most other assignments?

FWIW, the also binds the __name__ attribute:

foo = lambda(x,y): ...
foo.__name__ = 'foo'


Raymond
 
D

Diez B. Roggisch

Frank said:
I love Python, and it is one of my 2 favorite
languages. I would suggest that Python steal some
aspects of the S language.

-------------------------------------------------------
1. Currently in Python
def foo(x,y): ...
assigns the name foo to a function object. Is this pythonic?

Why not use the = operator like most other assignments?
Define function objects as "function"s, let users put them where
they want to. Get rid of lambda, get rid of def, only use =
for assignments.

foo = function(x,y) x+y*2 # Example S language code
bar = foo
bar(3,4)
m = lapply( s, foo )
bb = lapply(s, function(t) t[3]*4 )

foo = func(x,y): x+y*2 # Possible python code
bar = foo
bar(3,4)
m = barf( s, foo )
bb = barf(s, func(t): t[3]*4 )

The whole purpose being that you have statements in lambdas - something
that has been proposed and rejected about a bazillion times. Read the
archives.

-------------------------------------------------------
2. Allow sequences to be indices:
s=["hello", 6, 33, "none"]
x= [1,3]
[ s[y] for y in x] # Current verbose version [6, 'none']
s[x] # Simpler, clearer, more productive

To quote a poster at http://www.thescripts.com/forum/thread22741.html,
"While we are at it, I also don't understand why sequences can't be
used as indices. Why not, say, l[[2,3]] or l[(2, 3)]? Why a special
slice concept? " Isn't that unpythonic?


What has the one to do with the other? Slices are use- and powerful.

I'm not against this, but then what you call "verbose" is very concise
in my book - and concise enough to be used when the need arises, which
is seldom enough.
--------------------------------------------------------
3. When I first started using python, I frequently used
map, because I didn't want to have to learn the
additional syntax of list comprehensions, which
appeared very nonstructured.

# Is this readable?
b= [x+y for x in vec1 if x>0 for y in vec2 if y>x ]


Yes, I think it is readable. You could ask the same for the
parenthesis-overload in lisp - after all, it's just what you are
accustomed to.

Perhaps a list comprehension syntax more like the rest
of python. "for" could return a list given by continue
arguments:

b= for x in vec1 :
if (x>0): continue # "returns" nothing
continue for y in vec2:
if (x>y): continue(x+y)

Note that my code would actually return a list of lists
rather than a single list like the list comprehension.
More structured syntax opens the door to having much
more complicated, yet still comprehensible (thus more
pythonic), list comprehensions.

So it is _not_ a list comprehension, but all it does is to create
implicit lists?

I prefer list-comps. They allow for nested as well as flattened structures:

b= [x+y for x in vec1 if x>0 for y in vec2 if y>x ]
b= [[x+y for y in vec2 if y>x ] for x in vec1 if x>0]

Overall, I'd say you don't stand a chance that your proposals will be
adopted. They are minor variations of things that have been proposed &
rejected too often to count - and to be honest: it get's tiresome
beating the same old horses again and again...

Diez
 
B

Ben Finney

Frank Samuelson said:
I love Python, and it is one of my 2 favorite
languages. I would suggest that Python steal some
aspects of the S language.

I would suggest each of these be discussed in a separate thread, each
with a specific Subject field, rather than three loosely-related
questions in a single thread.
 
B

Bruno Desthuilliers

Frank Samuelson a écrit :
I love Python, and it is one of my 2 favorite
languages. I would suggest that Python steal some
aspects of the S language.

Yes. Python deliberately choosed to be a statement-based language.
Why not use the = operator like most other assignments?

This dead horse has been beaten to hell and back.

Note that as far as I'm concerned, I may like an expression-based
Python-inspired language. But this is another story.
-------------------------------------------------------
2. Allow sequences to be indices:
s=["hello", 6, 33, "none"]
x= [1,3]
[ s[y] for y in x] # Current verbose version

Verbose ???
[6, 'none']
s[x] # Simpler, clearer, more productive

To quote a poster at http://www.thescripts.com/forum/thread22741.html,
"While we are at it, I also don't understand why sequences can't be
used as indices. Why not, say, l[[2,3]] or l[(2, 3)]? Why a special
slice concept?

slices are very powerful.
" Isn't that unpythonic?

Not IMHO. But extending slices to support might not be totally stupid.
Do you volunteer to write the PEP and the patch ?
--------------------------------------------------------
3. When I first started using python, I frequently used
map, because I didn't want to have to learn the
additional syntax of list comprehensions, which
appeared very nonstructured.

# Is this readable?
b= [x+y for x in vec1 if x>0 for y in vec2 if y>x ]
Yes.

Perhaps a list comprehension syntax more like the rest
of python. "for" could return a list given by continue
arguments:

b= for x in vec1 :
if (x>0): continue # "returns" nothing
continue for y in vec2:
if (x>y): continue(x+y)

Now *this* is a mess. And FWIW, you inverted the second test...
Note that my code would actually return a list of lists
rather than a single list like the list comprehension.
More structured syntax opens the door to having much
more complicated, yet still comprehensible (thus more
pythonic), list comprehensions.

The Pythonic way to write "more complicated yet still comprehensible
list comprehensions" is to not use list comprehensions.

b = []
for x in vec1:
if x > 0:
for y in vec2:
if y > x:
b.append(x + y)

Or if you really want a more functional approach:

def do_stuff(vec1, vec2):
for x in vec1:
if x > 0:
for y in vec2:
if y > x:
yield x + y

b = list(do_stuff(vec1, vec2))
 
S

Steven D'Aprano

1. Currently in Python
def foo(x,y): ...
assigns the name foo to a function object. Is this pythonic?

Why not use the = operator like most other assignments?

Why? What benefit do you gain?
Define function objects as "function"s, let users put them where they
want to. Get rid of lambda, get rid of def, only use = for assignments.

So you remove two keywords. That's a plus. But then you have to create a
WHOLE lot more syntax to support it, and that's about a thousand minuses.
Python has very little syntax, and the designers are (rightly) very
resistant to adding more.

foo = function(x,y) x+y*2 # Example S language code

This would currently be a syntax error, hence the need for new syntax.

Also, function() couldn't be a normal type, or even a built-in function,
because it has to have special syntax:

name = function(*argument_list) expression

It's also not clear how you expect this to work with anything more
complex than a single expression. How do you handle statements and
multiple returns?

def foo(x, y):
L = []
try:
if x[y] % 2:
print x, y
return y
return x[y]
except:
return None




bar = foo

But you can still do this with Python:

def foo(x, y):
return x+y*2
bar = foo




2. Allow sequences to be indices:
s=["hello", 6, 33, "none"]
x= [1,3]
[ s[y] for y in x] # Current verbose version
[6, 'none']

I don't especially call it verbose.

s[x] # Simpler, clearer, more productive

It's certainly smaller.

But the fatal objection to this is that it is a special case for a very
limited benefit: saving half a dozen characters. List comprehensions are
very flexible:

[float(s[3*i % 2]) for i in x if 3 <= i < 12] # for example...

Your proposal doesn't have anywhere near that flexibility. It simply
duplicates perhaps the most trivial list comp [s[y] for y in x], just to
save a dozen or so characters. Python doesn't treat terseness as that big
a virtue.

Besides, if you want this behaviour, you can add it yourself:

class mylist(list):
# Untested!
def __getitem__(self, index):
if type(index) is list:
return [self for i in index]
return super(mylist, self).__getitem__(index)

list = mylist


The only difference is you have to write:

s = list([1,2,3])

instead of s = [1,2,3].


To quote a poster at http://www.thescripts.com/forum/thread22741.html,
"While we are at it, I also don't understand why sequences can't be used
as indices. Why not, say, l[[2,3]] or l[(2, 3)]? Why a special slice
concept? " Isn't that unpythonic?


But can't you see that the suggestion to use sequences as replacements
for slices is completely incompatible with your suggestion above?

seq = range(10)

Would you expect seq[[2,6]] to do an index lookup, as you suggested
originally, or a slice? In the first case, it would return [2, 6], but in
the second, it would return [2, 3, 4, 5].

If sequence indices are slices, what does an index of [1, 2, 3, 4] mean?


3. When I first
started using python, I frequently used map, because I didn't want to
have to learn the additional syntax of list comprehensions, which
appeared very nonstructured.

It is a slightly more verbose form of standard mathematical set notation.

In case it comes out wrong, the symbol ϵ is supposed to be the symbol for
"element".

"the set of 3x+1 such that x is in (1, 2, 3) if x is odd"

{ 3x+1 : x ϵ (1, 2, 3) if x%2 }

becomes the list comprehension:

[ 3*x+1 for x in (1, 2, 3) if x%2 ]

# Is this readable?
b= [x+y for x in vec1 if x>0 for y in vec2 if y>x ]

It is probably pushing the boundaries of how readable a list comp can get
before it should be refactored, but it hasn't crossed over into
unreadability.

Perhaps a list comprehension syntax more like the rest of python.

I think the great majority Python folks find the list comprehension
syntax to be one of the MOST Pythonic features, not the least.
 
S

Scott David Daniels

Frank said:
I love Python, and it is one of my 2 favorite
languages. I would suggest that Python steal some
aspects of the S language.

I generally agree with the various naye-sayers, but find one
argument missing:
-------------------------------------------------------
2. Allow sequences to be indices:
s=["hello", 6, 33, "none"]
x= [1,3]
[ s[y] for y in x] # Current verbose version [6, 'none']
s[x] # Simpler, clearer, more productive

To quote a poster at http://www.thescripts.com/forum/thread22741.html,
"While we are at it, I also don't understand why sequences can't be
used as indices. Why not, say, l[[2,3]] or l[(2, 3)]? Why a special
slice concept? " Isn't that unpythonic?

Right now, after:

which_corner = {}
corner = {}
for n, position in enumerate([(1,1), (1,5), (3,5), (3,1)]):
corner[n] = position
which_corner[position] = n

which_corner[1,5] returns 1

I would hate to have to know whether which_corner is a dictionary
or a list before I can decide whether this is iteration.

-Scott David Daniels
Scott,[email protected]
 
D

Duncan Booth

Scott David Daniels said:
To quote a poster at http://www.thescripts.com/forum/thread22741.html,
"While we are at it, I also don't understand why sequences can't be
used as indices. Why not, say, l[[2,3]] or l[(2, 3)]? Why a special
slice concept? " Isn't that unpythonic?

Right now, after:

which_corner = {}
corner = {}
for n, position in enumerate([(1,1), (1,5), (3,5), (3,1)]):
corner[n] = position
which_corner[position] = n

which_corner[1,5] returns 1

I would hate to have to know whether which_corner is a dictionary
or a list before I can decide whether this is iteration.

Quite so, tuples are currently a valid and indeed commonly used type for
subscripts, so if there was going to be special treatment it would have
to be for lists only and not sequences in general.

Alternatively, and more pythonically (explicit is better than implicit),
we could have a special syntax to indicate that we want to use each
element of the sequence as a separate subscript. I'd like to propose
that the special syntax be:

[s[y] for y in x]

Now someone go and fire up the time machine.
 
D

Duncan Booth

Steven D'Aprano said:
Besides, if you want this behaviour, you can add it yourself:

class mylist(list):
# Untested!
def __getitem__(self, index):
if type(index) is list:
return [self for i in index]
return super(mylist, self).__getitem__(index)

list = mylist


It works very nicely, just don't try passing it a recursive list.

:)
 
F

Frank Samuelson

Steven said:
Why? What benefit do you gain?


So you remove two keywords. That's a plus. But then you have to create a
WHOLE lot more syntax to support it, and that's about a thousand minuses.
Python has very little syntax, and the designers are (rightly) very
resistant to adding more.

You answered your own question. There is less syntax.
It's also not clear how you expect this to work with anything more
complex than a single expression. How do you handle statements and
multiple returns?
>
def foo(x, y):
L = []
try:
if x[y] % 2:
print x, y
return y
return x[y]
except:
return None

Huh? This is trivial. I don't see why this is so hard to grasp.

foo= function(x, y):
L = []
try:
if x[y] % 2:
print x, y
return y
return x[y]
except:
return None

s[x] # Simpler, clearer, more productive

It's certainly smaller.

But the fatal objection to this is that it is a special case for a very
limited benefit: saving half a dozen characters.

It sounds like you are describing slices here. It is interesting
how python people don't like specific case syntax, unless it applies
to their already existing specific cases. Lists and tuples represent
any sequence that slices can. Why slices?
But can't you see that the suggestion to use sequences as replacements
for slices is completely incompatible with your suggestion above?

seq = range(10)

Would you expect seq[[2,6]] to do an index lookup, as you suggested
originally, or a slice? In the first case, it would return [2, 6], but in
the second, it would return [2, 3, 4, 5].

If sequence indices are slices, what does an index of [1, 2, 3, 4] mean?

It means whatever you decide it to mean. Make a definition and stick with it.
 
D

Duncan Booth

Frank Samuelson said:
It's also not clear how you expect this to work with anything more
complex than a single expression. How do you handle statements and
multiple returns?

def foo(x, y):
L = []
try:
if x[y] % 2:
print x, y
return y
return x[y]
except:
return None

Huh? This is trivial. I don't see why this is so hard to grasp.

foo= function(x, y):
L = []
try:
if x[y] % 2:
print x, y
return y
return x[y]
except:
return None
It is hard to grasp because you said you wanted:

name = function(*argument_list) expression

There is no colon in your proposed syntax, and you only asked for a
single expression in the function rather than a suite. Your 'this is
trivial' response seems to be proposing another syntax entirely.

Unfortunately my crystal ball is away being cleaned, so I am unable to
guess whether you meant for a function definition to be an expression
(opening up a whole host of questions about how you nest it inside other
expressions) or a special case of an assignment to a single name and
wanted to disallow such things as:

foo = l[3] = function(): pass

or if you didn't mean to disallow them, what name should that function
have.

If you are going to make syntax proposals you must be precise.
 
F

Frank Samuelson

Bruno said:
Yes. Python deliberately choosed to be a statement-based language.


This dead horse has been beaten to hell and back.

Note that as far as I'm concerned, I may like an expression-based
Python-inspired language. But this is another story.

My bad! In my rush I forgot that python requires the return statements.
You people think I'm trying to push lisp, which I am not. (Apparently
many people do, because you are rather skittish.)
In S the value of the last executed statement of a function is the
returned value of the function by default, if there is no return() function.
It is very convenient for writing really small functions. But skip that for now.

foo = function(x,y) { # S language
if (x>2) return(x+y*2)
x+y
}

foo = func(x,y): # My suggested python syntax (Improved!)
if x>2: return x+y*2
return x+y

# put the function object in the code wherever you want it.
bb= bar( a,b, func(x,y): x=x*2 ; b=4 ; return x*b+y )

It still is a statement language. The statement is an explicit
assignment using the "=" like most other assignments. "Def" and "lambda"
are silly. Assign names to function objects names the same way
you assign them to any other object. Call a duck a duck.

While I'm at it, classes are objects too. Assign names to them the
same way you assign names to _any_ other object: "="

MyClass= class(inhereted):
attributes
functions
whatever

x=MyClass() # an object of the above class.


The Pythonic way to write "more complicated yet still comprehensible
list comprehensions" is to not use list comprehensions.

b = []
for x in vec1:
if x > 0:
for y in vec2:
if y > x:
b.append(x + y)

You have just demonstrated that list comprehensions are not really
necessary at all. So given the "one clear way of doing things",
shouldn't they be removed? My point is that if you are going to
have them, why make some new unstructured syntax for them?
 
C

Chris Mellon

Bruno said:
Yes. Python deliberately choosed to be a statement-based language.


This dead horse has been beaten to hell and back.

Note that as far as I'm concerned, I may like an expression-based
Python-inspired language. But this is another story.

My bad! In my rush I forgot that python requires the return statements.
You people think I'm trying to push lisp, which I am not. (Apparently
many people do, because you are rather skittish.)
In S the value of the last executed statement of a function is the
returned value of the function by default, if there is no return() function.
It is very convenient for writing really small functions. But skip that for now.

foo = function(x,y) { # S language
if (x>2) return(x+y*2)
x+y
}

foo = func(x,y): # My suggested python syntax (Improved!)
if x>2: return x+y*2
return x+y

# put the function object in the code wherever you want it.
bb= bar( a,b, func(x,y): x=x*2 ; b=4 ; return x*b+y )

It still is a statement language. The statement is an explicit
assignment using the "=" like most other assignments. "Def" and "lambda"
are silly. Assign names to function objects names the same way
you assign them to any other object. Call a duck a duck.

While I'm at it, classes are objects too. Assign names to them the
same way you assign names to _any_ other object: "="

MyClass= class(inhereted):
attributes
functions
whatever

x=MyClass() # an object of the above class.


The Pythonic way to write "more complicated yet still comprehensible
list comprehensions" is to not use list comprehensions.

b = []
for x in vec1:
if x > 0:
for y in vec2:
if y > x:
b.append(x + y)

You have just demonstrated that list comprehensions are not really
necessary at all. So given the "one clear way of doing things",
shouldn't they be removed? My point is that if you are going to
have them, why make some new unstructured syntax for them?

There are at least 2 posts a month by someone who decides that they
want to re-wire Python syntax. Usually it's because of some particular
idiom they're used to in another language, in other cases it's because
they've got some particular issue with "consistency". The ideas are
*never* fully thought out or materialized, and they invariably invite
scorn from the user community. The poster is almost always a Python
beginner (I don't know if thats true in your case or not).

Arbitrary changes to syntax are never going to fly. It's a lost cause.
If you can't handle Python without your pet changes, fork it and write
your own version and let the marketplace of ideas decide if its
useful.
 
C

Carl Banks

My bad! In my rush I forgot that python requires the return statements.
You people think I'm trying to push lisp, which I am not. (Apparently
many people do, because you are rather skittish.)
In S the value of the last executed statement of a function is the
returned value of the function by default, if there is no return() function.
It is very convenient for writing really small functions. But skip that for now.

foo = function(x,y) { # S language
if (x>2) return(x+y*2)
x+y

}

foo = func(x,y): # My suggested python syntax (Improved!)
if x>2: return x+y*2
return x+y

# put the function object in the code wherever you want it.
bb= bar( a,b, func(x,y): x=x*2 ; b=4 ; return x*b+y )

It still is a statement language. The statement is an explicit
assignment using the "=" like most other assignments. "Def" and "lambda"
are silly. Assign names to function objects names the same way
you assign them to any other object. Call a duck a duck.


If you do that, then when your pure, duck-called function results in a
raised exception, your stack trace will look like this:

File "xxx.py", line yy

instead of the much more helpful

File "xxx.py", line yy, in foo

because the function will be nameless. For me, this is more than
enough to justify a special assignment syntax for function. (In fact,
I'd say there are at least three reasons that are good enough by
themselves, but this'll do for now.)

More generally: your proposals are not "Pythonic", and will get very
little support in the Python community, because the mentality "If it's
good for X, it's good for Y" isn't Pythonic. Python is about a
practical balance of many concerns, and all decisions are considered
from many viewpoints. An argument for consistency for the sake of
keeping things simple is only one of many factors considered.


Carl Banks
 
L

Loic Mahe

Frank Samuelson a écrit :
foo = function(x,y) x+y*2 # Example S language code
bar = foo
bar(3,4)
m = lapply( s, foo )
bb = lapply(s, function(t) t[3]*4 )

Here you want all functions to be lambda functions:

you can get something very close to what you want,
just like this:

foo = lambda x,y: x+y*2
bar = foo
bar(3,4)

you would only need to extend existing lambda:
* support all python keywords/expressions in lambda
* multiline lambda
* multi return value lambda
* and possibility to map 'function' name to be equivalent to 'lambda'


Loic
 
C

Chris M

Multi-return value lambda? Just so you know, there is no concept of
returning more than one value from a function.

def a(): return 1, 2

returns one value - a tuple of (1, 2).

lambda: (1, 2)

does the same thing.
 
F

Frank Samuelson

So you like my ideas too!
There are at least 2 posts a month by someone who decides that they
want to re-wire Python syntax. Usually it's because of some particular
idiom they're used to in another language,

And python does not use idioms from other languages?
in other cases it's because
they've got some particular issue with "consistency".

My impression was that "consistency" was important to Python.
"Consistency" improves my productivity because I don't have to
keep referring to the manual. Things work the way I expect them
to work.
The ideas are
*never* fully thought out or materialized, and they invariably invite
scorn from the user community.

Of course, they're thought out: They're stolen from another language.
Specifically, the language in which I am most productive.
The poster is almost always a Python
beginner (I don't know if thats true in your case or not).

Only a couple years at it, but that is true of all of the languages
that I know, I guess...
Arbitrary changes to syntax are never going to fly. It's a lost cause.

The changes are not arbitrary. They are logical, consistent, less
arbitrary and thus more productive. If such
changes are a lost cause, that is too bad, because
it implies that Python will stagnate. Unfortunately that appears the case.
Though backward compatibility is not an issue (3.0 breaks stuff), I have
learned that there are many pythonistas who make up lots of arbitrary
reasons not to change anything, even if it is for the better.
If you can't handle Python without your pet changes, fork it and write
your own version and let the marketplace of ideas decide if its
useful.

Apparently you missed my statement about loving Python. I love it
because it is the second most productive language I have ever used,
though I do believe it has the potential to be the greatest ever by
far.
 
M

Marc 'BlackJack' Rintsch

And python does not use idioms from other languages?

It does. So what? Does that mean any idiom from any other language makes
sense in Python?
My impression was that "consistency" was important to Python.

It is but to quote a headline from the Python style guide: `A Foolish
Consistency is the Hobgoblin of Little Minds`.
"Consistency" improves my productivity because I don't have to keep
referring to the manual. Things work the way I expect them to work.

I expect meaningful function names in tracebacks.
Of course, they're thought out: They're stolen from another language.
Specifically, the language in which I am most productive.

They are not thought out. You just ripped an aspect from that other
language and threw it into a Python environment. This doesn't mean it
will fit into the language or scales beyond the small toy examples. What
about function names in tracebacks? What about nesting these anonymous
multiline functions? What about the impact on the grammar?

Ciao,
Marc 'BlackJack' Rintsch
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top