[Tutor] TypeError: 'str' object is not callable

Mats Wichmann mats at wichmann.us
Sat May 15 18:16:16 EDT 2021


On 5/15/21 4:05 PM, Alan Gauld via Tutor wrote:
> On 15/05/2021 15:13, me wrote:
>> Hello,
>>
>> I'm on my very first steps with python and I have a problem where I don't see,
>> what's wrong.
>>
>> This is the code:
>> -----<snip>-----------
>> #!/usr/bin/env python3
>> import glob
>>
>> class foo:
> 
> This is a *terrible* name for a class. Use a noun that describes
> what it represents! In this case it is a wrapper around a
> string which represents a path.
> 
> Not very useful, but at least calling it Path would be
> more descriptive.
> 
> 
>>      def __init__(self, path):
>>          self.path = path
>>      def path(self):
>>          return self.path
> 
> The issue here is the order on which the code is executed.
> When you first run the code the class definition is executed which
> defines a method called self.path and another called self.__init__.
> 
> When you create a new instance of the class self.init is executed.
> What it does is create a new binding of self.path to the string
> variable path. The old binding to the method is now lost.
> 
>> def main():
>>      hs = []
>>      drives = glob.glob('/dev/sd?')
>>      for d in drives:
>>          print(d)
>>          hs.append(foo(d))
> 
> Each instance of foo overwrites the method with the string d.
> 
>>      for e in hs:
>>          print(e.path())
>>
>>
> 
> So now, when you call e.path() you are trying to call the string.
> 
> A simple solution to this is to use a different name for
> the attribute path. A common trick is to put an underscore
> in front:
> 
> def __init__(self.path):
>      self._path = path
> 
> def path(self):
>      return self._path
> 
> A more general option is to use an article prefix such as 'the':
> 
> def __init__(self.path):
>      self.thePath = path
> 
> def path(self):
>      return self.thePath

Or, since it's Python, which doesn't _force_ you to use getters and 
setters, just dispense with the method entirely and access the path 
attribute directly. So this should work:

import glob

class foo:
     def __init__(self, path):
         self.path = path


def main():
     hs = []
     drives = glob.glob('/dev/sd?')
     for d in drives:
         print(d)
         hs.append(foo(d))
     for e in hs:
         print(e.path)


if __name__ == '__main__':
     main()


(note for future learnings: there's a standard library module called 
pathlib which is designed to work with paths, might end up being useful 
someday rather than rolling your own - I realize this one is just a 
learning exercise)




More information about the Tutor mailing list