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.
Related
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)
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.
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.
I'm trying to use the Python function annotations (PEP 3107) as type hints for PyCharm, but failed to do so. The problem is probably related to my use of ABCMeta:
import abc
class base(object, metaclass=abc.ABCMeta):
#abc.abstractmethod
def test(self):
pass
class deriv1(base):
def test(self):
return "deriv1"
class deriv2(base):
def test(self):
return "deriv2"
my_list = []
def append_to_list(el: base) -> list(base):
# def append_to_list(el):
# """
# :param el: item to add
# :type: base
# :return: items so far
# :rtype: list[base]
# """
my_list.append(el)
return my_list
append_to_list(deriv1())
a = append_to_list(deriv2())
for o in a:
print(o.test())
This code does not run. Instead, I get a TypeError: 'ABCMeta' object is not iterable on the def append_to_list line.
When I use the alternative function with docstring type hints (the commented lines in the code above), everything works great.
Is it possible to use annotations for this kind of type hinting?
It's not related to abc but because you told Python to literally evaluate
list(base)
which is impossible because base is not iterable. That's what the error message is telling you.
You need to change it to square brackets and wrap it in quotes (because the list type is not subscriptable):
def append_to_list(el: base) -> 'list[base]':
or use typing.List which is subscriptable:
from typing import List
def append_to_list(el: base) -> List[base]:
To indicate it's a list containing base objects.
I am searching for a class method which decides which arguments will be given when an instance of the class is given as an argument.
I have this:
class Answers_Matrix(list):
def __setitem__(self, index, value):
if (type(value) is int):
if (0 <= value <= 255):
list.__setitem__(self, index, value)
else:
print "Invalid value size."
else:
print "Invalid value type. Value must be an integer."
def __repr__(self):
# a function I made which returns a matrix in a string format
return _matrix_to_string(self)
# **EDIT:**
# here I want a __asargument__ or something alike, so when an instance
# of this class is given as an argument I can decide what and how it
# will be given.
# Example:
# def __asargument__(self):
# array = (ctypes.c_ubyte*len(self))(*self)
# return array
Is there something alike in python which I can use?
What you want is not possible. There is no way to say that when you call
foo(Answers_Matrix())
foo will actually receive some other thing derived from Answers_Matrix(). This is for good reason, as it would be incredibly confusing and difficult to implement. Particularly, it's very likely that you'd want to use self as an argument to something in the hypothetical __asargument__ method, and that'd lead to either infinite recursion or extremely confusing context-sensitive semantics for when __asargument__ is or isn't called.
If you want object A to be replaced with object B whenever you try to use it for anything, don't have object A in the first place. Just use object B.