Python: AttributeError: 'list' object has no attribute 'startswith'

Hello. I am facing an issue with my python application. Apparently the variable I am using is not a list object, yet for some reason I get this error when the following code is executed:

def buildShow():

    directory = "Used Words"

    if(os.path.exists(directory)):
       for files in os.walk(directory):
           for name in files:
               if name.startswith("2020"):
                   file=open(os.path.join(directory,file),"r+")
                   txt=file.read()
                   file.close()
                   lines=txt.split('\n')
                   words=[]
                   for line in lines:
                       words.append(line.split('\n'))
                   if e1.get() in words:
                       tk.messagebox.showinfo("Warning", "World Already Used")

if name.startswith(“2020”):

AttributeError: ‘list’ object has no attribute ‘startswith’

This is meant to look through all the .txt files in the “Used Words” directory, and scan each of them for a specific word (e1.get()). 

Would really appreciate some help. Thanks.

I think the problem is a misunderstanding about the items returned by os.walk(). They’re not list of filenames, they’re tuples in which one element is a list of filenames (see documentation). If that’s what you want, you need to pick the right element instead of iterating over the tuple.

It seems to me that you only want to check files directly in the given directory, not recursively look through a directory tree. In that case it’d be much simpler to use os.listdir() instead, which should just give you a list of file (or subdirectory) names.

However, I’d recommend looking at the pathlib module (also part of the standard library) instead. The Path.iterdir() function looks like what you need, and you don’t need to fiddle with joining directory and file name that way.

1 Like

Thank you for your reply. I tried to use pathlin in the following way:

if(os.path.exists(directory)):
        for file in directory.iterdir():
            if file.endswith(".txt"):
                file=open(os.path.join(directory,file),"r+")
                txt=file.read()
                file.close()
                lines=txt.split('\n')
                words=[]
                for line in lines:
                    words.append(line.split('\n'))
                if e1.get() in words:
                    tk.messagebox.showinfo("Warning", "World Already Used")

However I got the error that "WindowsPath object has no endswith attribute. Can you tell me where did I go wrong here?

It’s exactly what the error message says: The Path object doesn’t have the “endswith” attibute. Another way to put it: The problem is that you’re trying to use the Path object as a str. The “name” attribute of a path will give you the filename as a str.

However, I see a few more problems in that code:

  • In the open() call you try to run os.path.join() on two Path objects (directory and file). This won’t work and is also pointless: The Path object contains the directory information already, just use open(file, ‘r’).

  • The open() call overwrites the “file” variable with a completely different object. This works but is considered bad practice, because it is confusing to people reading the code. Use a different variable name.

  • You can directly iterate over the file handle returned by open() instead of reading the file into a buffer and then splitting it into lines, like so:

    with open(file, ‘r’) as fh:
    for line in fh:
    # do whatever you need for each line in the file

The “with” structure also ensures fh will always be closed, even in case of an exception.

  • Check if you really need to open the file in update mode (“r+”). Your code only reads the file, so “r” should be enough. Check the open() documentation for details on the available modes.
  • Finally: Using os.path.exists(directory) is unnecessarily complicated when “directory” is already a Path object, you can just use directory.is_dir().

I highly recommend looking at the Python API documentation to see how you can use different objects, I hope the links above help you get started on that. :wink:

According to the doc you get a list of Path objects, not strings. You want to use PurePath.suffix to get the file extension (i.e. “file.suffix = ‘.txt’”)

And then PurePath.name (i.e. “file.name”) for your os.path.join call.

I think Pathlib is just making this far too complex.

Better would be to completely drop the os.path.join() call, and just pass the Path object to open(), because it already contains the directory information. Equally, if “directory” is already a Path object, you can simplify the check if it exists (and is a directory) like so:

if(directory.is_dir()):
    for file in directory.iterdir():
        if file.suffix == ".txt":
            fh = open(file, "r")
# ...

Pathlib may take a little time to learn, but it simplifies path handling a lot. When using strings and os.path there’s a lot of special cases and OS-specific stuff to consider, while pathlib takes care of most of that.

However, I have two other suggestions here:

  • Don’t overwrite the “file” variable with the return value of open(), use a new variable instead (as in the example above). Changing the meaning of a variable is considered bad practice because it can be confusing to people reading your code.

  • Instead of reading the whole file into a buffer, you can iterate over the file handle returned by open():

    if(directory.is_dir()):
    for file in directory.iterdir():
    if file.suffix == “.txt”:
    with open(file, “r”) as fh:
    for line in fh:

    do whatever you need to do for each line

The “with” structure guarantees that the file handle is closed properly, even in case of an exception.

Edit: Sorry this post is mostly a duplicate of my post above. For some reason it didn’t show in the thread when I saw the post this is in response to, so I thought it got lost.

Thank you for the detailed response. The code is working. I am left with 1 last hurdle however.

if(directory.is_dir()):
        for file in directory.iterdir():
            if file.suffix == ".txt":
                with open(file, "r", encoding = "utf-8-sig") as fh:
                    for line in fh:
                        line.rstrip().split()
                        UsedWords.append(line)
                        if(e1.get() in UsedWords):
                            tk.messagebox.showinfo("Warning", "World Already Used")

I am attempting to retrieve the text entered into the text box, and compare it to text I appended to a list from the file I read. However, when this function is executed, nothing is happening when the if statement is reached.

This is the list that was made from the file. Even if I input a word that is the same as one of those, I do not get the error. So if I input door for example, nothing happens.

[‘Door\n’]
[‘Door\n’, ‘House\n’]
[‘Door\n’, ‘House\n’, ‘Game\n’]
[‘Door\n’, ‘House\n’, ‘Game\n’, ‘Toy’]

I assume the reason is that your input doesn’t exactly match one of the words in the list. There are two possible reasons, and both might apply:

  1. The ‘\n’ at the end of the words as saved in the list.
  2. Different case (e.g. “Door” vs. “door”).

Your code looks like you’re trying to use line.rstrip() to remove the line break. That is correct in principle, but you then add “line” to the list. str.rstrip() does not change the string, it returns a new string with the modification, so as long as you have only one word per line in your files you could use:

UsedWords.append(line.rstrip())

If your files might contain multiple words per file you’ll still need to use str.split(). That also takes care of removing the line break (so you don’t need rstrip in that case), but the result is a list. Using list.append() in that case would add the list object to your list of words, which of course wouldn’t match any string input. In that case you’d need to use extend() instead, like so:

UsedWords.extend(line.split())

The easiest way to avoid the case problem is to turn all words (including the input) into lower case (or upper, if you prefer). Note that if you use the line.split() approach you should do case conversion before splitting, so you don’t have to modify each word in a line separately.

1 Like

Awesome. Everything is working as intended now. Thanks a lot for your time, patience and help!