Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #79905 > unrolled thread

Python Style Question

Started byAnton <anton.schattenfeld@gmail.com>
First post2014-10-29 03:42 -0700
Last post2014-11-12 12:31 -0800
Articles 14 — 7 participants

Back to article view | Back to comp.lang.python


Contents

  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

#79905 — Python Style Question

FromAnton <anton.schattenfeld@gmail.com>
Date2014-10-29 03:42 -0700
SubjectPython 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]


#79907

FromMartin Kemp <martin.kemp@ingg.com>
Date2014-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]


#79908

FromRafael Romero Carmona <rafaelromcar@gmail.com>
Date2014-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]


#79935

FromAnton <anton.schattenfeld@gmail.com>
Date2014-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]


#79909

FromRafael Romero Carmona <rafaelromcar@gmail.com>
Date2014-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]


#79938

FromAnton <anton.schattenfeld@gmail.com>
Date2014-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]


#79911

FromMartin Kemp <martin.kemp@ingg.com>
Date2014-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]


#79948

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2014-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]


#79951

FromMRAB <python@mrabarnett.plus.com>
Date2014-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]


#79978

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2014-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]


#79998

FromDenis McMahon <denismfmcmahon@gmail.com>
Date2014-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]


#79981

FromRoy Smith <roy@panix.com>
Date2014-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]


#79997

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2014-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]


#80624

FromAnton <anton.schattenfeld@gmail.com>
Date2014-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