Search code examples
pythonpycharmpython-typing

Pycharm Type-Hinting of Class Fields / Instance Variables


Whenever we need a new object in Java, we declare the type and the name choose to give it a initial value or not. In Python we can't do this, because we don't declare types.

How can I get around this because if the types are not declared; I am not getting any code completion hints. Like the fields of a particular object or any methods we can call on the object...

 class Album:
    def __init__(self, name, Photo, next):
        self.name = name
        self.Photo = None
        self.next = next

    def __str__(self):
        return "Album name is: " + self.name


class Photo:
    def __init__(self, name, caption, Tag, next):
        self.name = name
        self.caption = caption
        self.Tag = Tag
        self.next = next

    def __str__(self):
        return "Photo name is: " + self.name + " with caption: " + self.caption


class Tag:
    def __init__(self, type, info, next):
        self.name = type
        self.info = info
        self.next = next

    def __str__(self):
        return "Photo name is: " + self.name


def addPhoto(toEdit, photoName, caption):

   if isinstance(toEdit, Album):    
        if toEdit.Photo is None:
            toEdit.Photo = Photo(photoName, caption, None, None)    
        else:

            tempPhoto = toEdit.Photo
            prev = None
            isFound = False 
            while tempPhoto != None:

                if tempPhoto.name.lower() == photoName.lower():
                    isFound = True
                    break

                prev = tempPhoto
                tempPhoto = tempPhoto.next

            if isFound == False:
                prev.next = Photo(photoName, caption, None, None)

            else:
                print("Photo " + photoName + " already exists in " + toEdit.name)


def deletePhoto(toEdit, photoName): 
    if isinstance(toEdit, Album):
        if photoName in toEdit.Photo.name:
            if toEdit.Photo.next is not None:
                toEdit.Photo = toEdit.Photo.next
                return True

            else:
                toEdit.Photo = None
                return True
        else:
            Photo = toEdit.Photo.next
            Photo_prev = None

            while Photo is not None:
                if Photo.name in photoName:
                    prev.next = Photo.next

                prev = Photo
                Photo = Photo.next

        print("Removed photo: " + photoName + " from " + toEdit.name)

pPtr = album1.Photo  
while (pPtr != None):
    print(pPtr)
    pPtr = pPtr.next

So whenever I try to do pPtr = album1.Photo and then try to access any fields of that pPtr object I get no suggestions in PyCharm. I need to know whether I am doing this wrong or if PyCharm is at fault.

The implementation is one giant linkedlists. Albums nodes contain Photo nodes which contain Tag nodes


Solution

  • First off, as one of the commenters on your question pointed out, using upper_case variable names confuses them with Classes/Types. If you look at your question, you'll see it even confused the Stackoverflow code-formatter.

    Additionally, in your Album constructor, you are masking the global variable "Photo" with a local parameter. That might confuse PyCharm, especially when you do the following. So for my answer + test below, I renamed your parameter to a lower-case photo so that it doesn't interfere.

    **Edit: ** I did find a better way. See "The Right Way".

    First Attempt

    class Album:
        def __init__(self, name, photo, next):
            self.name = name
            self.photo = self.get_photo(photo)
            self.next = next
    
        def get_photo(self, photo):
            """
            @rtype: Photo
            """
            return photo
    
        def __str__(self):
            return "Album name is: " + self.name
    

    The way it works is it uses the PyCharm type-inference. See here for details on how it works.

    And below a screenshot of it working:

    Image of answer working

    Note: I do not recommmend doing this, as it is a hack. I came across your question as I'm attempting to find out if there is a better way of doing this in PyCharm.

    The Right Way

    The right way of doing this is to give PyCharm the type of the variables in the constructor. Essentially, moving the type-binding from the method as above, and into the constructor as part of the docstring entry "type".

    class Album:
        def __init__(self, name, photo, next):
            """
            @type name: str
            @type photo: Photo
            @type next: str
            """
            self.name = name
            self.photo = photo
            self.next = next
    
        def __str__(self):
            return "Album name is: " + self.name
    
    
    class Photo:
        def __init__(self, name, caption, Tag, next):
            self.name = name
            self.caption = caption
            self.Tag = Tag
            self.next = next
    
    photo1 = Photo("name.txt", "caption", "tag", "next")
    
    album1 = Album("TestAlbum", photo1, "next")
    
    album1.photo.#code completion working here