Access subtype of type hint [duplicate] - python

This question already has answers here:
How to access the type arguments of typing.Generic?
(5 answers)
Closed 19 days ago.
class MyClass:
prop: list[str]
MyClass.__annotations__
# {'prop': list[str]}
How do I access "str"?
As a more generic question, given an obnoxiously complex and long type hint, like prop: list[set[list[list[str] | set[int]]]], how do I access the internal values programmatically?

Here is a function that will recursively pretty print the type and all the types inside it, using typing.get_args as suggested by #user2357112 :
from typing import List, Set, Union
import typing
complex_type = Union[List[str], Set[int]]
def log_type_layers(typing_type, indent=4, depth=0):
current_type = typing_type
print(" "*indent*depth + str(current_type))
for subtype in typing.get_args(current_type):
log_type_layers(subtype, indent=indent, depth=depth+1)
log_type_layers(complex_type)
Output:
typing.Union[typing.List[str], typing.Set[int]]
typing.List[str]
<class 'str'>
typing.Set[int]
<class 'int'>

Related

How to type hint a list with a particular structure [duplicate]

This question already has an answer here:
Is there a way to define a list data structure with specific element types in Python 3.9 type hinting? [closed]
(1 answer)
Closed 12 months ago.
I have a data structure in my code that (for the sake of an MWE) is a list where the first element is a string, and the second element is an integer. For example:
foo: MyStructure = ["hello", 42].
Now, since there is an ordering to this structure, usually I would use a tuple and instead do:
foo: Tuple[str, int] = ("hello", 42).
But I explicitly want to be able to easily modify elements within the structure. In particular, I want to be able to set foo[0] = "goodbye", which cannot be done if foo is a tuple.
What is the best way to go about typing this structure?
(I don't think that this question is opinion-based, since I think there is likely clear rationale for how to handle this that would be preferred by most developers.)
Right now, the main solution I can think of is to not actually type the structure correctly, and instead to define my own structure whose true type is listed in a comment:
# MyStructure = [str, int]
MyStructure = List[Union[str, int]]
foo: MyStructure = ["hello", 42]
Is there a better way?
You don't want a list or a tuple; you want a custom class representing the type-level product of str and int. A dataclass is particularly useful here.
from dataclasses import dataclass
#dataclass
class MyStructure:
first: str
second: int
foo: MyStructure = MyStructure("hello", 42)
assert foo.first == "hello"
assert foo.second = 42
If you really want to access the components using integer indices, you can add a __getitem__ method to the class:
#dataclass
class MyStructure:
first: str
second: int
def __getitem__(self, key) -> Union[str,int]:
if key == 0:
return self.first
elif key == 1:
return self.second
else:
raise IndexError(key)
In addition, an instance of MyStructure uses less memory than the corresponding list:
>>> foo = MyStructure("hello", 42)
>>> import sys
>>> sys.getsizeof(foo)
48
>>> sys.getsizeof(["hello", 42])
72

What type is "__main__.Example"? [duplicate]

This question already has answers here:
Understanding class type '__main__.ClassName'
(2 answers)
Closed 2 years ago.
I'm creating a class and checking the arguments for __init__.
class Example:
def __init__(self, a):
for key, val in locals().items():
print(type(val))
When I create this class, (something like Example(14)) I get <class '__main__.Example'> on the first line.
I know this is 'self', but what type is it?
The __main__ prefix is present because your class is defined in your script's global scope.
>>> class A:
... pass
...
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>>
The type of Example(14) is... well, Example, since that's the class of which that object is an instance.

Typing the instance of a class in python [duplicate]

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.

Is it possible to make a class object iterable in Python? [duplicate]

This question already has answers here:
Python: Make class iterable
(6 answers)
Closed 2 years ago.
I'm wondering if it's possible to make a class object iterable in Python (ie. a subclass of type, NOT an instance of a class).
I've tried the following code:
class Foo:
#classmethod
def __iter__(cls):
yield 1
print(next(Foo.__iter__())) # prints 1
print(next(iter(Foo))) # TypeError: 'type' object is not iterable
Turns out it's possible with metaclasses.
class Foo(type):
def __iter__(self):
yield self.baz
class Bar(metaclass=Foo):
baz = 1
print(type(Bar)) # prints "<class '__main__.Foo'>"
print(next(Bar.__iter__())) # prints "1"
print(next(iter(Bar))) # prints "1"
Thanks #DanielB for pointing me in the right direction.
This isn't possible, as it would imply that all type objects are iterable - as you stated, classes are of type type. The underlying reason for this is that the __iter__ method is looked up on the type, not the value. E.g., in:
for i in Foo:
print(i)
Python will look up type(Foo).__iter__, not Foo.__iter__.

is it possible to have 2 type hints for 1 parameter in Python? [duplicate]

This question already has answers here:
How to specify "nullable" return type with type hints
(2 answers)
How to specify multiple return types using type-hints
(4 answers)
Closed 3 years ago.
so here i have some definition for a container class:
class Box:
def __init__(self, x1: int) -> None:
self._x1 = x1
#property
def x1(self) -> int:
return self._x1
Then in my second function, I will pass in this Box object
class Foo:
def __init__(self, bar_1: Box) -> None
Then now, for some cases, bar_1 can also be None...and if i pass none to class Foo, error will come out:
"Foo" has incompatible type "None"; expected "Box"
so how can i walk around this? does python allow 2 type hints at the same time?
class Foo:
def __init__(self, bar_1: Box or None) -> None
i need help!
The type hint for an optional argument would be, appropriately, Optional:
from typing import Optional
def foo(bar: Optional[Box]): ...
Which is just a shorthand for Union[Box, None]. More generically, Union allows for "multiple type hints".

Categories

Resources