Increment class instance counter in Python

Question:

I’m not well versed in Python as I have a Java background.
I have this simple class:

## Class declaration
class SemVersion:
    major: int
    minor: int
    patch: int

    def __init__(self, major, minor, patch):
        self.major = major
        self.minor = minor
        self.patch = patch

    def incrementMinor(self):
        print(self)
        self.minor = self.minor + 1
        return self

    def toString(self):
        return self.major + "." + self.minor + "." + self.patch

    def toSnapshotString(self):
        return self.toString() + "-SNAPSHOT"

The following statement throws an error:

newDevelopVersion = releaseSemVersion.incrementMinor()

Exception:

Traceback (most recent call last): File
"C:devenvjavaappsvsb-letters-transfer-apirelease_preprod.py",
line 41, in
newDevelopVersion = releaseSemVersion.incrementMinor() File "C:devenvjavaappsvsb-letters-transfer-apirelease_helpers.py",
line 17, in incrementMinor
self.minor = self.minor + 1 TypeError: can only concatenate str (not "int") to str

I’d like to understand how Python can manage not to recognize it as an integer as I declared it as such and just increment the way I asked it.
I’m sure there is a reasonable explanation for it.
I don’t see how I asked anywhere to add a string to an int or anything of that nature.

Many thanks.

Asked By: html_programmer

||

Answers:

Python doesn’t enforce types from annotations. The annotations are primarily for documentation purposes.

You are likely passing in an str to the constructor when you initialize your object which would have made the interpreter think releaseSemVersion.minor is a string instead of an int.


You can avoid this by adding a check in __init__() as follows

def __init__(self, major, minor, patch):
    if not isinstance(major, int):
        raise TypeError('major must by of type int')
    
    if not isinstance(minor, int):
        raise TypeError('minor must by of type int')
    
    if not isinstance(patch, int):
        raise TypeError('patch must by of type int')

    self.major = major
    self.minor = minor
    self.patch = patch
Answered By: imbuedHope

First of all, some considerations when using Python:

  • Python do not enforce type, type hints just helps you and others to understand what is expected on some method or function

  • Variables and method name must be in snake_case format, following the best practices described on pep8

Now, refactoring your example, you should be able to perform your desired operation with:

class SemVersion:
    major: int
    minor: int
    patch: int

    def __init__(self, major, minor, patch):
        self.major = major
        self.minor = minor
        self.patch = patch

    def increment_minor(self):
        self.minor = self.minor + 1
        return self

    def to_string(self):
        return self.major + "." + self.minor + "." + self.patch

    def to_snapshot_string(self):
        return self.toString() + "-SNAPSHOT"


# Your class instance
develop_vision = SemVersion(
    major=1, 
    minor=2, 
    patch=3
)

print(f"Initial value of minor: {develop_vision.minor}")

develop_vision.increment_minor()
develop_vision.increment_minor()

print(f"New value of minor: {develop_vision.minor}")

Now, if you want to check your constructor arguments type, pydantic can help you!

# Make sure that your virtual environment is active

$ pip3 install pydantic

Adapt your class to use pydantic validations

from pydantic import BaseModel, StrictInt


class SemVersion(BaseModel):
    major: StrictInt
    minor: StrictInt
    patch: StrictInt

    # You don't need build a constructure anymore, pydantic do it for you

    def increment_minor(self):
        self.minor = self.minor + 1
        return self

    def to_string(self):
        return self.major + "." + self.minor + "." + self.patch

    def to_snapshot_string(self):
        return self.toString() + "-SNAPSHOT"


# Your class instance
develop_vision = SemVersion(
    major=1, 
    minor=2, 
    patch='10'
)

print(develop_vision)

If you try to run this code, using a string like ’10’, you will get an exception from pydantic, like this:

Traceback (most recent call last):
  File "/home/jns/Desktop/stack.py", line 23, in <module>
    develop_vision = SemVersion(
  File "pydantic/main.py", line 341, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for SemVersion
patch
  value is not a valid integer (type=type_error.integer)
Answered By: Jonas Ferreira
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.