0

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:\devenv\java\apps\vsb-letters-transfer-api\release_preprod.py", line 41, in newDevelopVersion = releaseSemVersion.incrementMinor() File "C:\devenv\java\apps\vsb-letters-transfer-api\release_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.

3
  • 3
    How are you creating instances of SemVersion (e.g. what arguments?). Type hints are only that, they do not enforce typing. Commented Aug 24, 2022 at 22:56
  • if you make the function call with an integer in this case it would treat it as an integer Commented Aug 24, 2022 at 23:00
  • 1
    Where/how are you instantiating your releaseSemVersion instance? Commented Aug 24, 2022 at 23:03

2 Answers 2

2

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
Sign up to request clarification or add additional context in comments.

2 Comments

Stupid question... but aren't these languages supposed to recognise types? I will accept it if it's like that. Seems a bit dumb to cast a 1 to a 1. Maybe that's just me.
Not all programming languages enforce variable types. Python follows the philosophy of duck typing. All objects in python have a type, but the interpreter doesn't check types until prompted. There are both pros and cons to the approach and there's a lot of debate around the topic.
1

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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.