How to convert an int to a list? - python

I have some code that might return either a single int, or a list. When it returns an int, I need to convert it into a list that contains only the int.
I tried the following, but it doesn't work:
newValue = list(retValue)
Apparently I can't do list(some int) because ints are not iterable. Is there another way of doing this?
Thanks in advance!

define your own function:
def mylist(x):
if isinstance(x,(list,tuple)):
return x
else:
return [x]
>>> mylist(5)
[5]
>>> mylist([10])
[10]

In Python, duck typing is preferable - don't test for a specific type, just test whether it supports the method you need ("I don't care if it's a duck, so long as it quacks").
def make_list(n):
if hasattr(n, '__iter__'):
return n
else:
return [n]
a = make_list([1,2,3]) # => [1,2,3]
b = make_list(4) # => [4]

Try to convert the variable to an int. If it is already an int this is a no-op. If it is a list then this raises a TypeError.
try:
return [int(x)]
except TypeError:
return x
Though using exceptions for flow control is generally frowned upon if the exceptional circumstance has a high probability of occurring. This is because processing exceptions is quite a lengthy task.
The other way is to use the isinstance operator.
if isinstance(x, list):
return x
else:
return [x]

listInt = intVal if isinstance(intVal,list) else [intVal]
this will always return a list if value is not a list.
Hope this helps

if isinstance(x,list): return x
else: return [x]
That's it.
Of course, this won't deal intelligently with other iterable types, but it's not clear that you want to treat all iterables as if they were lists (maybe you do, maybe you don't).

This is really just a variation on Hugh Bothwell's answer, but... if you want state-of-the-art duck typing, you can get the semantics of hasattr(rval, '__iter__') in a more attractive package with isinstance(rval, collections.Iterable). So...
def wrap_in_iterable(x):
if isinstance(x, collections.Iterable):
return x
else:
return [x]
Also, perhaps you want a list, and not just an iterable; to get list-like things but eliminate generator-like and dict-like things, collections.Sequence is handy. (Just don't pass an infinite generator to this function.)
def convert_to_sequence(x):
if isinstance(x, collections.Sequence):
return x
elif isinstance(x, collections.Iterable):
return list(x)
else
return [x]
These work because collections.Sequence and collection.Iterable define __subclasshook__s that perform the appropriate hasattr checks.
Finally, at the risk of being boring -- if you have control over the returning function, just return a one-item list if possible.

Related

How to handle edge case when iterating over cartesian product of sets in Python?

I have a function on zero or more Pyomo sets:
def myfunc(*sets):
if len(sets) == 0:
return # Do something else that is irrelevant here
indices = reduce(lambda x, y: x * y, sets) # Cartesian product of sets
for i in indices:
call_some_other_function(*i)
This fails when I pass it a single set of integers, like
import pyomo.environ
myset = pyomo.environ.Set(initialize=[1, 2])
myfunc(*myset)
because then I'm evaluating *i on an integer. What's an elegant way of handling this situation?
You can always check if it is an collections.Iterable to catch cases where it is not iterable (lists, sets, etc. are iterables - integer aren't):
from collections import Iterable
a = 1
isinstance(a, Iterable) # returns False
a = [1,2,3]
isinstance(a, Iterable) # returns True
so just do a check before you pass it into the function:
if isinstance(myset, Iterable):
myfunc(*myset)
else:
# something else
I think you're making things harder by implementing your own Cartesian product. Python's provided itertools.product since 2.6, and it works with any number of input sets.
import itertools
def args(*args):
return repr(args)
def for_each_index(*sets):
if not sets:
print('No sets given. Doing something else.')
return
for index in itertools.product(*sets):
print('Do something with ' + args(*index) + '...')
return
I added the args function solely to show the exact result of expanding *args. You don't need it, except maybe for debugging.
Note also that there is no need to call len to test if a tuple is non-empty. if not sets: will do.

Python: Passing a list through a recursive function call causes the list to become 'NoneType', why?

I have the following recursive function:
def recurse(y,n):
if len(y) == n:
return y
else:
return recurse(y.append(1),n)
When I run it:
x=recurse([],10)
I get the following error:
TypeError: object of type 'NoneType' has no len()
It seems that the function gets past the if statement the 1st time around, then it goes into the next level of recursion, and there, y.append(1) is 'NoneType', why is it not: '[1]' as expected? I have thought about this for a while and I can't seem to figure it out. Any insight is appreciated!
The problem is here:
y.append(1)
The append() method returns None, so you can't pass its result for building the output list (you'd have to first append to the list and then pass it, as shown in other answers). Try this instead:
def recurse(y, n):
if len(y) == n:
return y
else:
return recurse(y + [1], n)
The above solution is more in line with a functional programming style. Using append adds an element to an existing list - which will mutate a function parameter, in general not a very good idea. On the other hand y + [1] creates a new list each time, leaving the parameter untouched. Proponents of functional programming will tell you that's a Good Thing.
list.append() calls the append method on a list, and while it modifies the list, it returns None.
So it does not return the list.
You want something like:
def recurse(y,n):
if len(y) == n:
return y
else:
y.append(1)
return recurse(y,n) # explicitly pass the list itself
y.append operates on y in place and returns None

Python beginner (lists)

I need to write a function named make_ends that receives one parameter - a list. It returns a new list that contain the first and last items of the input list.
Here is my code, but it doesn't separate it into a list. I'm a little confused about how to go about this. Do I need to use the .join feature? If so, how do I do that? I think I am close.
def make_ends(x):
return x[0], x[-1]
Here was my earlier build, but it didn't do anything except return the original string:
def go_right(str):
if str >= 2:
a = str[-2:0] + str
return a
What was wrong with that?
Thanks for the help everyone.
You're actually very close; the only problem is that you're returning a tuple instead of a list. (Whatever a and b are, a, b is a tuple of those two things, just like [a, b] is a list of those two things.)
One way to solve this is to call list to make a list out of the tuple:
def make_ends(x):
return list((x[0], x[-1]))
But the easy way to do it is just to create a list in the first place:
def make_ends(x):
return [x[0], x[-1]]
You then ask another question:
Here was my earlier build, but it didn't do anything except return the original string:
def go_right(str):
if str >= 2:
a = str[-2:0] + str
return a
Let's go through this step by step.
First, str >= 2 is comparing a string to a number. In Python 2.x, either all strings are bigger than all numbers, or all strings are smaller than all numbers. (That's left up to the implementation.) So, this isn't a very useful check.
Maybe you wanted to check if len(str) >= 2 instead? But even then, I'm not sure what that would get you. If the length were 0 or 1, what would you want to do? As it is, you'd return None, which probably isn't right.
Meanwhile, str[-2:0] asks for all elements that come after 2-before-the-end, but before the start. There are no elements before the start, so this is empty. Then you add the original value to this empty collection, so you get the original value.
Also, as a side note, calling a parameter str is a bad idea. Besides the fact that it hides the built-in str (which is a function that turns anything into its string representation), it also strongly implies that what you're dealing with is a string, not a list.
Try the following
def make_ends(x):
return [x[0], x[-1]]
In the current form instead of a list you are creating a tuple. This is what the comma operator does when put between values
You're currently returning a tuple, not a list. Simply wrap it in square brackets to make it a list:
def make_ends(x):
return [x[0], x[-1]]
However, I'd question why you want a list in the first place. You know you're only returning two items, so you don't need it to be mutable - it seems to me perhaps that a tuple is what you want after all here.
There are indeed multiple ways of tackling this task
One would be the most common and probably most conventional
def make_ends(x):
return [x[0], x[-1]]
Another method
def make_ends(x):
finished = []
finished.append(x[0])
finished.append(x[1])
return finished
Also, you could turn the string into a list, allowing the function to work with strings
def make_ends(x):
x = list(x)
return [x[0], x[-1]]
you're very close. You just have to cast the return into a list.
return [x[0], x[-1]]
def make_ends(x):
return [x[0], x[-1]]

Python type comparision

Ok, so I have a list of tuples containing a three values (code, value, unit)
when I'm to use this I need to check if a value is an str, a list or a matrix. (or check if list and then check if list again)
My question is simply should I do like this, or is there some better way?
for code, value, unit in tuples:
if isinstance(value, str):
# Do for this item
elif isinstance(value, collections.Iterable):
# Do for each item
for x in value:
if isinstance(x, str):
# Do for this item
elif isinstance(x, collections.Iterable):
# Do for each item
for x in value:
# ...
else:
raise Exception
else:
raise Exception
The best solution is to avoid mixing types like this, but if you're stuck with it then what you've written is fine except I'd only check for the str instance. If it isn't a string or an iterable then you'll get a more appropriate exception anyway so no need to do it yourself.
for (code,value,unit) in tuples:
if isinstance(value,str):
#Do for this item
else:
#Do for each item
for x in value:
if isinstance(value,str):
#Do for this item
else:
#Do for each item
for x in value:
This works but every time you call isinstance, you should ask yourself "can I add a method to value instead?" That would change the code into:
for (code,value,unit) in tuples:
value.doSomething(code, unit)
For this to work, you'll have to wrap types like str and lists in helper types that implement doSomething()
An alternative to your approach is to factor out this code into a more general generator function (assuming Python 2.x):
def flatten(x):
if isinstance(x, basestring):
yield x
else:
for y in x:
for z in flatten(y):
yield y
(This also incorporates the simplifications suggested and explained in Duncan's answer.)
Now, your code becomes very simple and readable:
for code, value, unit in tuples:
for v in flatten(value):
# whatever
Factoring the code out also helps to deal with this data structure at several places in the code.
Just use the tuples and catch any exceptions. Don't look before you jump :)
Recursion will help.
def processvalue(value):
if isinstance(value, list): # string is type Iterable (thanks #sven)
for x in value:
processvalue(value)
else:
# Do your processing of string or matrices or whatever.
# Test each the value in each tuple.
for (code, value, unit) in tuples:
processvalue(value)
This is a neater way of dealing with nested structures, and will also give you the ability to process abitrary depths.

Avoiding the use of type() comparisons where polymorphism won't work

I came across the following in How to Think Like a Computer Scientist (here):
def recursive_sum(nested_num_list):
sum = 0
for element in nested_num_list:
if type(element) == type([]):
sum = sum + recursive_sum(element)
else:
sum = sum + element
return sum
I was shocked by the use of type(element) == type([]). Not only is it bad practice, but this function won't work for any other sequence types. Polymorphism is the typical way of avoiding type comparisons, but can't be used here. How could one avoid the type comparison in such a case? I considered:
def recursive_sum(nested_sum_list):
sum = 0
for element in nested_num_list:
try:
sum += element
except TypeError:
sum += recursive_sum(element)
return sum
which makes the function applicable to other sequences, but is still kinda gross. Thanks!
You can check if an element is a sequence by using isinstance(element, collections.Sequence).
"sum" functions takes an iterable, so I would check the element implements the __iter__ method or not,using "hasattr" builtin function.
Like this:
def recursive_sum(nested_num_list):
sum = 0
for element in nested_num_list:
if hasattr(element, '__iter__'):
sum = sum + recursive_sum(element)
else:
sum = sum + element
return sum
For the flattening of an aribtrarily nested list, you will always need some kind of check to test if an element is itself an iterable or a leaf node. I wouldn't combine the flattening with computing the sum in one function, but rather define a generator function that only does the flattening:
def flatten(x):
try:
it = iter(x)
except TypeError:
yield x
else:
for i in it:
for j in flatten(i):
yield j
This way, you will have all the ugly bits contained in a single function. For a nested sequence x, you can now do
sum(flatten(x))
to get the recursive sum.
Things that are true of a list:
>>> import collections
>>> hasattr(element, '__getitem__')
True
>>> not hasattr(element, 'keys')
True
>>> isinstance(element, collections.Sequence)
True
>>> hasattr(element, '__iter__')
True
Things that are true of a string:
>>> string = '1234'
>>> hasattr(string, '__getitem__')
True
>>> not hasattr(string, 'keys')
True
>>> isinstance(string, collections.Sequence)
True
>>> hasattr(string, '__iter__')
False
What you see here isn't polymorphism in any language I know. += for lists means one thing, for numbers another thing. You'd like += for lists to mean something unusual (sum up all elements and return the sum) - but this is only meaningful for your specific example. For other (most, I'd say) uses of lists, the original meaning of += is much more convenient.
To make this behave truly polymorphically, you can derive from list and make += mean what you want - then you won't need these hacks.
BTW:
if type(element) == type([]):
Should be rewritten to:
if isinstance(element, list):
The purpose of this function is not to be universally applicable for adding nested structures, it was simply created to illustrate recursion.
Adding more complex sequence type checking, try and except, or the ability to add something other than numbers would make the function less useful as a learning tool for recursion.
That being said, isinstance(element, (list, tuple)) would probably be more appropriate here, and it wouldn't add any complexity.
You were checking if the element can be added to a int, which is not what you wanted.
The try is not bad though: Try to use it as a iterable - if it works then it is a iterable:
def recursive_sum(nested_sum_list):
sum = 0
# this raises TypeError if element is not a sequence
for element in nested_num_list:
try:
sum += recursive_sum(element)
except TypeError:
sum += element
return sum
There is also a typeclass for iterables:
import collections
print isinstance(element, collections.Iterable)
which basically just searches for a __iter__ method.

Categories

Resources