Re: Pythonic style

2020-09-23 Thread Stavros Macrakis
Thanks, Chris, for the useful comments. Comments in line (with snipping of
unnecessary content):


> > some more variants which *don't* use tuple unpacking, on the theory
> that the coding patterns may be useful in
> > other cases where unpacking doesn't apply.
>
> When doesn't it apply? Can you elaborate on this? It might be easier
> to advise on Pythonic style when the specific requirements are known.
>

No specific requirement. These are *finger exercises* intended to isolate
one issue for discussion rather than be useful in themselves.


> > ...but there are ways to avoid the verbose exception handling.
> In Python, exception handling IS the way to do these things.


Good to know. Since *try* statements are at least 4 lines long, I was
trying for something more concise. And maybe somewhere in the back of my
mind, I have the anti-Pythonic principle that exceptions should be reserved
for errors. I'll try to suppress that. I remember one (very old) paper

arguing that exceptions were a bad idea in general... but I have
implemented and used exceptions in many languages, both before and after
that paper!


> > _uniq = []
> > def firstg(iterable):
> > it = iter(iterable)
> > val0 = next(it,_uniq)
> > val1 = next(it,_uniq)
> > if val0 is not _uniq and val1 is _uniq:
> > return val0
> > else:
> > raise ValueError("first1: arg not exactly 1 long")
> >
> > But I don't know if the *_uniq* technique is considered Pythonic.
>
> It is when it's needed, but a more common way to write this would be
> to have the sentinel be local to the function (since it doesn't need
> to be an argument):
>

What do you mean by an argument?

Is it not considered Pythonic for constants to be calculated once at the
module or class level rather than be recalculated every time a function is
entered?

def firstg_variant(iterable):
> it = iter(iterable)
> sentinel = object()
> first = next(it, sentinel)
> if first is sentinel:
> raise ValueError("empty iterable")
> second = next(it, sentinel)
> if second is not sentinel:
> raise ValueError("too many values")
> return first
>
> But getting a return value and immediately checking it is far better
> spelled "try/except" here. (Note, BTW, that I made a subtle change to
> the logic here: this version doesn't call next() a second time if the
> first one returned the sentinel. This avoids problems with broken
> iterators that raise StopException and then keep going.)
>

Yes, I hadn't thought of that. I was trying to avoid repeating the *raise*
 statement.

Thanks again for the helpful comments. They give me a better idea of good
Python style.

 -s
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2020-09-22 Thread Chris Angelico
On Wed, Sep 23, 2020 at 3:52 AM Stavros Macrakis  wrote:
>
> Thanks to everyone for the comments, especially Tim Chase for the simple
> and elegant tuple unpacking solution, and Léo El Amri for the detailed
> comments on the variants. Below are some more variants which *don't *use
> tuple unpacking, on the theory that the coding patterns may be useful in
> other cases where unpacking doesn't apply.

When doesn't it apply? Can you elaborate on this? It might be easier
to advise on Pythonic style when the specific requirements are known.

> For me, one of the interesting lessons from all these finger exercises is
> that *for* and unpacking hide a lot of messiness, both the initial *iter* call
> and the exception handling. I don't see any way to eliminate the *iter*,
> but there are ways to avoid the verbose exception handling.

In Python, exception handling IS the way to do these things. Having a
two-part return value rather than using an exception is an unusual
idiom in Python (although it's well known in other languages; I
believe JavaScript does iterators this way, for one).

> Using the second arg to *next*, we get what is arguably a more elegant
> solution:
>
>
> _uniq = []
> def firstg(iterable):
> it = iter(iterable)
> val0 = next(it,_uniq)
> val1 = next(it,_uniq)
> if val0 is not _uniq and val1 is _uniq:
> return val0
> else:
> raise ValueError("first1: arg not exactly 1 long")
>
> But I don't know if the *_uniq* technique is considered Pythonic.

It is when it's needed, but a more common way to write this would be
to have the sentinel be local to the function (since it doesn't need
to be an argument):

def firstg_variant(iterable):
it = iter(iterable)
sentinel = object()
first = next(it, sentinel)
if first is sentinel:
raise ValueError("empty iterable")
second = next(it, sentinel)
if second is not sentinel:
raise ValueError("too many values")
return first

But getting a return value and immediately checking it is far better
spelled "try/except" here. (Note, BTW, that I made a subtle change to
the logic here: this version doesn't call next() a second time if the
first one returned the sentinel. This avoids problems with broken
iterators that raise StopException and then keep going.)

> If *next* were instead defined to return a flag (rather than raising an
> exception), the code becomes cleaner and clearer, something like this:
>
>
> def firsth(iterable):
>   it = iter(iterable)
>   (val0, good0) = next2(it)
>   (val1, good1) = next2(it)  # val1 is dummy
>   if good0 and not good1:
> return val0
>   else:
> raise ValueError("first1: arg not exactly 1 long")
>

IMO this isn't any better than the previous one. You still need a
sentinel, but now you use True and False instead of a special object.
It isn't *terrible*, but it's no advantage either.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2020-09-22 Thread Stavros Macrakis
Thanks to everyone for the comments, especially Tim Chase for the simple
and elegant tuple unpacking solution, and Léo El Amri for the detailed
comments on the variants. Below are some more variants which *don't *use
tuple unpacking, on the theory that the coding patterns may be useful in
other cases where unpacking doesn't apply.

For me, one of the interesting lessons from all these finger exercises is
that *for* and unpacking hide a lot of messiness, both the initial *iter* call
and the exception handling. I don't see any way to eliminate the *iter*,
but there are ways to avoid the verbose exception handling.

Using the second arg to *next*, we get what is arguably a more elegant
solution:


_uniq = []
def firstg(iterable):
it = iter(iterable)
val0 = next(it,_uniq)
val1 = next(it,_uniq)
if val0 is not _uniq and val1 is _uniq:
return val0
else:
raise ValueError("first1: arg not exactly 1 long")


But I don't know if the *_uniq* technique is considered Pythonic.

If *next* were instead defined to return a flag (rather than raising an
exception), the code becomes cleaner and clearer, something like this:


def firsth(iterable):
  it = iter(iterable)
  (val0, good0) = next2(it)
  (val1, good1) = next2(it)  # val1 is dummy
  if good0 and not good1:
return val0
  else:
raise ValueError("first1: arg not exactly 1 long")

# returns (value, validp)

# validp is False if no more values

def next2(iterable):
  try:
val = next(iterable)
  except StopIteration:
return (None, False)
  return (val, True)


(To be clear, I'm *not *suggesting that *next2* replace *next*!)

Thoughts?

 -s
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2020-09-21 Thread Stavros Macrakis
Thanks, Tim! I didn't realize that you could write (x,) on the LHS!
Very nice, very Pythonic!

  -s

On Mon, Sep 21, 2020 at 9:15 AM Tim Chase 
wrote:

> On 2020-09-20 18:34, Stavros Macrakis wrote:
> > Consider a simple function which returns the first element of an
> > iterable if it has exactly one element, and throws an exception
> > otherwise. It should work even if the iterable doesn't terminate.
> > I've written this function in multiple ways, all of which feel a
> > bit clumsy.
> >
> > I'd be interested to hear thoughts on which of these solutions is
> > most Pythonic in style. And of course if there is a more elegant
> > way to solve this, I'm all ears! I'm probably missing something
> > obvious!
>
> You can use tuple unpacking assignment and Python will take care of
> the rest for you:
>
>   >>> x, = tuple() # no elements
>   Traceback (most recent call last):
> File "", line 1, in 
>   ValueError: not enough values to unpack (expected 1, got 0)
>   >>> x, = (1, )  # one element
>   >>> x, = itertools.repeat("hello") # 2 to infinite elements
>   Traceback (most recent call last):
> File "", line 1, in 
>   ValueError: too many values to unpack (expected 1)
>
> so you can do
>
>   def fn(iterable):
> x, = iterable
> return x
>
> The trailing comma can be hard to spot, so I usually draw a little
> extra attention to it with either
>
>   (x, ) = iterable
>
> or
>
>   x, = iterable # unpack one value
>
> I'm not sure it qualifies as Pythonic, but it uses Pythonic features
> like tuple unpacking and the code is a lot more concise.
>
> -tim
>
>
>
>
>
>
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2020-09-21 Thread Terry Reedy

On 9/20/2020 6:34 PM, Stavros Macrakis wrote:

I'm trying to improve my Python style.

Consider a simple function which returns the first element of an iterable
if it has exactly one element, and throws an exception otherwise. It should
work even if the iterable doesn't terminate. I've written this function in
multiple ways, all of which feel a bit clumsy.


The 'obvious' thing to me was the double try-except StopIteration.  It 
is clear, and clarity is 'pythonic'.



--
Terry Jan Reedy

--
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2020-09-21 Thread Frank Millman

On 2020-09-21 3:46 PM, Chris Angelico wrote:

On Mon, Sep 21, 2020 at 11:37 PM Tim Chase
 wrote:


On 2020-09-20 18:34, Stavros Macrakis wrote:

Consider a simple function which returns the first element of an
iterable if it has exactly one element, and throws an exception
otherwise. It should work even if the iterable doesn't terminate.
I've written this function in multiple ways, all of which feel a
bit clumsy.

I'd be interested to hear thoughts on which of these solutions is
most Pythonic in style. And of course if there is a more elegant
way to solve this, I'm all ears! I'm probably missing something
obvious!


You can use tuple unpacking assignment and Python will take care of
the rest for you:

   >>> x, = tuple() # no elements
   Traceback (most recent call last):
 File "", line 1, in 
   ValueError: not enough values to unpack (expected 1, got 0)
   >>> x, = (1, )  # one element
   >>> x, = itertools.repeat("hello") # 2 to infinite elements
   Traceback (most recent call last):
 File "", line 1, in 
   ValueError: too many values to unpack (expected 1)

so you can do

   def fn(iterable):
 x, = iterable
 return x

The trailing comma can be hard to spot, so I usually draw a little
extra attention to it with either

   (x, ) = iterable

or

   x, = iterable # unpack one value

I'm not sure it qualifies as Pythonic, but it uses Pythonic features
like tuple unpacking and the code is a lot more concise.


Or:

[x] = iterable

I'd definitely recommend using unpacking as the most obvious way to do
this. Among other advantages, it gives different messages for the "too
many" and "too few" cases.



I used something similar years ago, but I made the mistake of relying on 
the error message in my logic, to distinguish between 'too few' and 'too 
many'. Guess what happened - Python changed the wording of the messages, 
and my logic failed.


After messing about with some alternatives, I ended up with the OP's 
first option (with some added comments), and have stuck with it ever 
since. It is not pretty, but it is readable and unambiguous.


Frank Millman

--
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2020-09-21 Thread Tim Chase
On 2020-09-21 09:48, Stavros Macrakis wrote:
>>   def fn(iterable):
>> x, = iterable
>> return x
>
> Thanks, Tim! I didn't realize that you could write (x,) on the LHS!
> Very nice, very Pythonic!

It also expands nicely for other cases, so you want the 3-and-only-3
first values with errors for too many or too few?

  x, y, z = iterable
  x, y, z = (1, 2, 3)

The (x,) version is just the single case.  And it's fast—a single
Python UNPACK_SEQUENCE opcode

  >>> dis.dis(fn)
  2   0 LOAD_FAST0 (i)
  2 UNPACK_SEQUENCE  1
  4 STORE_FAST   1 (x)

  3   6 LOAD_FAST1 (x)
  8 RETURN_VALUE

Though now I'm wondering if there's a way to skip the
STORE_FAST/LOAD_FAST instructions and create a function that
generates the opcode sequence

  UNPACK_SEQUENCE 1
  RETURN_VALUE

:-)

(totally tangential ramblings)

-tkc







-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2020-09-21 Thread Léo El Amri via Python-list
On 21/09/2020 15:15, Tim Chase wrote:
> You can use tuple unpacking assignment and Python will take care of
> the rest for you:
> 
> so you can do
> 
>   def fn(iterable):
> x, = iterable
> return x
> 
> I'm not sure it qualifies as Pythonic, but it uses Pythonic features
> like tuple unpacking and the code is a lot more concise.

I guess you just beat the topic. I think it is Pythonic and I'd be
surprised if someone came with something more Pythonic.

FI: The behavior of this assignation is detailed here:
https://docs.python.org/3/reference/simple_stmts.html#assignment-statements
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2020-09-21 Thread Chris Angelico
On Mon, Sep 21, 2020 at 11:37 PM Tim Chase
 wrote:
>
> On 2020-09-20 18:34, Stavros Macrakis wrote:
> > Consider a simple function which returns the first element of an
> > iterable if it has exactly one element, and throws an exception
> > otherwise. It should work even if the iterable doesn't terminate.
> > I've written this function in multiple ways, all of which feel a
> > bit clumsy.
> >
> > I'd be interested to hear thoughts on which of these solutions is
> > most Pythonic in style. And of course if there is a more elegant
> > way to solve this, I'm all ears! I'm probably missing something
> > obvious!
>
> You can use tuple unpacking assignment and Python will take care of
> the rest for you:
>
>   >>> x, = tuple() # no elements
>   Traceback (most recent call last):
> File "", line 1, in 
>   ValueError: not enough values to unpack (expected 1, got 0)
>   >>> x, = (1, )  # one element
>   >>> x, = itertools.repeat("hello") # 2 to infinite elements
>   Traceback (most recent call last):
> File "", line 1, in 
>   ValueError: too many values to unpack (expected 1)
>
> so you can do
>
>   def fn(iterable):
> x, = iterable
> return x
>
> The trailing comma can be hard to spot, so I usually draw a little
> extra attention to it with either
>
>   (x, ) = iterable
>
> or
>
>   x, = iterable # unpack one value
>
> I'm not sure it qualifies as Pythonic, but it uses Pythonic features
> like tuple unpacking and the code is a lot more concise.

Or:

[x] = iterable

I'd definitely recommend using unpacking as the most obvious way to do
this. Among other advantages, it gives different messages for the "too
many" and "too few" cases.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2020-09-21 Thread Tim Chase
On 2020-09-20 18:34, Stavros Macrakis wrote:
> Consider a simple function which returns the first element of an
> iterable if it has exactly one element, and throws an exception
> otherwise. It should work even if the iterable doesn't terminate.
> I've written this function in multiple ways, all of which feel a
> bit clumsy.
> 
> I'd be interested to hear thoughts on which of these solutions is
> most Pythonic in style. And of course if there is a more elegant
> way to solve this, I'm all ears! I'm probably missing something
> obvious!

You can use tuple unpacking assignment and Python will take care of
the rest for you:

  >>> x, = tuple() # no elements
  Traceback (most recent call last):
File "", line 1, in 
  ValueError: not enough values to unpack (expected 1, got 0)
  >>> x, = (1, )  # one element
  >>> x, = itertools.repeat("hello") # 2 to infinite elements
  Traceback (most recent call last):
File "", line 1, in 
  ValueError: too many values to unpack (expected 1)

so you can do

  def fn(iterable):
x, = iterable
return x

The trailing comma can be hard to spot, so I usually draw a little
extra attention to it with either

  (x, ) = iterable

or

  x, = iterable # unpack one value

I'm not sure it qualifies as Pythonic, but it uses Pythonic features
like tuple unpacking and the code is a lot more concise.

-tim





-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2020-09-21 Thread Léo El Amri via Python-list
On 21/09/2020 00:34, Stavros Macrakis wrote:
> I'm trying to improve my Python style.
> 
> Consider a simple function which returns the first element of an iterable
> if it has exactly one element, and throws an exception otherwise. It should
> work even if the iterable doesn't terminate. I've written this function in
> multiple ways, all of which feel a bit clumsy.
> 
> I'd be interested to hear thoughts on which of these solutions is most
> Pythonic in style. And of course if there is a more elegant way to solve
> this, I'm all ears! I'm probably missing something obvious!

Hello Stavros,

As there is no formal definition nor consensus on what is Pythonic and
what isn't, this reply will be very subjective.



My opinion on your code suggestions is the following:

> 1 def firstf(iterable):
> 2 n = -1
> 3 for n,i in enumerate(iterable):
> 4 if n>0:
> 5 raise ValueError("first1: arg not exactly 1 long")
> 6 if n==0:
> 7 return i
> 8 else:
> 9 raise ValueError("first1: arg not exactly 1 long")

firstf isn't Pythonic:

1. We are checking twice for the same thing at line 4 (n>0)
   and 6 (n==0).

2. We are using enumarate(), from which we ignore the second element
   it yields in its tuple

> 1 def firstd(iterable):
> 2 it = iter(iterable)
> 3 try:
> 4 val = next(it)
> 5 except StopIteration:
> 6 raise ValueError("first1: arg not exactly 1 long")
> 7 for i in it:
> 8 raise ValueError("first1: arg not exactly 1 long")
> 9 return val

firstd isn't Pythonic. While the usage of a for statement in place of a
try..except saves two lines, it is at the expense of showing a clear
intent: When I see a for statement, I expect a "complex" operation on
the iterable items (which we are ignoring here).

>  1 def firsta(iterable):
>  2 it = iter(iterable)
>  3 try:
>  4 val = next(it)
>  5 except StopIteration:
>  6 raise ValueError("first1: arg not exactly 1 long")
>  7 try:
>  8 next(it)
>  9 except StopIteration:
> 10 return val
> 11 raise ValueError("first1: arg not exactly 1 long")

>  1 def firstb(iterable):
>  2 it = iter(iterable)
>  3 try:
>  4 val = next(it)
>  5 except StopIteration:
>  6 raise ValueError("first1: arg not exactly 1 long")
>  7 try:
>  8 next(it)
>  9 except StopIteration:
> 10 return val
> 11 else:
> 12 raise ValueError("first1: arg not exactly 1 long")

>  1 def firstc(iterable):
>  2 it = iter(iterable)
>  3 try:
>  4 val = next(it)
>  5 except StopIteration:
>  6 raise ValueError("first1: arg not exactly 1 long")
>  7 try:
>  8 next(it)
>  9 raise ValueError("first1: arg not exactly 1 long")
> 10 except StopIteration:
> 11 return val

firsta, firstb and firstc are equally Pythonic. I have a preference for
firsta, which is more concise and have a better "reading flow".

>  1 def firste(iterable):
>  2 it = iter(iterable)
>  3 try:
>  4 good = False
>  5 val = next(it)
>  6 good = True
>  7 val = next(it)
>  8 good = False
>  9 raise StopIteration   # or raise ValueError
> 10 except StopIteration:
> 11 if good:
> 12 return val
> 13 else:
> 14 raise ValueError("first1: arg not exactly 1 long")

firste might be Pythonic although it's very "C-ish". I can grasp the
intent and there is no repetition. I wouldn't write the assignation at
line 7, though.



Mixing firsta and firste would make something more Pythonic:

def firstg(iterable):
it = iter(iterable)
try:
val = next(it)
try:
next(it)
except StopIteration:
return val
except StopIteration:
pass
raise ValueError("first: arg not exactly 1 long")

1. The code isn't repetitive (The "raise ValueError" is written
   only once)

2. The intent is a bit harder to grasp than for firsta or firste, but
   the code is shorter than firste

3. The try..catch nesting is considered a bad practice, but the code
   here is simple enough so it shouldn't trigger a strong aversion
   reading it



- Léo
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-29 Thread Steven D'Aprano
On Fri, 29 Apr 2016 10:48 am, Gregory Ewing wrote:

> MRAB wrote:
> 
>> Is it worthy of being in the Zen of Python?
> 
> +1. Maybe something along the lines of:
> 
> Dunder methods are for defining, not calling.
> Unless you're a dunderhead[1].
> 
> [1] Meant in the sense of an enthusiast, cf. gearhead.



I think that the advice to not call dundermethods directly is excellent
advice, but it doesn't belong in the Zen. Look at the Zen: it's all pretty
abstract:

Beautiful is better than ugly.
Explicit is better than implicit.

etc. There's very little[1] concrete advice in the way of specificities such
as:

- don't use floats for money;
- use namedtuple for the equivalent of a C struct or Pascal record;
- composition should be preferred over inheritance;

etc. "Don't use dunders" is much closer to the second, more specific type of
advice which doesn't really fall into the Zen's bailiwick.

Better suited for the Zen would be:

"Not everything needs to be a one-liner."

which is nicely abstract and also completely useless for deciding which
things should and shouldn't be, as good koans ought to be.




[1] By which I mean none.


-- 
Steven

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-29 Thread Chris Angelico
On Fri, Apr 29, 2016 at 4:12 PM, Gregory Ewing
 wrote:
> Chris Angelico wrote:
>>
>> I thought the twentieth zen would never be found?
>
>
> Yes. This will have to be numbered the 21st zen
> to maintain that invariant.
>

Python for the 21st Century.

In a hundred years, another zen!

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-29 Thread Gregory Ewing

Chris Angelico wrote:

I thought the twentieth zen would never be found?


Yes. This will have to be numbered the 21st zen
to maintain that invariant.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-28 Thread Chris Angelico
On Fri, Apr 29, 2016 at 10:48 AM, Gregory Ewing
 wrote:
> MRAB wrote:
>
>> Is it worthy of being in the Zen of Python?
>
>
> +1. Maybe something along the lines of:
>
>Dunder methods are for defining, not calling.
>Unless you're a dunderhead[1].
>
> [1] Meant in the sense of an enthusiast, cf. gearhead.

I thought the twentieth zen would never be found?

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-28 Thread Gregory Ewing

MRAB wrote:


Is it worthy of being in the Zen of Python?


+1. Maybe something along the lines of:

   Dunder methods are for defining, not calling.
   Unless you're a dunderhead[1].

[1] Meant in the sense of an enthusiast, cf. gearhead.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-28 Thread Random832
On Thu, Apr 28, 2016, at 01:16, Rustom Mody wrote:
> On Thursday, April 28, 2016 at 9:26:21 AM UTC+5:30, Chris Angelico wrote:
> > My rule of thumb is: Dunders are for defining, not for calling. It's
> > not a hard-and-fast rule, but it'll get you through 99%+ of
> > situations.
> 
> Neat and clever.
> Should get in the docs somewhere

And even most of the obvious cases where you would want to call one
[e.g. on super] are in the course of implementing one.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-28 Thread MRAB

On 2016-04-28 06:16, Rustom Mody wrote:

On Thursday, April 28, 2016 at 9:26:21 AM UTC+5:30, Chris Angelico wrote:

My rule of thumb is: Dunders are for defining, not for calling. It's
not a hard-and-fast rule, but it'll get you through 99%+ of
situations.


Neat and clever.
Should get in the docs somewhere


Is it worthy of being in the Zen of Python?

--
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-27 Thread Steven D'Aprano
On Thursday 28 April 2016 13:23, Ben Finney wrote:

> Christopher Reimer  writes:
> 
>> In short,  my original code before I turned it into a separate
>> dictionary. *sigh*
> 
> No, I think that misses the points that were being made. The discussion
> you're talking about was *not* to say “attribute access is better than
> dictionary access”, or vice versa. Each is good for its purpose.
> 
> Rather, the discussion was to drive home the point that in Python those
> are *two distinct concepts*, and you need to not conflate them.
> 
> If you want items in a mapping, explicitly use a Python ‘dict’ instance.
> If you want attributes that describe an object, explicitly use
> attributes of that object. Deliberately choose which one makes more
> sense.


Think of it like this: attribute access is for accessing parts of a thing:

dog.tail
car.engine
cup.handle

or one of a small number of fixed fields from a record:

employment_record.start_date
employee.salary
autopsy.cause_of_death
person.title


Mappings (dicts) are for recording a potentially unlimited number of 
distinct records with arbitrary keys, especially since the keys don't have 
to be valid identifiers:

employees['Fred Smith']
students['Joanna McTavish']
prisoners[12345]
catalog['Widgets']['GMH-12345']
dictionary['update']


While Python allows you to write code to use attribute syntax for item 
access, doing so is a category mistake and leads to semantic confusion and 
potential bugs:

employees.Fred_Smith
students.Joanna_McTavish
prisoners.P12345
parts.widgets.GMH_12345
dictionary.update


The last example shows how mixing up these two distinct concepts can lead to 
problems. Should dictionary.update refer to the update method, or the entry 
for the word "update"?



-- 
Steve

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-27 Thread Rustom Mody
On Thursday, April 28, 2016 at 9:26:21 AM UTC+5:30, Chris Angelico wrote:
> My rule of thumb is: Dunders are for defining, not for calling. It's
> not a hard-and-fast rule, but it'll get you through 99%+ of
> situations.

Neat and clever.
Should get in the docs somewhere
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-27 Thread Christopher Reimer

On 4/27/2016 8:52 PM, Ethan Furman wrote:


The point Ben was trying to make is this:  you should never* call 
__dunder__ methods in normal code; there is no need to do so:


- use len(), not __len__()
- use next(), not __next__()
- use some_instance.an_attribute, not 
some_instance.__dict__['an_attribute']




DOH! I need a doughnut. :)

Thank you,

Chris R.
--
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-27 Thread Christopher Reimer

On 4/27/2016 8:23 PM, Ben Finney wrote:

If you want items in a mapping, explicitly use a Python ‘dict’ instance.
If you want attributes that describe an object, explicitly use
attributes of that object. Deliberately choose which one makes more
sense.


Okay, that makes sense.

Thank you,

Chris R.

--
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-27 Thread Chris Angelico
On Thu, Apr 28, 2016 at 1:52 PM, Ethan Furman  wrote:
>
> The point Ben was trying to make is this:  you should never* call __dunder__
> methods in normal code; there is no need to do so:
>
> - use len(), not __len__()
> - use next(), not __next__()
> - use some_instance.an_attribute, not some_instance.__dict__['an_attribute']
>
> --
> ~Ethan~
>
> * Okay, maybe /almost/ never.  About the only time you need to is when
> giving your classes special methods, such as __add__ or __repr__.

My rule of thumb is: Dunders are for defining, not for calling. It's
not a hard-and-fast rule, but it'll get you through 99%+ of
situations.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-27 Thread Ethan Furman

On 04/27/2016 08:07 PM, Christopher Reimer wrote:

On 4/27/2016 7:07 PM, Ben Finney wrote:

>> Ian Kelly wrote:


 self.__dict__ = {'key', 'value'}

is essentially equivalent to:

 self.key = value

>>

I would say the latter is more Pythonic, because it:

>>
>> [snip]
>>

* Uses the built-in mechanisms of Python (don't invoke magic attributes,
   instead use the system that makes use of them behind the scenes).


In short,  my original code before I turned it into a separate
dictionary. *sigh*


No.

The point Ben was trying to make is this:  you should never* call 
__dunder__ methods in normal code; there is no need to do so:


- use len(), not __len__()
- use next(), not __next__()
- use some_instance.an_attribute, not some_instance.__dict__['an_attribute']

--
~Ethan~

* Okay, maybe /almost/ never.  About the only time you need to is when 
giving your classes special methods, such as __add__ or __repr__.

--
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style

2016-04-27 Thread Ben Finney
Christopher Reimer  writes:

> In short,  my original code before I turned it into a separate
> dictionary. *sigh*

No, I think that misses the points that were being made. The discussion
you're talking about was *not* to say “attribute access is better than
dictionary access”, or vice versa. Each is good for its purpose.

Rather, the discussion was to drive home the point that in Python those
are *two distinct concepts*, and you need to not conflate them.

If you want items in a mapping, explicitly use a Python ‘dict’ instance.
If you want attributes that describe an object, explicitly use
attributes of that object. Deliberately choose which one makes more
sense.

-- 
 \ “The double standard that exempts religious activities from |
  `\   almost all standards of accountability should be dismantled |
_o__)   once and for all.” —Daniel Dennett, 2010-01-12 |
Ben Finney

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style (was: Differences between Class(Object) and Class(Dict) for dictionary usage?)

2016-04-27 Thread Christopher Reimer

On 4/27/2016 7:07 PM, Ben Finney wrote:

I would say the latter is more Pythonic, because it:

* Better conveys the intention (“set the value of the ‘self.key’
   attribute”).

* Uses the built-in mechanisms of Python (don't invoke magic attributes,
   instead use the system that makes use of them behind the scenes).

* Expresses that intention more concisely (fewer terms).

* Expresses that intention more clearly (less syntactic noise).


In short,  my original code before I turned it into a separate 
dictionary. *sigh*


Thank you,

Chris R.
--
https://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style involves lots of lightweight classes (for me)

2006-12-14 Thread Andrea Griffini
metaperl wrote:

  The above program started out as a list of dictionaries, but I
 like the current approach much better.

There is even a common idiom for this...

class Record(object):
 def __init__(self, **kwargs):
 self.__dict__.update(kwargs)

This way you can use

 user = Record(name=Andrea Griffini, email=[EMAIL PROTECTED])

and then access the fields using user.name syntax

HTH
Andrea
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Pythonic style involves lots of lightweight classes (for me)

2006-12-14 Thread bayerj
Hi,

I think that tuples are the best and simplest approach for small
structures.

 songs = [(Paranoid, http://...;), (Christian Woman, http://...;)]
 for title, url in songs:
...  print %s: %s % (title, url)
...
Paranoid: http://...
Christian Woman: http://...

I think that python's unpacking and builtin data types very useful. I
prefer it a lot to over-object-oriented-programming.

-- 
http://mail.python.org/mailman/listinfo/python-list