1

I got stuck by some weird behavior in Python 3.5.2. I have a class Foo and want to execute a piece of code only once for all instances. This code is placed directly below the class statement.

import os

class Foo:

    path = "annotations/"
    files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]

    def __init__(self, x):
        # do something
        1+1

When runing this code, the following error message appears if the annotations directory is not empty (an empty file suffices)

Traceback (most recent call last):
    File "foo.py", line 3, in <module>
    class Foo:   File "foo.py", line 6, in Foo
    files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]   File "foo.py", line 6, in <listcomp>
    files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
NameError: name 'path' is not defined

However, no error occurs if the annotations/ is empty. Why? This strange behavior only occurs when using the single-line for loop.

I use Python 3.5.2. When running the above code with Python 2.7.12, the error does not appear.

null
  • 1,369
  • 2
  • 18
  • 38
  • Cannot reproduce on MacOs Python 3.5.2. Anyway I would suggest to calculate value for `files` in a function, not inside `class` definition -- what you do is ugly. Try to update/reinstall Python. – warvariuc Nov 14 '16 at 11:45
  • I use Python 3.5.2. on a Ubuntu 16.04 system. This seems a normal way for me to add static code to a class. Why should this be ugly? If nobody presents a satisfying solution I'll probably try to reinstall Python. – null Nov 14 '16 at 11:54
  • 1
    You get a `NameError` because in the *nested scope of a list comprehension*, the name `path` isn't visible. But only the use in the `if` statement, not the outermost `for` iterable source (because that is calculated outside the list comprehension), and the `if` statement is only used if there are actually elements in the iterator to test. See the duplicate. – Martijn Pieters Nov 14 '16 at 15:05
  • @MartijnPieters how then it works for me? – warvariuc Nov 14 '16 at 17:25
  • @warvariuc: are you using Python 2? See the duplicate, list comprehensions were given a separate scope in Python 3. In Python 2 it'll work. – Martijn Pieters Nov 14 '16 at 17:30
  • @warvariuc: it'll also work if the directory you use `os.listdir()` on is *empty*. – Martijn Pieters Nov 14 '16 at 17:31
  • @MartijnPieters you are correct. I thought I created a file in `annotations` directory, but didn't check this – warvariuc Nov 14 '16 at 19:47
  • Great. That's it! Thank you very much. I have never thought about a different scoping in Python 3. Now everything is clear. – null Nov 15 '16 at 10:10

2 Answers2

0

If I understood, first you do not have the "annotations" path, then you make the "annotations" path and works. If so, it make sense, because the script try read the "annotations" path but the path does not exist!

You can do something like this:

if os.path.exists('annotations/'):
    files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]

Or force make the path:

if not os.path.exists('annotations/'):
    os.mkdir('annotations/')
files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
Hugo Salvador
  • 1,094
  • 1
  • 11
  • 11
0

If you have path defined, no way you can get NameError - one you have mentioned. Running it may give you:

OSError: [Errno 2] No such file or directory: 'annotations/'

In case there isn't any directory named annotations


To handle the scenario - you must create directory if it is not there (as Hugo has mentioned):

import os

class Foo:

    path = "annotations/"
    if not os.path.exists('annotations/'):
        os.mkdir('annotations/')
    files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]

    def __init__(self, x):
        # do something
        1+1


if __name__ == "__main__":
    obj = Foo(1)
    print 'hello'

The output - worked absolutely fine i.e. also created the annotations directory itself:

[ahmed@localhost so_tmp]$ ls 
[ahmed@localhost so_tmp]$ python ../path_issue.py 
hello
[ahmed@localhost so_tmp]$ ls
annotations
[ahmed@localhost so_tmp]$ 
Nabeel Ahmed
  • 18,328
  • 4
  • 58
  • 63
  • I do get a NameError. I have no clue why this happends. This only happens if the target directory is not empty (see edit above). – null Nov 12 '16 at 17:38