'StrictInt' Python object class that prohibits casting numbers with non-integer components to `int`
Converting everything via a float means that you get the wrong result whenever the input cannot be represented exactly as a double-precision floating-point number. For example, this is surely not acceptable:

&gt;&gt;&gt; StrictInt(10**23)
99999999999999991611392

There's an OverflowError when the input is too large to be represented as a float:

&gt;&gt;&gt; StrictInt(10**400)
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
  File "cr195375.py", line 13, in __new__
    f = float(value)
OverflowError: int too large to convert to float

The strategy of enumerating the allowable types (int, StrictInt, float, str) means that many plausible use cases are disallowed, for example with fractions.Fraction:

&gt;&gt;&gt; from fractions import Fraction
&gt;&gt;&gt; StrictInt(Fraction(10, 1))
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
  File "cr195375.py", line 10, in __new__
    raise TypeError("Cannot convert type '{type}' to strict integer".format(type=t))
TypeError: Cannot convert type 'fractions.Fraction' to strict integer


and similarly with decimal.Decimal.
The Pythonic approach is to use duck typing — that is, instead of testing whether input belongs to a fixed collection of types, you call the appropriate methods on the input. Here we want to determine whether the input has a non-zero fractional part, and otherwise to use the integer part. Hence we need Python's built-in divmod function:

quotient, remainder = divmod(value, 1)
if remainder:
    raise ValueError("could not convert value due to non-zero "
                     f"fractional part: {val!r}")

There is no point taking *args and **kwargs if you are not going to use them.


Putting this together:

class StrictInt(int):
    "Subclass of int that refuses to coerce non-integer values."
    def __new__(cls, value):
        if isinstance(value, str):
            for converter in (int, float, complex):
                try:
                    value = converter(value)
                    break
                except ValueError:
                    pass
            else:
                raise ValueError(f"invalid literal for {cls.__name__}(): "
                                 f"{value!r}")
        if value.imag:
            raise ValueError("could not convert value due to non-zero "
                             f"imaginary part: {value!r}")
        quotient, remainder = divmod(value.real, 1)
        if remainder:
            raise ValueError("could not convert value due to non-zero "
                             f"fractional part: {value!r}")
        return super(StrictInt, cls).__new__(cls, int(quotient))


This handles a wider ranges of input types:

&gt;&gt;&gt; StrictInt(Fraction(10, 2))
5
&gt;&gt;&gt; StrictInt(Decimal('11.00000'))
11
&gt;&gt;&gt; StrictInt(5+0j)
5


and it copes with integers that are too large to be represented as floats:

&gt;&gt;&gt; StrictInt(10**400) == 10**400
True