typing helps the Python development with the data type. But sometimes it’s hard to work with type. How can we implement it if we want to check if a list has only one data type?
How to check the object type
Firstly, we should know how to check the object data type. We can know it by using type
.
print(type("sss")) # <class 'str'>
print(type(123)) # <class 'int'>
print(type(True)) # <class 'bool'>
print(type([1, 2])) # <class 'list'>
If it’s necessary to compare the type to do the different processes according to the type, we can write it this way.
print(type("SSS") == str) # True
if type(val) == str:
# Do something here for string type
elif type(val) == int:
# Do something here for int type
It’s also possible to use it for class.
class MyClass:
pass
instance = MyClass()
print(type(instance) == MyClass) # True
isinstance is equivalent to instanceof in TypeScript
I’m familiar with TypeScript. So I looked for an equivalent to instanceof
. It checks if the object is an instance of something.
class Person {
constructor(private name: string) { }
public sayHello(): void {
console.log(`Hello, I'm ${this.name}`);
}
}
const instance = new Person("Yuto");
if (instance instanceof Person) {
console.log("instance is Person");
}
In Python, isinstance
has the same feature. It can be used for all types because everything is an object type in Python.
def typeof(val: Any) -> Type[Union[int, str, bool, None]]:
if isinstance(val, str):
return str
elif isinstance(val, bool):
return bool
elif isinstance(val, int):
return int
else:
return type(None)
print(typeof("abc")) # <class 'str'>
print(typeof(122)) # <class 'int'>
print(typeof(True)) # <class 'bool'>
print(typeof(["abc", 122])) # None
But be careful with the order of the if-else if you need to do a different process for bool and int. Boolean is handled as int.
print(isinstance(True, int)) # True
print(isinstance(False, int)) # True
print(list(map(int, [True, False]))) # [1, 0]
If the int check comes first, the boolean value is also processed there.
We understand that isintance
can be used in the same context as using type
. The following two statements are the same.
if type(val) == str:
# something
if isinstance(val, str):
# something
We can use one of them according to our preference.
Check if the list has only one data type
Runtime:OK, type checker: NG
If it’s necessary to check if the list has only one data type, we can write the following functions.
def is_str_list(val: List[Any]) -> bool:
if len(val) == 0:
return False
return all(isinstance(x, str) for x in val)
def is_int_list(val: List[Any]) -> bool:
if len(val) == 0:
return False
return all(isinstance(x, int) for x in val)
print("--- is_str_list ---")
print(is_str_list([])) # False
print(is_str_list([1, 2, 3])) # False
print(is_str_list(["1", 2])) # False
print(is_str_list(["1", "2"])) # True
print("--- is_int_list ---")
print(is_int_list([])) # False
print(is_int_list([1, 2, 3])) # True
print(is_int_list(["1", 2])) # False
print(is_int_list(["1", "2"])) # False
It can be used in production but the type checker running on IDE doesn’t recognize that the list has only one data type.
Loot at the following function.
def process_list(values: Union[List[int], List[str], List[int | str]]) -> None:
if is_str_list(values):
# (variable) x: int | str
# "x" is not accessed (Pylance)
for x in values:
# IDE doesn't recognize that there is only one data type in the list
pass
def return_list(val: List[Any]) -> Union[List[int], List[str]]:
if is_str_list(val):
# (parameter) val: List[Any]
return val
elif is_int_list(val):
# (parameter) val: List[Any]
return val
raise Exception("The specified list has mixed data type")
If a cursor is on the x variable in the first function, it shows the info. It is still a mixed data type.
In the second function, the returned value is recognized as List[Any]
but not List[int], List[str]
.
runtime:OK, type checker: OK
I defined the following function to improve the previous problem.
def str_list(val: List[Any]) -> Optional[List[str]]:
is_mix = any(not isinstance(i, str) for i in val)
if is_mix:
return None
return val
print(str_list([])) # []
print(str_list([1, 2, 3])) # None
print(str_list(["1", 2])) # None
print(str_list(["1", "2"])) # ['1', '2']
If it returns a list when the specified list has only one data type, the type checker recognizes the type and it can help us better.
def return_list2(val: List[Any]) -> Union[List[int], List[str]]:
result = str_list(val)
if result:
# (variable) result: List[str]
return result
result = int_list(val)
if result:
# (variable) result: List[int]
return result
raise Exception("The specified list has mixed data type")
Overview
Currently (Python 3.10), it seems not possible to infer the data type of the first element of the list. If it’s possible to infer it, we could write it in another and better way.
So, this would be the current solution for me.
def same_type_list(val: List[Any]) -> Union[List[int], List[str]]:
result = int_list(val)
if result:
return result
result = str_list(val)
if result:
return result
raise Exception("The specified list has mixed data type")
Comments