the PHP ternary operator equivalent on Python

L

Luis M. Gonzalez

This could be done easier this way:

L = [('even','odd')[n%2] for n in range(8)]
 
B

bonono

Luis said:
This could be done easier this way:

L = [('even','odd')[n%2] for n in range(8)]

That is even/odd, his(created to demonstrate the uglies of ternary) has
4 states, zero, -/+ then even/odd.
 
F

Fredrik Lundh

Daniel said:
Let me tell you something: I'm not a one-liner coder, but sometimes It
is necesary.
For example:
I need to translate data from a DataField to Another.

def Evaluate(condition,truepart,falsepart):
if condition:
return truepart
else:
return falsepart

dOldDataFields = {}
dNewDataFields = {}

dNewDataFields = {
'CODE': dOldDataFields['CODEDATA'],
'DATE': dOldDataFields['DATE'],
'CONTACT': Evaluate(dOldDataFields['CONTACTTYPE']==2,
dOldDataFields['FIRSTCONTACT'], dOldDataFields['SECONDCONTACT'])
}

With this, I created a new dic very easy, saving in
dNewDataFields['CONTACT'] the value of dOldDataFields['FIRSTCONTACT']
or the value of dOldDataFields['SECONDCONTACT'] depending on
dOldDataFields['CONTACTTYPE']. How you do this in a practic way without
the use of one-line code? It is needed! You can't avoid it!

if you use less verbose names, you can do the same thing in less than half
the number of characters, without a single oneliner:

def convert(old):

new = dict(
CODE=old['CODEDATA'],
DATE=old['DATE']
)

if old['CONTACTTYPE'] == 2:
new['CONTACT'] = old['FIRSTCONTACT']
else:
new['CONTACT'] = old['SECONDCONTACT']

return new

</F>
 
R

rurpy

Fredrik said:
Daniel said:
Let me tell you something: I'm not a one-liner coder, but sometimes It
is necesary.
For example:
I need to translate data from a DataField to Another.

def Evaluate(condition,truepart,falsepart):
if condition:
return truepart
else:
return falsepart

dOldDataFields = {}
dNewDataFields = {}

dNewDataFields = {
'CODE': dOldDataFields['CODEDATA'],
'DATE': dOldDataFields['DATE'],
'CONTACT': Evaluate(dOldDataFields['CONTACTTYPE']==2,
dOldDataFields['FIRSTCONTACT'], dOldDataFields['SECONDCONTACT'])
}

With this, I created a new dic very easy, saving in
dNewDataFields['CONTACT'] the value of dOldDataFields['FIRSTCONTACT']
or the value of dOldDataFields['SECONDCONTACT'] depending on
dOldDataFields['CONTACTTYPE']. How you do this in a practic way without
the use of one-line code? It is needed! You can't avoid it!

if you use less verbose names, you can do the same thing in less than half
the number of characters, without a single oneliner:

def convert(old):

new = dict(
CODE=old['CODEDATA'],
DATE=old['DATE']
)

if old['CONTACTTYPE'] == 2:
new['CONTACT'] = old['FIRSTCONTACT']
else:
new['CONTACT'] = old['SECONDCONTACT']

return new

I don't find your code any more readable than the OP's
equivalent code:

def convert(old):
new = {
CODE: old['CODEDATA'],
DATE: old['DATE'],
CONTACT: old['FIRSTCONTACT'] \
if old['CONTACTTYPE'] == 2 \
else old['OLDDCONTACT']
}
return new

The OPs code make one pass through the dict, your's makes
two. I do not know what effect (if any) that has in the case of
a very large dict.
 
S

Steven D'Aprano

Let me tell you something: I'm not a one-liner coder, but sometimes It
is necesary.

I'm not arguing for multi-line code just for the sake of using more than
one line. I'm against artificially trying to compress a algorithm that is
naturally expressed in two or more lines into one line.

For example:
I need to translate data from a DataField to Another.

def Evaluate(condition,truepart,falsepart):
if condition:
return truepart
else:
return falsepart

dOldDataFields = {}
dNewDataFields = {}

Why do you bother to initialise dNewDataFields to an empty dict when you
then populate it in the very next statement?

dNewDataFields = {
'CODE': dOldDataFields['CODEDATA'],
'DATE': dOldDataFields['DATE'],
'CONTACT': Evaluate(dOldDataFields['CONTACTTYPE']==2,
dOldDataFields['FIRSTCONTACT'], dOldDataFields['SECONDCONTACT'])
}

Doesn't look like a one-liner to me. You've called Evaluate, which takes
five lines by my count.

With this, I created a new dic very easy, saving in
dNewDataFields['CONTACT'] the value of dOldDataFields['FIRSTCONTACT']
or the value of dOldDataFields['SECONDCONTACT'] depending on
dOldDataFields['CONTACTTYPE']. How you do this in a practic way without
the use of one-line code? It is needed! You can't avoid it! Even using
a = [if_false_expr, if_true_expr][predicate] or a function, you'll
always have to use a one-line code (for this purpose, of course).

Again, I am not arguing for needlessly inflating lines of code, I think
your solution is fine. But just to prove it can be done:

dNewDataFields['CODE'] = dOldDataFields['CODEDATA']
dNewDataFields['DATE'] = dOldDataFields['DATE']
if dOldDataFields['CONTACTTYPE'] == 2:
dNewDataFields['CONTACT'] = dOldDataFields['FIRSTCONTACT']
else:
dNewDataFields['CONTACT'] = dOldDataFields['SECONDCONTACT']


There. The world didn't end.

*wink*
 
P

Paul Rubin

Steven D'Aprano said:
dNewDataFields['CODE'] = dOldDataFields['CODEDATA']
dNewDataFields['DATE'] = dOldDataFields['DATE']
if dOldDataFields['CONTACTTYPE'] == 2:
dNewDataFields['CONTACT'] = dOldDataFields['FIRSTCONTACT']
else:
dNewDataFields['CONTACT'] = dOldDataFields['SECONDCONTACT']

There. The world didn't end.

It gets messy for a more complicated structure:

d = {'x': x1() if x_is_old else x2(),
'y': y1() if y_is_old else y2(),
'z': z1() if z_is_old else z2() }

etc.
 
F

Fredrik Lundh

Paul said:
It gets messy for a more complicated structure:

d = {'x': x1() if x_is_old else x2(),
'y': y1() if y_is_old else y2(),
'z': z1() if z_is_old else z2() }
etc.

somewhere between the '}' and the '.', a data-driven approach will
be more readable, easier to maintain, and nearly as efficient.

(and with an example as regular as yours, the data table can be auto-
generated...)

</F>
 
F

Fredrik Lundh

I don't find your code any more readable than the OP's
equivalent code:

the OP's question was

How you do this in a practic way without
the use of one-line code ?
The OPs code make one pass through the dict, your's makes
two. I do not know what effect (if any) that has in the case of
a very large dict.

the OPs code contains five reads and three writes, my and
steven's versions contain four reads and three writes. the
yet-to-be-released ternary operator would remove one read
from the OPs code, which makes both alternatives equivalent.

dictionary accesses don't involve passes over the dictionary.

there's a comment on readability and code understanding waiting
to be made here, but I'll leave that for another day.

</F>
 
S

Steven D'Aprano

Fredrik said:
def convert(old):

new = dict(
CODE=old['CODEDATA'],
DATE=old['DATE']
)

if old['CONTACTTYPE'] == 2:
new['CONTACT'] = old['FIRSTCONTACT']
else:
new['CONTACT'] = old['SECONDCONTACT']

return new


I don't find your code any more readable than the OP's
equivalent code:

def convert(old):
new = {
CODE: old['CODEDATA'],
DATE: old['DATE'],
CONTACT: old['FIRSTCONTACT'] \
if old['CONTACTTYPE'] == 2 \
else old['OLDDCONTACT']
}
return new


The problem I have with your code is that it is too
similar to:

def convert(old):
new = {
CODE: old['CODEDATA'],
DATE: old['DATE'],
CONTACT: old['FIRSTCONTACT'] }
if old['CONTACTTYPE'] == 2:
else: old['OLDDCONTACT']
return new

Yes, the above code is broken. But it *looks* right, at
first glance, unless you train yourself to never write

if cond: TRUE_CLAUSE
else: FALSE_CLAUSE

as a two-liner.

Regardless of whatever benefits the ternary operator is
going to have, in my opinion allowing people to write
TRUE_CLAUSE if COND else FALSE_CLAUSE will increase the
amount of poorly written, hard to read code.

The OPs code make one pass through the dict, your's makes
two.

The original code binds a name to an empty dict, then
rebinds the name to a populated dict.

Your code simply creates the dict in one step.

Fredrik's code creates an initial dict, then adds a new
key and value to it. That's hardly making two passes
through the dict -- what does that even mean?

I do not know what effect (if any) that has in the case of
a very large dict.


Quick and dirty speed test:

py> from time import time
py> def makedict1():
.... return dict.fromkeys(range(1000))
....
py> def makedict2():
.... D = {}
.... for i in range(1000):
.... D=None
.... return D
....
py> assert makedict1() == makedict2()
py> def test(func, n=100):
.... t = time()
.... for i in range(n):
.... tmp = func()
.... return (time() - t)/n
....
py> test(makedict1)
0.00020779848098754884
py> test(makedict2)
0.00042409896850585936

That's not timing quite the same thing you refer to,
but it is suggestive: if you create an empty dict, and
then populate it one item at a time, it will take
approximately twice as long as creating the non-empty
dict directly.

As a very unscientific, system-dependent, statistically
shoddy ball-park estimate, it looks like each item
assignment to a pre-existing dict takes less than
0.0000002s. So if you had a dict and added another
million items to it, one at a time, it might take an
extra 0.2s in total more than what it would have taken
you if you wrote those one million items in your source
code.

I can live with that.
 
R

rurpy

Steven said:
Fredrik said:
def convert(old):

new = dict(
CODE=old['CODEDATA'],
DATE=old['DATE']
)

if old['CONTACTTYPE'] == 2:
new['CONTACT'] = old['FIRSTCONTACT']
else:
new['CONTACT'] = old['SECONDCONTACT']

return new


I don't find your code any more readable than the OP's
equivalent code:

def convert(old):
new = {
CODE: old['CODEDATA'],
DATE: old['DATE'],
CONTACT: old['FIRSTCONTACT'] \
if old['CONTACTTYPE'] == 2 \
else old['OLDDCONTACT']
}
return new


The problem I have with your code is that it is too
similar to:

def convert(old):
new = {
CODE: old['CODEDATA'],
DATE: old['DATE'],
CONTACT: old['FIRSTCONTACT'] }
if old['CONTACTTYPE'] == 2:
else: old['OLDDCONTACT']
return new

Huh? How is this similar? Even a glance shows the
indentation is different. It has the same results?
Ok. But why would someone want to assign wrong
values to some of the entries, then go back and fix them?
I think either Fredrick's code or the code I posted is
clearer than this.
Yes, the above code is broken. But it *looks* right, at
first glance,

Yes, I see the error. OLDDCONTACT should be
OLDCONTACT. Are you going to suggest Enum's? :)
unless you train yourself to never write

if cond: TRUE_CLAUSE
else: FALSE_CLAUSE

as a two-liner.

Oh, that error too... But that error is a compile time
syntax error. Your argument would be much stronger
if it was not a syntax error, but resulted in erroneous
program behavior.
Regardless of whatever benefits the ternary operator is
going to have, in my opinion allowing people to write
TRUE_CLAUSE if COND else FALSE_CLAUSE will increase the
amount of poorly written, hard to read code.

1. Your definitions of hard to read are not the same as mine.
2. Good documentation can mitigate this somewhat.
3. The benefits of being able to write easier to read code
by people with good judgement outweighs the downside.
 
R

rurpy

Fredrik said:
the OP's question was

How you do this in a practic way without
the use of one-line code ?

I know. But you compared the readability of code with
one-liners and long variable names with the readability
of code with no one-liners and short variable names.
I thought that most of the readability improvement was
due to the long-to-short name change. So I posted what
I hoped was code equivalent to what you quoted, but
using your short names and a one-liner "then-if-else"
(on 3 lines :) It satisfied me that the readability
improvement was due to the short varible names.
the OPs code contains five reads and three writes, my and
steven's versions contain four reads and three writes. the
yet-to-be-released ternary operator would remove one read
from the OPs code, which makes both alternatives equivalent.

dictionary accesses don't involve passes over the dictionary.

I was actually thinking of the effects of hardware
memory cache...
 

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

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,821
Latest member
AleidaSchi

Latest Threads

Top