Typing the instance of a class in python [duplicate] - python

This question already has answers here:
Can you annotate return type when value is instance of cls?
(4 answers)
Closed 2 years ago.
Is there an inverse function for Type[SomeType] so that Instance[Type[SomeType]] == SomeType?
I'm given a class and I'd like to annotate the return value of calling its constructor
class FixedSizeUInt(int):
size: int = 0
def __new__(cls, value: int):
cls_max: int = cls.max_value()
if not 0 <= value <= cls_max:
raise ValueError(f"{value} is outside range " +
f"[0, {cls_max}]")
new: Callable[[cls, int], Instance[cls]] = super().__new__ ### HERE
return new(cls, value)
#classmethod
def max_value(cls) -> int:
return 2**(cls.size) - 1
Edit:
This class is abstract, it needs to be subclassed for it to make sense, as a size of 0 only allows for 0 as its value.
class NodeID(FixedSizeUInt):
size: int = 40
class NetworkID(FixedSizeUInt):
size: int = 64
Edit 2: For this specific case, using generics will suffice, as explained in https://stackoverflow.com/a/39205612/5538719 . Still, the question of a inverse of Type remains. Maybe the question then is: Will generics cover every case so that an inverse function is never needed?

I believe you want:
new: Callable[[Type[FixedSizeUInt], int], FixedSizeUInt] = ...
Or a little more dynamically:
from typing import TypeVar, Callable
T = TypeVar('T')
...
def __new__(cls: Type[T], value: int):
...
new: Callable[[Type[T], int], T] = ...
Still, the question of a inverse of Type remains. Maybe the question then is: Will generics cover every case so that an inverse function is never needed?
It's not about generics, it's about type hints in general. Take int as an example. int is the class. int() creates an instance of the class. In type hints, int means instance of int. Using a class as a type hint always talks about an instance of that type, not the class itself. Because talking about instances-of is the more typical case, talking about the class itself is less common.
So, you need to use a class in a type hint and a class in a type hint means instance of that class. Logically, there's no need for an Instance[int] type hint, since you cannot have a non-instance type hint to begin with. On the contrary, a special type hint Type[int] is needed for the special case that you want to talk about the class.

Related

Type hints with map like function?

Suppose I've got a map like function:
def generate(data, per_element):
for element in data:
per_element(element)
How can I add type-hints so that if I call generate(some_data, some_function) where some_data: List[SomeClass], I get a warning if SomeClass is missing a field used by some_function?
As an example - with the following code:
def some_function(x):
print(x.value)
some_data: List[int] = [1, 2, 3]
generate(some_data, some_function)
I would like to get a warning that int does not have the attribute value.
Use a type variable to make generate generic in the type of object that data contains and that per_element expects as an argument.
from typing import TypeVar, List, Callable
T = TypeVar('T')
def generate(data: List[T], per_element: Callable[[T], Any]):
for element in data:
per_element(element)
class Foo:
def __init__(self):
self.value = 3
def foo(x: Foo):
print(x.value)
def bar(x: int):
pass
generate([Foo(), Foo()], foo) # OK
# Argument 2 to "generate" has incompatible type "Callable[[Foo], Any]"; expected "Callable[[int], Any]"
generate([1,2,3], foo)
Whatever T is, it has to be the same type for both the list and the function, to ensure that per_element can, in fact, be called on every value in data. The error produced by the second call to generate isn't exactly what you asked for, but it essentially catches the same problem: the list establishes what type T is bound to, and the function doesn't accept the correct type.
If you specifically want to require that T be a type whose instances have a value attribute, it's a bit trickier. It's similar to the use case for Protocol, but that only supports methods (or class attributes in general?), not instance attributes, as far as I know. Perhaps someone else can provide a better answer.
Seems like you're searching for:
def generate(data: List[AClass], per_element):
for element in data:
per_element(element)
So that AClass implements the method you need.
Your class needs the value attribute:
class SomeClass:
value: Any # I used any but use whatever type hint is appropriate
Then using typing.Callable in your function as well as the builtin types. starting with python 3.7 and finally fully implemented in python 3.9 you can use the builtins themselves as well as in python 3.9 you can use parameter specifications
from typing import ParamSpec, TypeVar, Callable
P = ParamSpec("P")
R = TypeVar("R")
def generate(data: list[SomeClass], per_element: Callable[P, R]) -> None:
for element in data:
per_element(element)
Then in some_function using the class type hint and None return variable:
def some_function(x: SomeClass) -> None:
print(x.value)

TypeHinting function that give Types and returns Objects of these Types

I want to make Entity Component System (ECS) in python.
I make Entity class:
from typing import Optional, TypeVar, Type
T = TypeVar('T')
class Entity:
def __init__(self):
self.components = []
def add_component(self, c):
self.components.append(c)
def get_first_component(self, Type: Type[T]) -> Optional[T]:
for c in self.components:
if isinstance(c, Type):
return c
def get_first_components(self, *Types):
res = []
for Type in Types:
res.append(self.get_first_component(Type))
return res
type hinting for get_first_component was easy, but i dont understand how to do type hinting for get_first_components function. This function give list of Types and returns list of object of thees Types.
Example:
e.get_first_components(Position, Health) # returns [Position(2, 2), Health(10, 10)]
I see it like:
A = TypeVar('A')
B = TypeVar('B')
def f(Types: [Type[A], Type[B], ...]) -> [A, B, ...]:
# some code ...
Sorry my english is bad :(
It need for type hinting in systems:
class MoveSystem(System):
def __init__(self) -> None:
pass
def run_for_entity(self, e: Entity):
pos, m2 = e.get_first_components(Pos2, Move2)
if m2.active: # <- no type hinting after typing "m2."
pos.x += m2.dx
pos.y += m2.dy
m2.active = False
Python's type hinting system doesn't have the ability to describe your function in the way you want. You need to be able to describe a sequence of different types of arbitrary length, and then in parallel, describe objects of those types. Unfortunately, that's not currently possible.
About the best you can do is:
def get_first_components(self, *Types: Type[T]) -> List[Optional[T]]:
But this probably won't do what you want. The T will match a common base class of the types you pass in to the function, which might be object if your classes don't have any other common base class. That means that when you unpack the returned list into separate variables, they'll all be identified by the type checker as being instance of the base class, not each having the specific type you passed it in the corresponding position.
You can make your calling code work though, by using the other method that does have workable type hints:
pos = e.get_first_component(Pos2) # will be identified as Optional[Pos2]
m2 = e.get_first_component(Move2) # will be identified as Optional[Move2]
As a side note, since the values you're getting are Optional, you probably need a check for them being None. If you get the type hints working, you'll get warned if you do something like m2.active without checking that first, since None doesn't have an active attribute.

Python: Deleting a class instance

I'm new to Python and try to code some extras in my lerning lessons so now i need to code a class that crates an cube and than two methods. The methods are not the problem, but i want to generate a fail-save when creating my cube.
When you create the cube with any other types than int or float it should return that thats invalid and delete the created instance. I googled, tried and can't figure out how to get it done.
I also would like to generate the instance name inside the fail-save text. So it says "[...]instance "a" will be deleted[...]" when i create:
a = Cube("not an int or float")
and: "[...]instance "b" will be deleted[...]" when i try to create:
b = Cube("not an int or float")
Code:
class Cube():
def __init__(self, length):
if type(length) == int or type(length) == float:
self.length = length
else:
print("The type of length has to be int or float.\nThe instance (a) will be deleted!!")
del self
def surface(self):
print("surface")
def volume(self):
print("volume")
# creating an instance of the cube-class
a = Cube("not an int or float")
# and test the methods
a.surface()
a.volume()
Simply raise an exception if there is a problem with initialization. The exception will prevent the assignment from taking place, which means the object will be subject to immediate garbage collection, so you don't need to use del (which has no real effect anyway; del just decrements the reference count of the object by deleting the name, but the name self would go out of scope anyway, with the same effect).
Use isinstance to check the type of the parameter.
class Cube:
def __init__(self, length):
if not isinstance(length, (int, float)):
raise TypeError("Length must be an int or a float")
self.length = length
...
Ideally, though, you leave the burden to the caller to provide the correct type. You can use type hints to make it easier for the user to catch such errors:
from typing import Union
class Cube:
def __init__(self, length: Union[int, float]):
self.length = length
...
A tool like mypy can be used to check statically that no attempt to pass a different type of argument.

Can I use the typing information from another function as the return type in Python?

In the following example, how can I properly annotate the return type of the sum_two function?
from typing import Any, TypeVar
T = TypeVar('T')
S = TypeVar('S')
def sum_two(first: T, second: S):
return first + second
Assuming the __add__ operator is properly annotated for all possible arguments that will be passed to this function, is there some way to express the return type as the return type of calling __add__ on objects of type T and S?
I would like to avoid using typing's overload decorator to identify all possible cases as there could be dozens of cases.
You can theoretically accomplish a part of his by making first a generic protocol, which lets you "capture" the return type of __add__. For example:
# If you are using Python 3.7 or earlier, you'll need to pip-install
# the typing_extensions module and import Protocol from there.
from typing import TypeVar, Protocol, Generic
TOther = TypeVar('TOther', contravariant=True)
TSum = TypeVar('TSum', covariant=True)
class SupportsAdd(Protocol, Generic[TOther, TSum]):
def __add__(self, other: TOther) -> TSum: ...
Then, you could do the following:
S = TypeVar('S')
R = TypeVar('R')
# Due to how we defined the protocol, R will correspond to the
# return type of `__add__`.
def sum_two(first: SupportsAdd[S, R], second: S) -> R:
return first + second
# Type checks
reveal_type(sum_two("foo", "bar")) # Revealed type is str
reveal_type(sum_two(1, 2)) # Revealed type is int
reveal_type(sum_two(1.0, 2)) # Revealed type is float
# Does not type check, since float's __radd__ is ignored
sum_two(1, 2.0)
class Custom:
def __add__(self, x: int) -> int:
return x
# Type checks
reveal_type(sum_two(Custom(), 3)) # Revealed type is int
# Does not type check
reveal_type(sum_two(Custom(), "bad"))
This approach does have a few limitations, however:
It does not handle cases where there's no matching __add__ in 'first' but do have a matching __radd__ in 'second'.
You might get some weird results if you modify Custom so __add__ is an overload. I think at least mypy currently has a bug where it doesn't know how to handle complicated cases involving subtypes and overloads properly.

Python, set class variable to int value not int type [duplicate]

This question already has answers here:
Should I, and how to, add methods to int in python?
(1 answer)
Can I add custom methods/attributes to built-in Python types?
(8 answers)
Closed 5 years ago.
If I have a class such as this
class foo():
def __init__(self, value = 0):
self.__value = value
def __set__(self, instance, value):
self.__value = value
def calc(self):
return self.__value * 3
def __repr__(self):
return str(self.__value)
I can now make a variable of the class foo and use it's functions.
n = foo(3)
print(n.calc())
No problems there but if I keep going with something like this.
n = 5
print(n.calc())
I will get an error, because I have now set n to an int object with the value 5 and thus does not have the calc() function.
I normally work with C++ so I'm confused because I thought that the __set__ function was supposed to override the = operator and then set __value to the value of 5 just like if I were to to use
operator=(int value)
In C++, I have looked for an explanation but have not found any.
All help appreciated.
As stated here.
The following methods only apply when an instance of the class
containing the method (a so-called descriptor class) appears in an
owner class (the descriptor must be in either the owner’s class
dictionary or in the class dictionary for one of its parents).

Categories

Resources