4

I have a strange question when using python loop. It may be easy but strange to me. Say if I have a list of strings:

seqs=['AA', 'AT']

Then I want to print out elements in the list. One way (method 1) is to use for loop:

for seq in seqs:
    print seq

Which works fine. Meanwhile, I define a 'print' function (method 2) to print:

def print0(s):
    print s

[print0(s) for s in seqs]

If I use the 'print0' function to print out values, this is the output:

AA
AT
[None, None]

I want to know why here comes two 'None' values, since these two values doesn't come when I use the method 1? I want to do the for loop by using paralleling technic, but with the 'None' value, delayed function from joblib package can't work. Thanks. Update: if I want do it parallel:

Parallel(n_jobs=2)(delayed(print0)(seq) for seq in seqs)

It will give an error message:

TypeError: expected string or Unicode object, NoneType found
Tian
  • 397
  • 2
  • 9
  • 16
  • 4
    ... Because you don't return anything... – Ignacio Vazquez-Abrams Dec 09 '16 at 03:45
  • 1
    both snippets are semantically different: one is just a for-loop with print statement while the other, you are creating a list with list-comprehension using a function that returns nothing (none) – Jeanderson Candido Dec 09 '16 at 03:48
  • In addition to what has been proposed in the answers, if you do not want to see the `None`s, discard the value of the expression: `_=[print0(s) for s in seqs]`. – DYZ Dec 09 '16 at 03:50

5 Answers5

7

Since you are using the interactive interpreter, which by default prints the repr() for any object returned to the top level, you see a list of None objects, which is what got returned from the calls of your print0 function. This is why it's not good practice to create a list just for its side effects, in addition to the fact that all of those objects are stored in memory (although there's only one None object, and that list will be garbage-collected as soon as you return something else to the top level - until then, it's stored in the special variable _).

You'll recognize how the interpreter displays the repr() of any object returned to the top level:

>>> 'hello'
'hello'

And it makes sense that the following literal list is displayed:

>>> [print()]

[None]

And the same for a comprehension:

>>> [print(num) for num in range(3)]
0
1
2
[None, None, None]

But it's better to use an ordinary loop. One-liners are fun, but not always ideal.

>>> for num in range(3):
...     print(num)
...
0
1
2

Note that you can get odd results if a function prints one thing and returns another:

>>> def f():
...     print(1)
...     return 2
...
>>> [f() for num in range(3)]
1
1
1
[2, 2, 2]
>>> for num in range(3):
...     f()
...
1
2
1
2
1
2

This is a demonstration of "side-effects." Try to avoid code that makes changes in two different places at once (in this case, the interactive interpreter's displayed results and the local actions of a function).

TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97
  • 1
    None is a singleton, as you said, so I don't understand your point about the list / memory footprint thing. – Charles D Pantoga Dec 09 '16 at 03:48
  • 1
    This is a good answer because of how in-depth it is, but it fails to clear up any confusion the poster has. I don't think they're interested in garbage collection or good list practices. – byxor Dec 09 '16 at 03:51
  • 1
    @CharlesAddis - If they use a function that returns some non-singleton data they're not interested in, that'll use O(n) memory. It's just another reason to write a proper loop instead of a comprehension when you don't care about the result. – TigerhawkT3 Dec 09 '16 at 03:55
  • @BrandonIbbotson - I've added some more explanation. – TigerhawkT3 Dec 09 '16 at 04:04
  • removed the downvote, as the answer has been edited and is a good answer... but even if the return a list of strings or integers the memory footprint is NOT O(n) -- please check the output of `for _ in comprehension: print id(_)` -- you'll see that strings are interned and python only stores one instance of each unique integer @TigerhawkT3 – Charles D Pantoga Dec 09 '16 at 04:53
3

The syntax [print0(s) for s in seqs] is a List Comprehension.

It will call print0(s) for every element in seq and put the result into a list. Because print0 returns nothing, you get a list of 2 Nones.

byxor
  • 5,930
  • 4
  • 27
  • 44
1

I see such questions so often, there should be some ultimate answer to them all, which automatically triggers whenever print and None are both present in a single question...

While the answer to the question itself is trivial, what, I think, you need to really understand is a difference between a side effect and a return value. For example:

a = 10
def three(x):
    global a
    a += x #side effect
    print x #side effect
    return 3 #returning

The value our three() function returns is 3. It also has two side effects: modifying a global variable a and printing. Those are called side effects because they modify something outside of the function: the a variable and the screen state, respectively.

In your example:

def print0(s):
    print s

there's no explicit return value, only a side effect (printing). In other words, it prints something on the screen and then returns nothing. That nothing is called None in Python. If you call it like this:

a = print0(3)

it prints 3 into the console. But what is the value of a now?

>>> print a
None

Now to the list comprehension. It is a concept borrowed from functional programming (Lisp, etc.) where it's called map. There's still map function in Python, so the following two lines are equivalent:

[print0(s) for s in seqs]
map(print0, seqs)

What they both do is taking the elements of the input list (seqs), one by one, applying the function (print0) to each of them and putting the results (return values), one by one, into the output list, which they return. Each time they call your print0 function, it prints its argument s on the screen (a side effect) and then returns nothing (None), which is put into the output list by list comprehension or map. If you do it in the Python interactive console, that result appears in the output ([None, None]), if not - it is still produced by the interpreter and immediately discarded, unless you pass it as an argument to another statement. Which leads us to your final line of code and the TypeError message. You pass your function to another function, which expects a string, it doesn't care about the side effects your function may produce. The context is not completely clear to me, but you probably should define your function like this:

def print0(s):
    return str(s)

Now, instead of printing s on the screen, it converts it to string and then returns it. Note that if you call them inside the interactive interpreter just like print0(s), it appears they produce the same effect, which may be confusing. However, if you do a = print0(s) you will see that a is different. In some languages the last computed value automatically becomes the return value, but with Python that isn't the case for regular functions:

def times_three(x):
    x*3

returns None. However, there are also lambda-functions, for which that is the case:

times_three = lambda x: x*3
times_three(5) #returns 15
Headcrab
  • 6,838
  • 8
  • 40
  • 45
0

None is the return value of print0(s). When using print the result will just be displayed in the stdout but not returned as result of the function. So the comprehension list evalute the function as None. Your funtion should instead be:

def print0(s):
    return s
Dhia
  • 10,119
  • 11
  • 58
  • 69
gzc
  • 8,180
  • 8
  • 42
  • 62
0

Another way, if you want do it interactively in a one-liner is to use .join

text = ['28', '43', '6f', '72', '65', '20', '64', '6f', '6d', '70', '65', '64', '29', '0a']

''.join(chr(int(x, 16)) for x in text)
naisanza
  • 124
  • 1
  • 2
  • 9