Create tuple if not already one - python

How do I create a simple tuple containing a variable of any time without creating a tuple of tuples? For example a function can accept either an int or a tuple of ints. Inside the function I want to make sure that the variable is in fact a tuple.
So far I could think of
a = tuple(a)
and
a = (a,)
However the first one doesnt work if a is not iterable and the second one creates a tuple of a tuple if a is already one.
((1,2),)
I feel like I should be able to do that without checking the type first... What am I missing?

You can use exception handling; try calling iter() on the object, for example
try:
iter(a)
except TypeError:
# not an iterable, assume a single value
a = (a,)
If you were planning to iterate over the tuple to handle values anyway, you just store the output of the iter() call and use it directly:
try:
a = iter(a)
except TypeError:
a = (a,)
for elem in a:
You can also make your function signature accept multiple parameters:
def foobar(*a):
for elem in a:
Now a is always a tuple, but to pass in a tuple you'd have to call it with:
sometuple = (1, 2, 3)
foobar(*sometuple)
while a single value is passed in as:
foobar(singlevalue)

You can use a lambda function:
a = (lambda x: x if type(x) is tuple else (x,))(a)
Perhaps more verbose than what you wanted, but it allows you to keep it all in one line.
Unfortunately, there's really no way to do it without checking the type of the argument. When you try to construct a tuple from another tuple - e.g. t = (tup,) - it's treated as a generic Python object, i.e. it doesn't "know" that it's a tuple. In order for it to auto-expand the tuple, it will have to check it's type first.
A non-iterable object would also have to be identified, because it doesn't contain the necessary methods to iterate over it's elements. In other words, a non-iterable - e.g. a scalar - isn't just an iterable with a single element. They're completely different object types. It can be placed in a tuple, but not converted to one.
When you use the tuple function to create a tuple, it just treats the input as a generic iterable. It doesn't know that it's a tuple.
So whether the type check is done internally by tuple(), or by the calling function, it will still have to be done.

Related

what's the difference between list(temp) or [temp]

I am getting some errors using list like it works when i try this
new_list=new_list+[temp]
but it didn't work when i use list(temp)
new_list=new_list+list(temp)
You have the error because you are trying to make a list with an integer:
temp = 1
print(list(temp))
output:
TypeError: 'int' object is not iterable
The list() constructor returns a mutable sequence list of elements. The iterable argument is optional. You can provide any sequence or collection (such as a string, list, tuple, set, dictionary, etc). If no argument is supplied, an empty list is returned.
The example below works fine because you are putting an integer inside a list:
temp = 1
print([temp])
output:
[1]
The list has been constructed using a pair of square brackets.
According to python official documentation, list object can be constructed in several ways:
Using a pair of square brackets to denote the empty list: []
Using square brackets, separating items with commas: [a], [a, b, c]
Using a list comprehension: [x for x in iterable]
Using the type constructor: list() or list(iterable)
So, [] is syntax literal and list() is builtin constructor function. For the latter, the argument accepted is iterable (If no argument is given, the constructor creates a new empty list, []), that's the reason list(1) throws TypeError.

How to pass tuple and variable as args

I need to pass a large tuple and a single variable into a threaded task as arguments.
excelbtn_text.set("Outputting...")
excelClass = excelCL()
excel_thread = threading.Thread(target=excelClass.excelOut, args=(dcf_data_tuple, excelbtn_text))
excel_thread.daemon = True
excel_thread.start()
However I receive an error, TypeError: excelOut() missing 242 required positional arguments. Is there anyway I can get past this problem?
The error never occurred while I was only passing the tuple as an argument.
First, to pass a tuple and another value, you can just make another tuple with two members—the big tuple, and the other value. Exactly as you're doing:
args=(dcf_data_tuple, excelbtn_text)
But the arguments you pass have to match the method's function definition. Passing a valid tuple of 2 values as the arguments for a method doesn't work unless that method takes 2 parameters.
To resolve your confusion, first, this does not mean what you think it does:
args=(dcf_data_tuple)
Parentheses do not create a tuple; commas create a tuple. In other words, (2) is not a 1-element tuple containing the number 2, it's just the number 2. And (dct_data_tuple) is not a 1-element tuple containing the tuple dct_data_tuple, it's just dct_data_tuple.
So, the function definition for excelClass.excelOut is presumably taking not a single giant tuple as a parameter, but rather hundreds of separate parameters.
This is a bizarre design, but it's not actually illegal.
And that matches the exception you're getting: when you pass it 2 arguments (the first of which is a giant tuple), rather than hundreds arguments, it complains that you're missing 242 positional arguments:
TypeError: excelOut() missing 242 required positional arguments
The simplest way to fix this is to give excelOut a reasonable signature that matches what you want to pass it:
def excelOut(self, data_tuple, text):
# do stuff here
If you for some reason can't change its definition, then you have to look at what the definition is, and try to match it. If, for example, it looks like this:
def excelOut(self, data0, data1, … hundreds more, …, text):
… then you have to call it like this:
args=dcf_data_tuple + (excelbtn_text,)
Notice the comma at the end. That means (excelbtn_text,) is a 1-element tuple. And then we add the giant tuple to the 1-element tuple and get back a giant-plus-1-element tuple, which now matches the method's parameters.
If I understood your problem, you can add an asterisk before your tuple to pass the arguments unwrapped:
excelbtn_text.set("Outputting...")
excelClass = excelCL()
excel_thread = threading.Thread(target=excelClass.excelOut, args=(*dcf_data_tuple, excelbtn_text))
excel_thread.daemon = True
excel_thread.start()

Python: Create list from function that returns single item or another list

(Python 3.5).
Problem Statement: Given a function that returns either an item or a list of items, is there a single line statement that would initialize a new list from the results of calling the aforementioned function?
Details: I've looked at the documents on python lists, and tried some things out on the repl, but I can't seem to figure this one out.
I'm calling a third party function that reads an xml document. The function sometimes returns a list and sometimes returns a single item (depending on how many xml entries exist).
For my purposes, I always need a list that I can iterate over - even if it is a length of one. The code below correctly accomplishes what I desire. Given Python's elegance, however, it seems clunky. I suspect there is a single-line way of doing it.
def force_list(item_or_list):
"""
Returns a list from either an item or a list.
:param item_or_list: Either a single object, or a list of objects
:return: A list of objects, potentially with a length of 1.
"""
if item_or_list is None: return None
_new_list = []
if isinstance(item_or_list, list):
_new_list.extend(item_or_list)
else:
_new_list.append(item_or_list)
return _new_list
Thanks in advance,
SteveJ
If you're looking for a one-liner about listifying the result of a function call:
Let's say there's a function called func that returns either an item or a list of items:
elem = func()
answer = elem if isinstance(elem, list) else [elem]
That being said, you should really refactor func to return one type of thing - make it return a list of many elements, or in the case that it returns only one element, make it return a list with that element. Thus you can avoid such type-checking
You may check it like in one line as:
if item: # Check whether it is not None or empty list
# Check if it is list. If not, append it to existing list after converting it to list
_new_list.extend(item if isiinstance(item, list) else [item])
Another way of doing this in a single line is
def force_list(item_or_list=[]):
return item_or_list if type(item_or_list) is list else [item_or_list]
print force_list("test")
test
print force_list(["test","test2"])
["test","test2"]

How to understand variable-length argument in python function?

I want to write my own sum function to get the sum of a variable-length argument.
def myadd(*tuple):
sum=0
for element in tuple:
sum=sum+element
return(sum)
call method 1:
myadd(*(1,2,3,4))
It is the most formal way to call the function.no problem here.
call method 2:
myadd(1,2,3,4)
It also can get the result,why?
call method 3:
myadd((1,2,3,4))
error TypeError: unsupported operand type(s) for +: 'int' and 'tuple'.
In my point of view,call 2 and call 3 can not be accepted by python,there are no * operator in the position of arguments?can you tell me the pricinple of the operation on python function ?
You're mixing up variable-argument parameters and argument unpacking. This is a common mistake for beginners, because they both use the same * for syntax, and they're not completely unrelated… but they're not nearly as closely related as you think.
These two calls do the exact same thing:
myadd(*(1,2,3,4))
myadd(1, 2, 3, 4)
What the * means here is "take the following iterable, and unpack it into a bunch of separate arguments.
It doesn't matter whether the function you're calling was defined as f(*args), f(a, b, c, d), or f(a, b, *args), you're passing it 4 arguments.
This means method 1 is not "the most formal way to call the function"; in fact, it's just an obfuscated version of method 2.
This, on the other hand, does not do the same thing:
myadd((1, 2, 3, 4))
That passes a single argument, which happens to be a tuple.
So, your function is defined like this:
def myadd(*tuple):
This means whatever it arguments it's passed, no matter how they're passed (except for keyword arguments, but let's ignore that for the moment), they're going to be tossed into a list named tuple. So, let's look at your three cases.
In the first case, you're passing 4 arguments, all of which are integers. So, tuple gets a list of 4 integers. When you iterate over that list, each member is an integer, so you can add them up with no problem.
In the second case—which, again, is exactly the same—you're passing 4 integers, so tuple gets a list of 4 integers.
In the third case, you're passing 1 argument, which is a tuple, so tuple gets a list of 1 tuple. When you iterate over that list, each member is a tuple, and you can't add that to a number.
For more details, see Arguments and parameters, which has links to all the useful places to look in the docs, and hopefully a readable overview.
You are passing the whole tuple as one argument, and tuples cannot be added to numbers. If you want to pass all the tuple elements as individual arguments, use the * operator:
myadd(*x)
def myadd(x):
sum=0
for element in x:
sum=sum+element
return(sum)
x=(1,2,3)
print myadd(x)
output
6

Don't understand this python For loop

I'm working through a tutorial which includes this code:
for position, target in population_gen(population):
pos = float(position)
all_inputs.append([random.random(), pos * factor])
all_targets.append([target])
I don't fully understand how the for loop works. In particular: what is the loop iterating through exactly? I'm only familiar with simple examples like for i in mylist:. How can there be a function call on the right-hand side of in, and two things separated by a comma on the left-hand side?
The function population_gen is returning a list of tuples, which are unpacked automatically into variable names using this syntax.
So basically, you're getting something like the following as return value from the function:
[("pos1", "target1"), ("pos2", "target2"), ]
Given this example, in the the for loop's first iteration, the variables "position" and "target" will have the values:
position = "pos1"
target = "target1"
In second iteration:
position = "pos2"
target = "target2"
Tuple unpacking.
for a, b in [(1, 2), (3, 4)]:
print a
print b
print 'next!'
And the function is just a function.
The function either returns a sequence or serves as something called a "generator:" it spits out successive elements in a sequence for the caller to iterate through. This question concerning the yield keyword has some thorough discussion of how these work.
As for the comma, since the function (apparently) returns a two-tuple, the comma-separated list of names is a convenient way to name individual elements of the tuple without having to unpack them yourself.
It's called tuple unpacking. The population_gen (generator) function yields tuples containing exactly two elements. In python, you can assign several variables to tuples like this
a, b = (1, 2)
So in this for loop, you directly put the two tuple values from the current iteration item into your two variables position and target.

Categories