I need some help understanding a function that i want to use but I'm not entirely sure what some parts of it do. I understand that the function is creating dictionaries from reads out of a Fasta-file. From what I understand this is supposed to generate pre- and suffix dictionaries for ultimately extending contigs (overlapping dna-sequences).
The code:
def makeSuffixDict(reads, lenSuffix = 20, verbose = True):
lenKeys = len(reads[0]) - lenSuffix
dict = {}
multipleKeys = []
i = 1
for read in reads:
if read[0:lenKeys] in dict:
multipleKeys.append(read[0:lenKeys])
else:
dict[read[0:lenKeys]] = read[lenKeys:]
if verbose:
print("\rChecking suffix", i, "of", len(reads), end = "", flush = True)
i += 1
for key in set(multipleKeys):
del(dict[key])
if verbose:
print("\nCreated", len(dict), "suffixes with length", lenSuffix, \
"from", len(reads), "Reads. (", len(reads) - len(dict), \
"unambigous)")
return(dict)
Additional Information: reads = readFasta("smallReads.fna", verbose = True)
This is how the function is called:
if __name__ == "__main__":
reads = readFasta("smallReads.fna", verbose = True)
suffixDicts = makeSuffixDicts(reads, 10)
The smallReads.fna file contains strings of bases (Dna):
"> read 1
TTATGAATATTACGCAATGGACGTCCAAGGTACAGCGTATTTGTACGCTA
"> read 2
AACTGCTATCTTTCTTGTCCACTCGAAAATCCATAACGTAGCCCATAACG
"> read 3
TCAGTTATCCTATATACTGGATCCCGACTTTAATCGGCGTCGGAATTACT
Here are the parts I don't understand:
lenKeys = len(reads[0]) - lenSuffix
What does the value [0] mean? From what I understand "len" returns the number of elements in a list.
Why is "reads" automatically a list? edit: It seems a Fasta-file can be declared as a List. Can anybody confirm that?
if read[0:lenKeys] in dict:
Does this mean "from 0 to 'lenKeys'"? Still confused about the value.
In another function there is a similar line: if read[-lenKeys:] in dict:
What does the "-" do?
def makeSuffixDict(reads, lenSuffix = 20, verbose = True):
Here I don't understand the parameters: How can reads be a parameter? What is lenSuffix = 20 in the context of this function other than a value subtracted from len(reads[0])?
What is verbose? I have read about a "verbose-mode" ignoring whitespaces but i have never seen it used as a parameter and later as a variable.
The tone of your question makes me feel like you're confusing things like program features (len, functions, etc) with things that were defined by the original programmer (the type of reads, verbose, etc).
def some_function(these, are, arbitrary, parameters):
pass
This function defines a bunch of parameters. They don't mean anything at all, other than the value I give to them implicitly. For example if I do:
def reverse_string(s):
pass
s is probably a string, right? In your example we have:
def makeSuffixDict(reads, lenSuffix = 20, verbose = True):
lenKeys = len(reads[0]) - lenSuffix
...
From these two lines we can infer a few things:
the function will probably return a dictionary (from its name)
lenSuffix is an int, and verbose is a bool (from their default parameters)
reads can be indexed (string? list? tuple?)
the items inside reads have length (string? list? tuple?)
Since Python is dynamically typed, this is ALL WE CAN KNOW about the function so far. The rest would be explained by its documentation or the way it's called.
That said: let me cover all your questions in order:
What does the value [0] mean?
some_object[0] is grabbing the first item in a container. [1,2,3][0] == 1, "Hello, World!"[0] == "H". This is called indexing, and is governed by the __getitem__ magic method
From what I understand "len" returns the number of elements in a list.
len is a built-in function that returns the length of an object. It is governed by the __len__ magic method. len('abc') == 3, also len([1, 2, 3]) == 3. Note that len(['abc']) == 1, since it is measuring the length of the list, not the string inside it.
Why is "reads" automatically a list?
reads is a parameter. It is whatever the calling scope passes to it. It does appear that it expects a list, but that's not a hard and fast rule!
(various questions about slicing)
Slicing is doing some_container[start_idx : end_idx [ : step_size]]. It does pretty much what you'd expect: "0123456"[0:3] == "012". Slice indexes are considered to be zero-indexed and lay between the elements, so [0:1] is identical to [0], except that slices return lists, not individual objects (so 'abc'[0] == 'a' but 'abc'[0:1] == ['a']). If you omit either start or end index, it is treated as the beginning or end of the string respectively. I won't go into step size here.
Negative indexes count from the back, so '0123456'[-3:] == '456'. Note that [-0]is not the last value,[-1]is. This is contrasted with[0]` being the first value.
How can reads be a parameter?
Because the function is defined as makeSuffixDict(reads, ...). That's what a parameter is.
What is lenSuffix = 20 in the context of this function
Looks like it's the length of the expected suffix!
What is verbose?
verbose has no meaning on its own. It's just another parameter. Looks like the author included the verbose flag so you could get output while the function ran. Notice all the if verbose blocks seem to do nothing, just provide feedback to the user.
Related
I am new to python and i have this project I am working on a small project with two functions where the first returns the index of the first time a difference is spotted in a string. The next function does that but in a list of strings. Now, due to my being an amateur, i have used an excessive amount of if and else statements which resulted in too many return statements especially in the second function, and i get the error [R1710: inconsistent-return-statements]. How do i fix it and can anybody give me clear examples to better pieces of code? Sorry for the question being so long.
IDENTICAL = -1
def singleline_diff(line1, line2):
"""
Inputs:
line1 - first single line string
line2 - second single line string
Output:
Returns the index where the first difference between
line1 and line2 occurs.
Returns IDENTICAL if the two lines are the same.
"""
len1 = len(line1)
len2 = len(line2)
minimum_length = min(len1, len2)
if len1 != len2:
if minimum_length == 0:
return 0
for idx in range(minimum_length):
if line1[idx] == line2[idx]:
pass
else:
return idx
return idx + 1
for idx in range(len1):
if line1[idx] == line2[idx]:
pass
else:
return idx
return IDENTICAL
def multiline_diff(lines1, lines2):
"""
Inputs:
lines1 - list of single line strings
lines2 - list of single line strings
Output:
Returns a tuple containing the line number (starting from 0) and
the index in that line where the first difference between lines1
and lines2 occurs.
Returns (IDENTICAL, IDENTICAL) if the two lists are the same.
"""
line_no = singleline_diff(lines1, lines2)
len_lines1, len_lines2 = len(lines1), len(lines2)
if len_lines1 == len_lines2:
if (len_lines1 or len_lines2) == 0:
if len_lines1 == len_lines2:
return (IDENTICAL, IDENTICAL)
else:
idx = singleline_diff(lines1[line_no], lines2[line_no])
return (line_no, idx)
else:
idx = singleline_diff(lines1[line_no], lines2[line_no])
if line_no == IDENTICAL:
return (IDENTICAL, IDENTICAL)
elif line_no != IDENTICAL:
return (line_no, idx)
else:
return (line_no, 0)
Where was a semantic mistake in OP's code is in Abhishek Arya's answer
TL;DR - early return:
def your_function():
if not should_do():
return # NO RETURN VALUE!
# rest of the function
...yes, this will no longer emit the inconsistent-return-statements ;)
This Q/A pops also when you search for inconsistent-return-statements, I want to give a brief "common problems" guide for those.
Case A: return value is irrelevant, you just want to exit function early
There are cases, where there are functions (or "procedures" if you want to get technical about it) that just do something, but are not expected to have any return values AT ALL,
at the same time, there may be e.g. some sort of check at the start of the function whether this function run even makes sense, what may first come to your mind, is wrapping the whole function code in an if statement:
def your_function(article):
if display_content():
content = get_content(article)
# do some extensive logic to generate final content
# ...
print(content)
...this is oversimplified, but let's hope you can imagine how such coding can pretty quickly fall into a "spaghetti code" if there are more checks and more code in general + it also steals that one "tab" of a space that you so desperately need to fit into your project's max line length.
Luckily, same as in many other programming languages, there IS a way of an early ending of a function by returning at ANY place within the function run, meaning in any "Control Flow" - including if/elif/else, for/while loops, ...
Now you'd probably jump quick to just return None, False, etc. although it would work, you'd still get the pylint inconsistent-return-statements warning - to understand why let's see the warning's message:
Either all return statements in a function should return an
expression, or none of them should. pylint(inconsistent-return-statements)
From pylint's point of view, if you put anything after the return it will be considered as an expression. So what to do? Actually, in Python, you CAN return "nothing" (again this is not unique to Python)
def your_function(article):
if not display_content():
return
content = get_content(article)
# do some extensive logic to generate final content
# ...
print(content)
Although in Python returning "nothing" should be (and technically, to my knowledge, it is) an equivalent of return None, by physically writing "None" you are expressing the intention no matter the implicity of it.
Don't confuse this though with pylint(assignment-from-none) (Assigning result of a function call, where the function returns None) - where both "return" AND "return None" are considered as returning None!
Case B: Your function has a case when it doesn't return
Quite common mistake especially in a larger code is to create a code part which results in simply not returning anything. This is not exactly OP's case, since they used just a negation of the same condition, but pylint doesn't know that, so here's its thought process:
if SOME_CONDITION: # ok, here's just another condition
return someReturnExpression # and ok, it returns SOMETHING, let's note that
elif OPPOSITE_OF_SOME_CONDITION: # ok, here's just another condition
return someReturnExpression # and ok, it returns SOMETHING, let's note that
# WAIT ! What?! THERE WAS NO "else:"! Hmmm...
# ...what happens if both conditions fail? NOTHING WOULD BE RETURNED!
# We need to make a warning about that!
# (fact that sometimes they return SOMETHING and sometimes NOTHING)
So this inconsistent-return-statements could be resolved with
if SOME_CONDITION: # ok, here's some condition
return someReturnExpression # and ok, it returns SOMETHING, let's note that
else: # ok, here's else
return someReturnExpression # and ok, it returns SOMETHING, let's note that
# Ok, so if returns SOMETHING, else returns SOMETHING,
# so SOMETHING is returned every time! that's good!
...this in itself works, but it will generate yet another pylint issue
Unnecessary "else" after "return" pylint(no-else-return)
See python actually encourages early returns since it often leads to a cleaner code.
return during function run ENDS(/exits) the function and pylint sees that - it sees that if the condition was true, the function code would simply end there - so what it, Abhishek Arya, me and many others suggest is simply continuing with the code after the if part:
if SOME_CONDITION:
return someReturnExpression
# ... some more code ...
# ... some more code ...
return someReturnExpression
Case C: Combination
Simply don't combine "just" return with return SOMETHING,
if you really need to return None, simply explicitly return None in that case
def get_article(id):
article = find_article(id)
if article.id == 0:
return None
return article
This is just an example, this is not how you'd really check for some articles ;)
Look at the code here:
if len_lines1 == len_lines2:
return (IDENTICAL, IDENTICAL)
else:
idx = singleline_diff(lines1[line_no], lines2[line_no])
return (line_no, idx)
You could have written the above thing like:
if len_lines1 == len_lines2:
return (IDENTICAL, IDENTICAL)
idx = singleline_diff(lines1[line_no], lines2[line_no])
return (line_no, idx)
You just don't need an else block to return this expression as this part of code will automatically be called if the control doesn't go into if block. Hope it helps.
My question is about how to deal with the piece of code where I am using the Caesar´s cipher.
Functions Decrypt and Encrypt have to deal with the limits of the alphabet (A - Z and a - z). I tried to write the two possible cycles for both alphabets in one cycle function named cycleencrypt.
But the function takes about 6 arguments and I have read somewhere that is less readable and understandable having more than 3 arguments in one function so my question is:
Should I reduce the number of arguments by splitting in two functions and make the piece of code longer (but maybe more understandable)?
Thanks for any answer I aprreciate that.
EDIT: Docstrings around the functions were deleted to make visible the
main purpose of my question.
def offsetctrl(offset):
while offset < 0:
offset += 26
return offset
def cycleencrypt(string, offset, index, listing, first, last):
offset = offsetctrl(offset)
if string >= ord(first) and string <= ord(last):
string += offset
while string > ord(last):
string = ord(first) + (string - ord(last) -1)
listing[index] = chr(string)
Cycle for encrypting with a lots of arguments and control of negative offset´s
def encrypt(retezec, offset):
listing = list(retezec)
for index in range(0, len(retezec)):
string = ord(retezec[index])
cycleencrypt(string, offset, index, listing, 'A', 'Z')
cycleencrypt(string, offset, index, listing, 'a', 'z')
print(''.join(listing))
main encryption part taking many arguments in two lines with printing
def decrypt(retezec, offset):
return encrypt(retezec, -offset)
if __name__ == "__main__":
encrypt("hey fellow how is it going", 5)
decrypt("mjd kjqqtb mtb nx ny ltnsl", 5)
In this kind of situation, it's often better to write your code as a class. Your class's constructor could take just the minimum number of arguments that are required (which may be none at all!), and then optional arguments could be set as properties of the class or by using other methods.
When designing a class like this, I find it's most useful to start by writing the client code first -- that is, write the code that will use the class first, and then work backwards from there to design the class.
For example, I might want the code to look something like this:
cypher = Cypher()
cypher.offset = 17
cypher.set_alphabet('A', 'Z')
result = cypher.encrypt('hey fellow how is it going')
Hopefully it should be clear how to work from here to the design of the Cypher class, but if not, please ask a question on Stack Overflow about that!
If you want to provide encrypt and decrypt convenience methods, it's still easy to do. For example, you can write a function like:
def encrypt(text, offset):
cypher = Cypher()
cypher.offset = offset
return cypher.encrypt(text)
Here is the docstring of datetime.datetime:
class datetime(date):
"""datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
...
"""
And the signature of its constructor:
def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
What we could learn from it:
Add exactly as many arguments as it makes sense to add
Use parameters and to give sensible default values to arguments
Side thought: do you think users of your library would should use cycleencrypt()? You could mark it private (with underscore), so everybody will see it's not a public API and they should use encrypt() and decrypt() instead.
The number of arguments doesn't really matters as long as there are not a dozen of them (maybe someone can link to what you mention about having more than 3 arguments, I may be wrong).
To be more readable in the definition of a function, write comments by following docstrings convention.
To be more readable at the call of a function, gives default values in the definition as much as possible for the more useful values (for example, offset can have the value 1 by default, and index 0).
Either way, for a long line, use PEP8 guidelines which describes a way to jump lines correctly (the lines must not exceed 80 characters, according to PEP8).
def cycleencrypt(string, offset=1, index=0,
listing, first, last):
"""Description
:param string: description
:param offset: description
:param index: description
:param listing: description
:param first: description
:param last: description
:return description
"""
offset = offsetctrl(offset)
if string >= ord(first) and string <= ord(last):
string += offset
while string > ord(last):
string = ord(first) + (string - ord(last) - 1)
listing[index] = chr(string)
def mysum(L):
return 0 if not L else L[0] + mysum(L[1:])
def mysum(L):
return L[0] if len(L) == 1 else L[0] + mysum(L[1:])
def mysum(L):
first, *rest = L
return first if not rest else first + mysum(rest)
The latter two also work on a single string argument e.g mysum('spam') because strings are sequences of one-character strings.
The third variant works on arbitrary iterables, including open input files mysum(open(name)), but the others do not because they use index.
The function header def mysum(first *rest), although similar to the third variant, because it expects individual arguments not a single iterable.
The author seems to be implying that the variant with (first, *rest) as the input arguments wouldn't work with files but after experimenting with it, I found that it does work.
# Code I tried:
def mysum(first, *rest):
return first if not rest else first + mysum(*rest)
mysum(*open("script1.py")) works fine.
I think mysum(open("script1.py")) won't work because that what python would then see is first = open("script1.py and rest = [] which means it's gonna give me the <_io.TextIOWrapper name='script1.py' mode='r' encoding='cp1252'> because not [] is true.
The author wants a function that takes an iterable (e.g. a list, tuple, etc) as input and returns the sum, e.g. like this:
mysum(open("script1.py"))
When you write
mysum(*open("script1.py"))
This is roughly equivalent to
f = open("script1.py").readlines()
mysum(f[0], f[1], ..., f[n])
Note that here your code does not take an interable as input, instead it takes several separate arguments which is not what the author wanted.
Using a tuple to explain what happens. The *sequence syntax is used for unpacking.
numbers = (1, 2, 3)
mysum(*numbers) # this happens: mysum(1, 2, 3)
is equivalent to mysum(1, 2, 3). The members are taken from the iterable and fed into the function as arguments. Using *open('path/to/file') causes the file to be opened and its contents passed into mysum(L) as arguments. This is equivalent to mysum(open('path/to/file').read())
I am brushing up a bit of good old algorithms, and doing it with python, since I use it more often nowadays.
I am facing an issue when running a recursive function; where the variable get reset every time that the recursive function call itself:
def recursive_me(mystring):
chars = len(mystring)
if chars is 0:
print("Done")
else:
first = int(str[0])
total = + first
print(total)
recursive_me(mystring[1:])
recursive_me("4567")
What I am doing here is to get a string made of digits; take the first, convert it to an int; and run recursively the function again, so I can take one digit at time from the string and sum all the values.
Ideally the output should show the total, while it add all the digits (4+5+6+7), although when the recursive function is called the first time, the function reset the total value.
Is common habit to use global variables when running operations with recursive functions or am I doing something wrong?
You can code as simply as this:
def recursive_me(mystring):
if mystring: # recursive case
return int(mystring[0]) + recursive_me(mystring[1:])
else: # base case
return 0
or
def recursive_me(mystring, total = 0):
if mystring: # recursive case
return recursive_me(mystring[1:], total + int(mystring[0]))
else: # base case
return total
although this won't help much in Python since it doesn't implement tail-call optimisation.
If you want to see the intermediate values, change the second version like so:
def recursive_me(mystring, total = 0):
if mystring: # recursive case
newtotal = total + int(mystring[0])
print(newtotal)
return recursive_me(mystring[1:], newtotal)
else: # base case
return total
then
4
9
15
22
22 # this is the return value; previous output is from `print()`
as a foreword: a lot of answers received meaningful edits in the meantime I was writing this answer. Don't hold it against me.
I'm throwing my two cents in here just because there's a lot of over-complicated answers.
This is a corrected copy-paste of the OP's effort.
def recursive_me(mystring, total=0):
chars = len(mystring)
if chars is 0:
print("Done")
return total
else:
first = int(mystring[0])
total += first
print(total)
recursive_me(mystring[1:], total)
first what happens is that we check the base case, if there's no left chars in the string. If the string length is 0 we return the total calculated ammount.
Otherwise, we turn the first of the chars into an int, and add it to total. The first error you have is that you wrote str[0]. str is a python built in type and the produced error would be something like "str is not subscriptable".
This error means that the str can't be operated on by "[]" operator. The same would happen if you tried doing 1[0] because 1 is a integer. The "[]" operator can only operate on lists, tuples and strings (I might've forgot some built-in type).
The second error you had was with the addition part. You had written total = + first but the operator you are looking for is the += which in fact is just a shortened way to write a = a+b.
Additionally, your original question was concerning about "python" forgetting the value of "total". This is because you have to either pass that value forward, or write your recursive function in a way that "forces" it to, what's called, evaluate your next call to your function on the spot.
In my example I'm sending the next call of the function recursive_me, the current total value. In the example given by #uselpa; above he's making python evaluate the next call to the function by putting it after operator +:
return int(mystring[0]) + recursive_me(mystring[1:])
this then gets to be (for recursive_me("4567"))
return int(4)+recursive_me("567")
return int(4)+int(5)+recursive_me("67")
....
return int(4)+int(5)+int(6)+int(7)+0
because python needs to return a value here, but the expression keeps calling new functions and python can't return until it evaluates all of them to a final number (in this case at least).
The common practice is to save these variables as parameters, and pass them along the chain. It seems in your case, you would want to pass total as an additional parameter, and update it as needed.
There's also a neat functional way to do it in python
t=raw_input()
print reduce(lambda a, b: a+b, map(int,t))
This is recursive in nature.
Some pointers:
Your default case should return an actual number (0 in your case) and not just print done.
total = + first is setting total to first, not adding first to total. You would need total += first to do the latter.
The trick with "retaining" the value of your current total is to "save" it in the recursive call-chain itself by passing it along with each call. You won't need a global variable or a default parameter to do this.
Here's a solution:
def recursive_me(mystring):
if not mystring: # True if mystring is empty
return 0
return int(mystring[0]) + recursive_me(mystring[1:])
print(recursive_me("4567")) # 22
Here is a solution that uses the LEGB scope rule to avoid creating a new string instance on every recursive call
def sum_str(mystring):
def recursive_me(pos):
cur_char = int(mystring[pos])
if pos:
return cur_char + recursive_me(pos-1)
else:
return cur_char
return recursive_me(len(mystring)-1)
s = '4567'
print('summing', s)
print(sum_str(s))
However, indexing can be avoided as well by iterating on the string
def sum_str(mystring):
def recursive_me(itx):
try:
cur_char = int(next(itx))
return cur_char + recursive_me(itx)
except StopIteration:
return 0
return recursive_me(iter(mystring))
Obviously, both solutions produce
summing 4567
22
I am always annoyed by this fact:
$ cat foo.py
def foo(flag):
if flag:
return (1,2)
else:
return None
first, second = foo(True)
first, second = foo(False)
$ python foo.py
Traceback (most recent call last):
File "foo.py", line 8, in <module>
first, second = foo(False)
TypeError: 'NoneType' object is not iterable
The fact is that in order to correctly unpack without troubles I have either to catch the TypeError or to have something like
values = foo(False)
if values is not None:
first, second = values
Which is kind of annoying. Is there a trick to improve this situation (e.g. to so set both first and second to None without having foo returning (None, None)) or a suggestion about the best design strategy for cases like the one I present ? *variables maybe ?
Well, you could do...
first,second = foo(True) or (None,None)
first,second = foo(False) or (None,None)
but as far as I know there's no simpler way to expand None to fill in the entirety of a tuple.
I don't see what is wrong with returning (None,None). It is much cleaner than the solutions suggested here which involve far more changes in your code.
It also doesn't make sense that you want None to automagically be split into 2 variables.
I think there is a problem of abstraction.
A function should maintain some level of abstraction, that helps in reducing complexity of the code.
In this case, either the function is not maintaining the right abstraction, either the caller is not respecting it.
The function could have been something like get_point2d(); in this case, the level of the abstraction is on the tuple, and therefore returning None would be a good way to signal some particular case (e.g. non-existing entity). The error in this case would be to expect two items, while actually the only thing you know is that the function returns one object (with information related to a 2d point).
But it could also have been something like get_two_values_from_db(); in this case the abstraction would be broken by returning None, because the function (as the name suggest) should return two values and not one!
Either way, the main goal of using a function - reducing complexity - is, at least partially, lost.
Note that this issue would not appear clearly with the original name; that's also why it is always important to give good names to function and methods.
I don't think there's a trick. You can simplify your calling code to:
values = foo(False)
if values:
first, second = values
or even:
values = foo(False)
first, second = values or (first_default, second_default)
where first_default and second_default are values you'd give to first and second as defaults.
How about this:
$ cat foo.py
def foo(flag):
if flag:
return (1,2)
else:
return (None,)*2
first, second = foo(True)
first, second = foo(False)
Edit: Just to be clear, the only change is to replace return None with return (None,)*2. I am extremely surprised that no one else has thought of this. (Or if they have, I would like to know why they didn't use it.)
You should be careful with the x or y style of solution. They work, but they're a bit broader than your original specification. Essentially, what if foo(True) returns an empty tuple ()? As long as you know that it's OK to treat that as (None, None), you're good with the solutions provided.
If this were a common scenario, I'd probably write a utility function like:
# needs a better name! :)
def to_tup(t):
return t if t is not None else (None, None)
first, second = to_tup(foo(True))
first, second = to_tup(foo(False))
def foo(flag):
return ((1,2) if flag else (None, None))
OK, I would just return (None, None), but as long as we are in whacko-land (heh), here is a way using a subclass of tuple. In the else case, you don't return None, but instead return an empty container, which seems to be in the spirit of things. The container's "iterator" unpacks None values when empty. Demonstrates the iterator protocol anyway...
Tested using v2.5.2:
class Tuple(tuple):
def __iter__(self):
if self:
# If Tuple has contents, return normal tuple iterator...
return super(Tuple, self).__iter__()
else:
# Else return a bogus iterator that returns None twice...
class Nonerizer(object):
def __init__(self):
self.x=0
def __iter__(self):
return self
def next(self):
if self.x < 2:
self.x += 1
return None
else:
raise StopIteration
return Nonerizer()
def foo(flag):
if flag:
return Tuple((1,2))
else:
return Tuple() # It's not None, but it's an empty container.
first, second = foo(True)
print first, second
first, second = foo(False)
print first, second
Output is the desired:
1 2
None None
Over 10 years later, if you want to use default values I don't think there is a better way than the one already provided:
first, second = foo(False) or (first_default, second_default)
However, if you want to skip the case when None is returned, starting from Python 3.8 you can use the walrus operator (ie. assignment expressions) - also note the simplified foo:
def foo(flag):
return (1, 2) if flag else None
if values := Foo(False):
(first, second) = values
You could use an else branch to assign default values that's worse than the previous or option.
Sadly, the walrus operator does not support unparenthesized tuples so it is just a one line gain compared to:
values = foo(False)
if values:
first, second = values
One mechanism you can use to avoid the problem entirely when you have control of the method foo is to change the prototype to allow giving a default. This works if you are wrapping state but can't guarantee that a particular tuple value exists.
# self.r is for example, a redis cache
# This method is like foo -
# it has trouble when you unpack a json serialized tuple
def __getitem__(self, key):
val = self.r.get(key)
if val is None:
return None
return json.loads(val)
# But this method allows the caller to
# specify their own default value whether it be
# (None, None) or an entire object
def get(self, key, default):
val = self.r.get(key)
if val is None:
return default
return json.loads(val)
I found a solution for this problem:
Return None or return an object.
However you don't want to have to write a class just to return an object. For this, you can use a named tuple
Like this:
from collections import namedtuple
def foo(flag):
if flag:
return None
else:
MyResult = namedtuple('MyResult',['a','b','c']
return MyResult._make([1,2,3])
And then:
result = foo(True) # result = True
result = foo(False) # result = MyResult(a=1, b=2, c=3)
And you have access to the results like this:
print result.a # 1
print result.b # 2
print result.c # 3