Search code examples
pythonlistsortingobjectattributes

Object has no attribute error when I name attribute with double underscores


I am trying to group all Patient objects by their surnames.

My code:

class Patient:
    def __init__(self, first_name, surname, age, mobile, postcode):
        self.__first_name = first_name
        self.__surname = surname
        self.__doctor = "None"
        self.__age = age
        self.__mobile = mobile
        self.__postcode = postcode
        self.__symptoms = ""
        
    def full_name(self) :
        return "{} {}".format(self.__first_name, self.__surname)
        
    def __str__(self):
        return f'{self.full_name():^20}|{self.__doctor:^6}|{self.__age:^4}|{self.__mobile:^15}|{self.__postcode:^10}'
    
patients = [Patient('Sara','Cath', 20, '07012345678','B1 234'),
            Patient('Mike','Abe', 37,'07555551234','L2 2AB'), 
            Patient('Daivd','Barr', 15, '07123456789','C1 ABC'), 
            Patient('Gerald','Abe', 21, '07012345678','L2 2AB'), 
            Patient('Sara','Abe', 19, '07012345678','L2 2AB'), 
            Patient('Jane','Barr', 35, '07123456789','C1 ABC')]

patients.sort(key=lambda y: y.__surname)
print(patients[0])
print(patients[1])
print(patients[2])
print(patients[3])
print(patients[4])
print(patients[5])
print()
newPat = sorted(patients, key=lambda y: y.__surname)
print(newPat[0])
print(newPat[1])
print(newPat[2])
print(newPat[3])
print(newPat[4])
print(newPat[5])

My code has the surname attribute self.__surname = surname in the __init__ function, the full_name function and in both sorting functions, but when I try to run it gives me the AttributeError: 'Patient' object has no attribute '__surname'

Error:

File "c:\users\mundane\downloads\pythonfiles\files\untitled0.py", line 25, in <lambda>
    patients.sort(key=lambda y: y.__surname)

AttributeError: 'Patient' object has no attribute '__surname'

If I replace each of the four .__surname with literally anything else - like .xyzsurname - it works and correctly prints:

      Mike Abe      | None | 37 |  07555551234  |  L2 2AB  
     Gerald Abe     | None | 21 |  07012345678  |  L2 2AB  
      Sara Abe      | None | 19 |  07012345678  |  L2 2AB  
     Daivd Barr     | None | 15 |  07123456789  |  C1 ABC  
     Jane Barr      | None | 35 |  07123456789  |  C1 ABC  
     Sara Cath      | None | 20 |  07012345678  |  B1 234  

      Mike Abe      | None | 37 |  07555551234  |  L2 2AB  
     Gerald Abe     | None | 21 |  07012345678  |  L2 2AB  
      Sara Abe      | None | 19 |  07012345678  |  L2 2AB  
     Daivd Barr     | None | 15 |  07123456789  |  C1 ABC  
     Jane Barr      | None | 35 |  07123456789  |  C1 ABC  
     Sara Cath      | None | 20 |  07012345678  |  B1 234 

Is there anyway for me to keep the double underscore for the attribute by using another way to order a list of objects?


Solution

  • You can use the built in dir() method to find out what attributes your Patient objects do have. Running that yields:

    ['_Patient__age',
     '_Patient__doctor',
     '_Patient__first_name',
     '_Patient__mobile',
     '_Patient__postcode',
     '_Patient__surname',
     '_Patient__symptoms',
     '__class__',
     '__delattr__',
     '__dict__',
     '__dir__',
     '__doc__',
     '__eq__',
     '__format__',
     '__ge__',
     '__getattribute__',
     '__gt__',
     '__hash__',
     '__init__',
     '__init_subclass__',
     '__le__',
     '__lt__',
     '__module__',
     '__ne__',
     '__new__',
     '__reduce__',
     '__reduce_ex__',
     '__repr__',
     '__setattr__',
     '__sizeof__',
     '__str__',
     '__subclasshook__',
     '__weakref__',
     'full_name']
    

    You can see that because you are using double underscores (meaning that the attributes are not intended to be interacted with outside the class) Python is hiding them as _Patient__surname. Therefore it will work if you change your sort to patients.sort(key=lambda y: y._Patient__surname)