Reputation: 77
I'm writing a function that receive an iterable of tuples, then I have to yield tuples that all elements are ints. Which the conditions:
For each element in the tuple:
otherwise, return None
For example:
list(func([(1, "48")])) = [(1, 48)]
list(func([(1, "0xf")])) = [(1, 15)]
list(func([(1, "junk")])) = None
list(func([([3], 4)])) = None
Here's my code so far, look nasty and doesnt work
def func(iterable):
for x in iterable:
for i in x:
if not ((isinstance(i, str) and i.isdigit()) or (isinstance(i, str) and isinstance(int(i, 16), int)) or isinstance(i, int)):
yield None
yield [i if isinstance(i, int) else int(i) if i.isdigit() else int(i, 16) for i in x for x in iterable]
Do you have a better solution for this>? i'm new to python, any help is greatly appreciated :)
Upvotes: 2
Views: 835
Reputation: 77359
My (untested) take:
def to_int(item):
try:
return int(item)
except ValueError:
return None
def func(iterable):
for input_tuple in iterable:
yield tuple(to_int(item) for item in input_tuple)
I assume "for each element in the tuple" if it is not possible to convert to int you will return None for that element, not for the whole tuple.
[update]
Now that you updated the question, there is no point using yield
if you want func([(1, "junk")])
to return None
when any unconvertible item occurs anywhere on the input; the whole point of yield
is to start returning values as soon as you compute them. For the desired output you have to hold the result until you process the whole list. Perhaps you misread your assignment?
Upvotes: 0
Reputation: 365995
This is a bit of a strange thing, but it looks like you want to handle any Python integer literal format as if it were an int
. The best way to do that is probably to call ast.literal_eval
to parse it, then see if it parsed to an int
. Like this:
def intify(x):
if isinstance(x, int):
return x
parsed = ast.literal_eval(x)
if isinstance(parsed, int):
return parsed
raise ValueError('{} is not an int literal'.format(x))
Note that you might want to instead test isinstance(x, numbers.Integral)
and if so return int(x)
, or even just return int(x)
whenever it succeeds, or something different, depending on what you want to do with things that are int
-like and int
-convertible but not actually int
, for some appropriate definition of int
-like.
Now, you just do this:
try:
return tuple(intify(x) for x in iterable)
except ValueError:
return None
And if you want to do that for a whole iterable of iterables, you need a nested loop, like this:
try:
return [tuple(intify(x) for x in subiterable) for subiterable in iterable]
except ValueError:
return None
If you want to do this lazily, yielding the elements without ever building a list, there's no way to "bail out early" and return None
—once you've already yielded some values, you can't unyield them. So, what you want isn't directly possible. But if you could explain why you want it, something similarly useful might be actually doable. For example:
yield from (tuple(intify(x) for x in subiterable) for subiterable in iterable)
Or, if you don't have Python 3.3:
for subiterable in iterable:
yield tuple(intify(x) for x in subiterable)
This will raise an exception on the first bad value. So, if you're just using the iterator to pass to a call to list
, the partially-built list will be abandoned to handle the exception.
Then again, if the only thing you're doing with this function is building a list, there's really no reason to use a generator in the first place; just return a list.
Upvotes: 4