Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #79905 > unrolled thread
| Started by | Anton <anton.schattenfeld@gmail.com> |
|---|---|
| First post | 2014-10-29 03:42 -0700 |
| Last post | 2014-11-12 12:31 -0800 |
| Articles | 14 — 7 participants |
Back to article view | Back to comp.lang.python
Python Style Question Anton <anton.schattenfeld@gmail.com> - 2014-10-29 03:42 -0700
Re: Python Style Question Martin Kemp <martin.kemp@ingg.com> - 2014-10-29 11:25 +0000
Re: Python Style Question Rafael Romero Carmona <rafaelromcar@gmail.com> - 2014-10-29 12:09 +0100
Re: Python Style Question Anton <anton.schattenfeld@gmail.com> - 2014-10-29 12:09 -0700
Re: Python Style Question Rafael Romero Carmona <rafaelromcar@gmail.com> - 2014-10-29 12:52 +0100
Re: Python Style Question Anton <anton.schattenfeld@gmail.com> - 2014-10-29 12:23 -0700
Re: Python Style Question Martin Kemp <martin.kemp@ingg.com> - 2014-10-29 12:42 +0000
Re: Python Style Question Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-10-30 22:10 +1100
Re: Python Style Question MRAB <python@mrabarnett.plus.com> - 2014-10-30 12:21 +0000
Re: Python Style Question Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-10-31 09:48 +1100
Re: Python Style Question Denis McMahon <denismfmcmahon@gmail.com> - 2014-10-31 04:55 +0000
Re: Python Style Question Roy Smith <roy@panix.com> - 2014-10-30 20:34 -0400
Re: Python Style Question Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-10-31 14:52 +1100
Re: Python Style Question Anton <anton.schattenfeld@gmail.com> - 2014-11-12 12:31 -0800
| From | Anton <anton.schattenfeld@gmail.com> |
|---|---|
| Date | 2014-10-29 03:42 -0700 |
| Subject | Python Style Question |
| Message-ID | <406463d0-1287-49a2-b1ed-08a928db4b94@googlegroups.com> |
Let's say I have an incoming list of values *l*. Every element of *l* can be one of the following options: 1) an integer value 2) a string in form of '<int_value>', e.g. '7' 3) a string with a json serialization of an integer value, e.g. '"7"' 4) something else that should be ignored I need to transform this list into another list with values from options 1)-3) coerced to int. The code below should do this. Variant 1 === values = [] for c in l: # Case 1) or 2) try: c_int = int(c) except ValueError: pass else: values.add(c_int) continue # Case 3) try: c_int = int(json.loads(c)) except ValueError: pass else: values.add(c_int) continue === Is this code ugly? Does it follow EAFP? Am I missing something in language best practice? Or maybe below is more preferable way with a nested try...except clause? Variant 2 === values = [] for c in l: # Case 1) or 2) try: c_int = int(c) except ValueError: # Case 3) try: c_int = int(json.loads(c)) except ValueError: pass else: values.add(c_int) continue else: values.add(c_int) continue === Thanks, Anton.
[toc] | [next] | [standalone]
| From | Martin Kemp <martin.kemp@ingg.com> |
|---|---|
| Date | 2014-10-29 11:25 +0000 |
| Message-ID | <mailman.15294.1414582983.18130.python-list@python.org> |
| In reply to | #79905 |
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 29/10/2014 10:45, Anton wrote: > Let's say I have an incoming list of values *l*. Every element of > *l* can be one of the following options: 1) an integer value 2) a > string in form of '<int_value>', e.g. '7' 3) a string with a json > serialization of an integer value, e.g. '"7"' 4) something else > that should be ignored > > I need to transform this list into another list with values from > options 1)-3) coerced to int. The code below should do this. > > > Variant 1 === > > values = [] for c in l: # Case 1) or 2) try: c_int = int(c) except > ValueError: pass else: values.add(c_int) continue > > # Case 3) try: c_int = int(json.loads(c)) except ValueError: pass > else: values.add(c_int) continue > > === > > Is this code ugly? Does it follow EAFP? Am I missing something in > language best practice? > > Or maybe below is more preferable way with a nested try...except > clause? > > Variant 2 === values = [] for c in l: # Case 1) or 2) try: c_int = > int(c) except ValueError: # Case 3) try: c_int = > int(json.loads(c)) except ValueError: pass else: values.add(c_int) > continue else: values.add(c_int) continue === > > Thanks, Anton. > > Your first example is perfectly fine and is EAFP Personally, I prefer to avoid nesting when it's not necessary. - -- Martin Kemp (martin.kemp@ingg.com) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAEBAgAGBQJUUM7zAAoJEJ0Re0UIDzSucugIALn/zY8RdpP8iaMShHoszzqf I0zl0mFHyqhNtwgQ0ZF7VGO+H+U0Dk8rhzTYOmEMzPTKNBGwll3fda9mOnrK9Xvp 9gQjII6DyQIWH7Z3dLcLr2e1j8OMNUSL6UmAYs8urNSIKZLowdV3JI4G/bLyW0KS y5Ko8dI6y5nOJ1P9XCmPTmags43UZfR8DrBUaAbzNcS8FGwmUE2KBkEhLQOvmpJi jmMc7wMOpq0jL+XbA+7pHUqoVZ7w1tUFjuy9I3h45tgPuTFAFB0gX+FpE+oVgO5o spQpVaOPEYN9ceLgHdKSxzdVIhOQLE6H/SYNHlsEW/ZNM6aR9n4yipgkOmtJ0+M= =WzHA -----END PGP SIGNATURE-----
[toc] | [prev] | [next] | [standalone]
| From | Rafael Romero Carmona <rafaelromcar@gmail.com> |
|---|---|
| Date | 2014-10-29 12:09 +0100 |
| Message-ID | <mailman.15295.1414583003.18130.python-list@python.org> |
| In reply to | #79905 |
Hi, first in Python 2.7.6 and Python 3.4.0 list haven't got any add
function but they have append.
I think you could do better with something like
==========
import json
l = [1, -1, 0, '1', '-1', '0', json.dumps(-1), json.dumps(1),
json.dumps(0), 'x', 'sqjklsqjk__', (1, 2)]
values = []
for c in l:
try:
c_int = int(c)
except ValueError:
pass
except TypeError:
pass
else:
values.append(c_int)
continue
print(values)
==========
The code has been tested in Python 2.7.6 and 3.4 and returns [1, -1,
0, 1, -1, 0, -1, 1, 0]
You don't need to do two try because you can process both exceptions
in the same way. You don't really need to do json.loads because if you
have a json string which is an integer, you could do that directly
with int(c) which can take a string and transform in an integer.
With ValueError it captures the exception when it tries to transform
the characters' strings and with TypeError it captures the exception
when it tries to work with the tuples.
Have a good day and hope it works for you!
2014-10-29 11:42 GMT+01:00 Anton <anton.schattenfeld@gmail.com>:
> Let's say I have an incoming list of values *l*. Every element of *l* can be one of the following options:
> 1) an integer value
> 2) a string in form of '<int_value>', e.g. '7'
> 3) a string with a json serialization of an integer value, e.g. '"7"'
> 4) something else that should be ignored
>
> I need to transform this list into another list with values from options 1)-3) coerced to int. The code below should do this.
>
>
> Variant 1
> ===
>
> values = []
> for c in l:
> # Case 1) or 2)
> try:
> c_int = int(c)
> except ValueError:
> pass
> else:
> values.add(c_int)
> continue
>
> # Case 3)
> try:
> c_int = int(json.loads(c))
> except ValueError:
> pass
> else:
> values.add(c_int)
> continue
>
> ===
>
> Is this code ugly?
> Does it follow EAFP?
> Am I missing something in language best practice?
>
> Or maybe below is more preferable way with a nested try...except clause?
>
> Variant 2
> ===
> values = []
> for c in l:
> # Case 1) or 2)
> try:
> c_int = int(c)
> except ValueError:
>
> # Case 3)
> try:
> c_int = int(json.loads(c))
> except ValueError:
> pass
> else:
> values.add(c_int)
> continue
>
> else:
> values.add(c_int)
> continue
> ===
>
> Thanks,
> Anton.
>
>
> --
> https://mail.python.org/mailman/listinfo/python-list
[toc] | [prev] | [next] | [standalone]
| From | Anton <anton.schattenfeld@gmail.com> |
|---|---|
| Date | 2014-10-29 12:09 -0700 |
| Message-ID | <8b20c9ae-ebde-41fd-8569-394e1372fb81@googlegroups.com> |
| In reply to | #79908 |
On Wednesday, October 29, 2014 4:43:33 AM UTC-7, Rafael Romero Carmona wrote:
> Hi, first in Python 2.7.6 and Python 3.4.0 list haven't got any add
> function but they have append.
You are right, in my original code I use set instead of array, so it should be either values = set() or values.append() in the original code.
>
> I think you could do better with something like
>
> ==========
> import json
> l = [1, -1, 0, '1', '-1', '0', json.dumps(-1), json.dumps(1),
> json.dumps(0), 'x', 'sqjklsqjk__', (1, 2)]
It should also work with cases like [1, json.dumps('-1')], which is case 3), sorry if it was not clear in the initial post.
>
> values = []
>
> for c in l:
> try:
> c_int = int(c)
> except ValueError:
> pass
> except TypeError:
> pass
> else:
> values.append(c_int)
> continue
> print(values)
> ==========
>
> The code has been tested in Python 2.7.6 and 3.4 and returns [1, -1,
> 0, 1, -1, 0, -1, 1, 0]
>
> You don't need to do two try because you can process both exceptions
> in the same way. You don't really need to do json.loads because if you
> have a json string which is an integer, you could do that directly
> with int(c) which can take a string and transform in an integer.
In case of 3) an element can be a string like '"1"', which will fail int(...), in this case it tries to parse it with json.
[toc] | [prev] | [next] | [standalone]
| From | Rafael Romero Carmona <rafaelromcar@gmail.com> |
|---|---|
| Date | 2014-10-29 12:52 +0100 |
| Message-ID | <mailman.15296.1414583954.18130.python-list@python.org> |
| In reply to | #79905 |
2014-10-29 12:25 GMT+01:00 Martin Kemp <martin.kemp@ingg.com>: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > On 29/10/2014 10:45, Anton wrote: >> Let's say I have an incoming list of values *l*. Every element of >> *l* can be one of the following options: 1) an integer value 2) a >> string in form of '<int_value>', e.g. '7' 3) a string with a json >> serialization of an integer value, e.g. '"7"' 4) something else >> that should be ignored >> >> I need to transform this list into another list with values from >> options 1)-3) coerced to int. The code below should do this. >> >> >> Variant 1 === >> >> values = [] for c in l: # Case 1) or 2) try: c_int = int(c) except >> ValueError: pass else: values.add(c_int) continue >> >> # Case 3) try: c_int = int(json.loads(c)) except ValueError: pass >> else: values.add(c_int) continue >> >> === >> >> Is this code ugly? Does it follow EAFP? Am I missing something in >> language best practice? >> >> Or maybe below is more preferable way with a nested try...except >> clause? >> >> Variant 2 === values = [] for c in l: # Case 1) or 2) try: c_int = >> int(c) except ValueError: # Case 3) try: c_int = >> int(json.loads(c)) except ValueError: pass else: values.add(c_int) >> continue else: values.add(c_int) continue === >> >> Thanks, Anton. >> >> > > Your first example is perfectly fine and is EAFP > Actually it doesn't work because there is no add function and it doesn't catch the TypeError function to ignore other exceptions than ValueError. Doesn't it? I tested in Python 2.7.6 and 3.4. > Personally, I prefer to avoid nesting when it's not necessary. > > - -- > Martin Kemp (martin.kemp@ingg.com) > -----BEGIN PGP SIGNATURE----- > Version: GnuPG v2 > > iQEcBAEBAgAGBQJUUM7zAAoJEJ0Re0UIDzSucugIALn/zY8RdpP8iaMShHoszzqf > I0zl0mFHyqhNtwgQ0ZF7VGO+H+U0Dk8rhzTYOmEMzPTKNBGwll3fda9mOnrK9Xvp > 9gQjII6DyQIWH7Z3dLcLr2e1j8OMNUSL6UmAYs8urNSIKZLowdV3JI4G/bLyW0KS > y5Ko8dI6y5nOJ1P9XCmPTmags43UZfR8DrBUaAbzNcS8FGwmUE2KBkEhLQOvmpJi > jmMc7wMOpq0jL+XbA+7pHUqoVZ7w1tUFjuy9I3h45tgPuTFAFB0gX+FpE+oVgO5o > spQpVaOPEYN9ceLgHdKSxzdVIhOQLE6H/SYNHlsEW/ZNM6aR9n4yipgkOmtJ0+M= > =WzHA > -----END PGP SIGNATURE----- > -- > https://mail.python.org/mailman/listinfo/python-list
[toc] | [prev] | [next] | [standalone]
| From | Anton <anton.schattenfeld@gmail.com> |
|---|---|
| Date | 2014-10-29 12:23 -0700 |
| Message-ID | <9806dae0-a47b-4aa8-aba0-0b2a69d002b2@googlegroups.com> |
| In reply to | #79909 |
On Wednesday, October 29, 2014 4:59:25 AM UTC-7, Rafael Romero Carmona wrote: > 2014-10-29 12:25 GMT+01:00 Martin Kemp <mar...@ingg.com>: > Actually it doesn't work because there is no add function and it > doesn't catch the TypeError function to ignore other exceptions than > ValueError. Doesn't it? I tested in Python 2.7.6 and 3.4. Originally I was using set instead of list. So it should have been either values = set() or values.append(), but it is not relevant to the question. Regarding TypeError, I don't catch it on purpose, because I want this type of Exception to bubble up and surface as an error and be logged, because this should never be the case. I expect an element to be either something coercible to int or a string. If for some reason it is an object, then there is something wrong one layer up, so I want it to fail explicitly.
[toc] | [prev] | [next] | [standalone]
| From | Martin Kemp <martin.kemp@ingg.com> |
|---|---|
| Date | 2014-10-29 12:42 +0000 |
| Message-ID | <mailman.15297.1414589867.18130.python-list@python.org> |
| In reply to | #79905 |
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 29/10/2014 12:01, Rafael Romero Carmona wrote: > 2014-10-29 12:25 GMT+01:00 Martin Kemp <martin.kemp@ingg.com>: On > 29/10/2014 10:45, Anton wrote: >>>> Let's say I have an incoming list of values *l*. Every >>>> element of *l* can be one of the following options: 1) an >>>> integer value 2) a string in form of '<int_value>', e.g. '7' >>>> 3) a string with a json serialization of an integer value, >>>> e.g. '"7"' 4) something else that should be ignored >>>> >>>> I need to transform this list into another list with values >>>> from options 1)-3) coerced to int. The code below should do >>>> this. >>>> >>>> >>>> Variant 1 === >>>> >>>> values = [] for c in l: # Case 1) or 2) try: c_int = int(c) >>>> except ValueError: pass else: values.add(c_int) continue >>>> >>>> # Case 3) try: c_int = int(json.loads(c)) except ValueError: >>>> pass else: values.add(c_int) continue >>>> >>>> === >>>> >>>> Is this code ugly? Does it follow EAFP? Am I missing >>>> something in language best practice? >>>> >>>> Or maybe below is more preferable way with a nested >>>> try...except clause? >>>> >>>> Variant 2 === values = [] for c in l: # Case 1) or 2) try: >>>> c_int = int(c) except ValueError: # Case 3) try: c_int = >>>> int(json.loads(c)) except ValueError: pass else: >>>> values.add(c_int) continue else: values.add(c_int) continue >>>> === >>>> >>>> Thanks, Anton. >>>> >>>> > > Your first example is perfectly fine and is EAFP > > >> Actually it doesn't work because there is no add function and it >> doesn't catch the TypeError function to ignore other exceptions >> than ValueError. Doesn't it? I tested in Python 2.7.6 and 3.4. > > Personally, I prefer to avoid nesting when it's not necessary. > >> -- https://mail.python.org/mailman/listinfo/python-list Ah ok, style-wise it was fine. - -- Martin Kemp (martin.kemp@ingg.com) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAEBAgAGBQJUUOD+AAoJEJ0Re0UIDzSu1KQIAK6aCMOv4VqOjmm/zoQrmzLf UGBCLwtHrnDkbXFAIweTSFiM1uf9TDaRpJqY1IrPbJHI4/EAP0Hu07nyx3V6HgzM /+Wb3DkpjW+JQoVqDSGzE/dTPJcU3/b1/EWWpbu72JHplqz9laEAFt9muWyDPs9u kDgM06mDd50lsi83W3i0H1iGL6YbLtsik+/x4G4mMjdq1o9BvRpUjkIiOx7yJ/BR OYzdudltXGqlXcToufHTU2lUv2C0RoHHNO4kytiLoUekCBdGE+Jy/6gQq/AKQu4G 0RYjCOnKNgugfdmDuHi0julPtTEzc+MdY/CcPob4cyy8RDzfQGklGKHP7f9+SJs= =hjWU -----END PGP SIGNATURE-----
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2014-10-30 22:10 +1100 |
| Message-ID | <54521c8f$0$12982$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #79905 |
Anton wrote:
> Let's say I have an incoming list of values *l*. Every element of *l* can
> be one of the following options:
> 1) an integer value
> 2) a string in form of '<int_value>', e.g. '7'
> 3) a string with a json serialization of an integer value, e.g. '"7"'
> 4) something else that should be ignored
>
> I need to transform this list into another list with values from options
> 1)-3) coerced to int. The code below should do this.
I don't particularly like either version. I prefer this:
def load_int(obj):
if isinstance(obj, int):
# Case 1), an int, e.g. 7
return obj
elif isinstance(obj, str):
# Case 2) and 3), a str or JSON serialised int.
# E.g. '7' or '"7"'.
try:
return int(obj)
except ValueError:
return int(json.loads(obj))
raise TypeError('require int or str, got %s' % type(obj).__name__)
load_int() covers the three cases you mention, and raises either ValueError
for malformed strings (e.g. 'x') or TypeError for things which aren't ints
(e.g. floats, dicts, etc.). Any other exception is probably a bug that
needs to be fixed.
Then, to cover case 4), ignoring everything else, you have a choice between
a procedural form:
values = []
for obj in l:
try:
values.append(load_int(obj))
except (ValueError, TypeError):
pass
or a functional form:
def tolerant_load_int(obj, default=None):
try:
return load_int(obj)
except (ValueError, TypeError):
return default
values = [n for n in map(tolerant_load_int, l) if n is not None]
# alternative to using map
values = [n for n in (tolerant_load_int(obj) for obj in l) if n is not None]
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | MRAB <python@mrabarnett.plus.com> |
|---|---|
| Date | 2014-10-30 12:21 +0000 |
| Message-ID | <mailman.15318.1414671698.18130.python-list@python.org> |
| In reply to | #79948 |
On 2014-10-30 11:10, Steven D'Aprano wrote:
> Anton wrote:
>
>> Let's say I have an incoming list of values *l*. Every element of *l* can
>> be one of the following options:
>> 1) an integer value
>> 2) a string in form of '<int_value>', e.g. '7'
>> 3) a string with a json serialization of an integer value, e.g. '"7"'
>> 4) something else that should be ignored
>>
>> I need to transform this list into another list with values from options
>> 1)-3) coerced to int. The code below should do this.
>
> I don't particularly like either version. I prefer this:
>
> def load_int(obj):
> if isinstance(obj, int):
> # Case 1), an int, e.g. 7
> return obj
> elif isinstance(obj, str):
> # Case 2) and 3), a str or JSON serialised int.
> # E.g. '7' or '"7"'.
> try:
> return int(obj)
> except ValueError:
> return int(json.loads(obj))
> raise TypeError('require int or str, got %s' % type(obj).__name__)
>
[snip]
How about:
int(str(obj).strip('"'))
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2014-10-31 09:48 +1100 |
| Message-ID | <5452c02a$0$12994$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #79951 |
MRAB wrote:
> On 2014-10-30 11:10, Steven D'Aprano wrote:
>> Anton wrote:
>>
>>> Let's say I have an incoming list of values *l*. Every element of *l*
>>> can be one of the following options:
>>> 1) an integer value
>>> 2) a string in form of '<int_value>', e.g. '7'
>>> 3) a string with a json serialization of an integer value, e.g. '"7"'
>>> 4) something else that should be ignored
>>>
>>> I need to transform this list into another list with values from options
>>> 1)-3) coerced to int. The code below should do this.
>>
>> I don't particularly like either version. I prefer this:
>>
>> def load_int(obj):
>> if isinstance(obj, int):
>> # Case 1), an int, e.g. 7
>> return obj
>> elif isinstance(obj, str):
>> # Case 2) and 3), a str or JSON serialised int.
>> # E.g. '7' or '"7"'.
>> try:
>> return int(obj)
>> except ValueError:
>> return int(json.loads(obj))
>> raise TypeError('require int or str, got %s' % type(obj).__name__)
>>
> [snip]
>
> How about:
>
> int(str(obj).strip('"'))
Absolutely not.
obj = '""""""""""""""1\n\n\n\n' # not valid JSON
load_int(obj)
=> raises ValueError
int(str(obj).strip('"'))
=> wrongly returns 1
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Denis McMahon <denismfmcmahon@gmail.com> |
|---|---|
| Date | 2014-10-31 04:55 +0000 |
| Message-ID | <m2v4o2$mle$1@dont-email.me> |
| In reply to | #79978 |
On Fri, 31 Oct 2014 09:48:10 +1100, Steven D'Aprano wrote:
> MRAB wrote:
>> How about:
>>
>> int(str(obj).strip('"'))
>
> Absolutely not.
>
> obj = '""""""""""""""1\n\n\n\n' # not valid JSON load_int(obj)
> => raises ValueError int(str(obj).strip('"'))
> => wrongly returns 1
How about
#!/usr/bin/python
import re, json
l = [1, -1, 0, '+2', '2', '-2', '0', '"+3"', '"3"', '"-3"', '"0"',
json.dumps(-4), json.dumps(4), json.dumps(0),
'x', 'sqjklsqjk__', (5, 6),
7.7, -7.7, '8.8', '+8.8', '-8.8', '"9.9"', '"+9.9"', '"-9.9"']
patt1 = re.compile(r'^([-+]?\d+)$')
patt2 = re.compile(r'^"([-+]?\d+)"$')
def getTheInt(x):
if isinstance(x,int):
return x
if isinstance(x,str):
tmp = patt1.match(x)
if tmp:
return int(tmp.group(1))
tmp = patt2.match(x)
if tmp:
return int(tmp.group(1))
return None
a = []
for n in l:
a.append(getTheInt(n))
print a
# end of code
prints:
[1, -1, 0, 2, 2, -2, 0, 3, 3, -3, 0, -4, 4, 0, None, None, None, None,
None, None, None, None, None, None, None]
I know re matching the strings may be overkill, but it may be the best
way of checking that the string contains the expected character format to
convert to an int.
--
Denis McMahon, denismfmcmahon@gmail.com
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2014-10-30 20:34 -0400 |
| Message-ID | <roy-7698C6.20335730102014@news.panix.com> |
| In reply to | #79948 |
In article <54521c8f$0$12982$c3e8da3$5496439d@news.astraweb.com>,
Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:
> Anton wrote:
>
> > Let's say I have an incoming list of values *l*. Every element of *l* can
> > be one of the following options:
> > 1) an integer value
> > 2) a string in form of '<int_value>', e.g. '7'
> > 3) a string with a json serialization of an integer value, e.g. '"7"'
> > 4) something else that should be ignored
> >
> > I need to transform this list into another list with values from options
> > 1)-3) coerced to int. The code below should do this.
>
> I don't particularly like either version. I prefer this:
>
> def load_int(obj):
> if isinstance(obj, int):
> # Case 1), an int, e.g. 7
> return obj
> elif isinstance(obj, str):
> # Case 2) and 3), a str or JSON serialised int.
> # E.g. '7' or '"7"'.
> try:
> return int(obj)
> except ValueError:
> return int(json.loads(obj))
> raise TypeError('require int or str, got %s' % type(obj).__name__)
Depending on how strictly you're trying to do input validation, the
int(json.loads(obj)) may not be what you want. It allows well-formed
json encoding floats, for example.
And, of course, since
>>> isinstance(True, int)
True
this code accepts booleans. Oh, but wait, that's by design :-)
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2014-10-31 14:52 +1100 |
| Message-ID | <54530792$0$12976$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #79981 |
Roy Smith wrote:
> In article <54521c8f$0$12982$c3e8da3$5496439d@news.astraweb.com>,
> Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:
>
>> Anton wrote:
>>
>> > Let's say I have an incoming list of values *l*. Every element of *l*
>> > can be one of the following options:
>> > 1) an integer value
>> > 2) a string in form of '<int_value>', e.g. '7'
>> > 3) a string with a json serialization of an integer value, e.g. '"7"'
>> > 4) something else that should be ignored
>> >
>> > I need to transform this list into another list with values from
>> > options 1)-3) coerced to int. The code below should do this.
>>
>> I don't particularly like either version. I prefer this:
>>
>> def load_int(obj):
>> if isinstance(obj, int):
>> # Case 1), an int, e.g. 7
>> return obj
>> elif isinstance(obj, str):
>> # Case 2) and 3), a str or JSON serialised int.
>> # E.g. '7' or '"7"'.
>> try:
>> return int(obj)
>> except ValueError:
>> return int(json.loads(obj))
>> raise TypeError('require int or str, got %s' % type(obj).__name__)
>
> Depending on how strictly you're trying to do input validation, the
> int(json.loads(obj)) may not be what you want. It allows well-formed
> json encoding floats, for example.
Really?
py> int(json.loads(json.dumps(23.5)))
23
Damn! You're right.
Back to Plan A:
elif isinstance(obj, str):
try:
return int(obj)
except ValueError:
if obj and obj.startswith('"') and obj.endswith('"'):
return int(obj[1:-1])
raise
But of course even the int() function itself may be a little more flexible
than we might want:
py> int(' 1 ')
1
So I guess the lessons are:
* before writing code, you need to decide what the code is meant to do;
* and that includes what input must be rejected, not just what input
must be accepted.
> And, of course, since
>
>>>> isinstance(True, int)
> True
>
> this code accepts booleans. Oh, but wait, that's by design :-)
Naturally :-)
If you wanted to avoid it, that's easy, add a clause:
if isinstance(obj, bool):
raise TypeError
at the start of the function.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Anton <anton.schattenfeld@gmail.com> |
|---|---|
| Date | 2014-11-12 12:31 -0800 |
| Message-ID | <b3889a96-91da-40a0-bad0-bd315c8ed7c3@googlegroups.com> |
| In reply to | #79948 |
On Thursday, October 30, 2014 4:10:23 AM UTC-7, Steven D'Aprano wrote:
> I don't particularly like either version. I prefer this:
>
> def load_int(obj):
> if isinstance(obj, int):
> # Case 1), an int, e.g. 7
> return obj
> elif isinstance(obj, str):
> # Case 2) and 3), a str or JSON serialised int.
> # E.g. '7' or '"7"'.
> try:
> return int(obj)
> except ValueError:
> return int(json.loads(obj))
> raise TypeError('require int or str, got %s' % type(obj).__name__)
This will definitely work, but then I don't see a benefit of EAFP and duck-typing:
Let's say I have a class
class FakeInt(object):
def __int__(self):
return 42
In this case:
>>> fi = FakeInt()
>>> isinstance(fi, int)
False
>>> int(fi)
42
So probably I need to rephrase 1) something, that I can cast to int.
Same for
> elif isinstance(obj, str):
As long as it behaves like string (can be some sort if bytearray). For particularly this case, it will probably won't happen, but again, it looks like an overloaded function with strongly typed argument.
or a functional form:
> def tolerant_load_int(obj, default=None):
> try:
> return load_int(obj)
> except (ValueError, TypeError):
> return default
> values = [n for n in map(tolerant_load_int, l) if n is not None]
> # alternative to using map
> values = [n for n in (tolerant_load_int(obj) for obj in l) if n is not None]
I like the idea of wrapping it up in a function and being able to use it in these functional forms.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web