Arbitrary type conversion in python - python

An arbitrary typecasting function (shown below as cast) seems like a fairly straightforward function:
print(type(variable))
variable = cast(variable,type) # where type is any type included in __builtins__
print(type(variable))
And the result:
>>> <original_type>
>>> <type>
Does such a function exist in python? I can't seem to find any reference to it if it does. If this function does not exist, please explain the rationale for why it does not.
As one example usage, I have a config with arbitrarily many values, and a schema with the desired type of each. I want to check that specified value for each config variable can be cast as corresponding type specified in the schema. Treating each as a dict below for convenience:
for variable in config.keys():
val = config[variable]
type_name = schema[variable]
try:
config[variable] = cast(val,type_name)
except TypeError:
print("Schema checking failed for variable {}".format(variable))

Ok, I think the comments have covered the matter in enough detail so I'll just try to summarize my best understanding of them here. Most of this is by way of #juanpa.arrivillaga.
A standard python casting operation like int(x) (or more precisely, a type conversion operation), is actually a call to the __call__() function of an object. Types like int, float, str, etc are all object classes and are all instances of the metaclass type. A call to one of these instance of type e.g. int.__call__() calls the int object constructor which creates a new instance of that type and initializes it with the inputs to __call__().
In short, there is nothing special or different about the common python "type conversions" (e.g. int(x), str(40)) other than that the int and str objects are included in __builtins__.
And to answer the original question, if type_name is an instance of the type class then the type_name.__call__() function simply declares and initializes a new instance of that type. Thus, one can simply do:
# convert x to type type_name
x = type_name(x)
however this may cause an exception if x is not a valid input to the type_name constructor.

To cast a value in another type you can use the type itself, you can pass the type as an argument and call it into a function and you can get it from the builtins module if you sure that the type is a builtin:
value = "1"
value = int(value) # set value to 1
value = 1
value = str(value) # set value to "1"
def cast(value, type_):
return type_(value)
import buitlins
def builtin_cast(value, type_):
type_ = getattr(buitlins, type_, None)
if isinstance(type_, type):
return type_(value)
raise ValueError(f"{type_!r} is not a builtins type.")
value = cast("1", int) # set value to 1
value = cast(1, str) # set value to "1"
value = builtin_cast("1", "int") # set value to 1
value = builtin_cast(1, "str") # set value to "1"

Related

Is it possible to instantiate a structure (tuple/list) knowing its type (Tuple/List)?

I'm currently developing python code to mock a certain C library. I have access to the library functions and docstrings thanks to pybind. The task is to mock the return of these functions.
The situation
So far, I can successfully read any function output using regex. Now, I need to evaluate the type of this output, get what's inside of this type and either instantiate it to a known value or fill it with an object. Here's an example of what I'm trying to explain:
docstring = parse(getattr(MyClass, the_method_I_want_to_mock).__doc__)
# The regex will read from -> to the end of the output hinting
method_type_search = re.search(r"(?<=-> ).+(?=)", docstring.short_description)
# If the regex finds something, evaluate the output
evaluated_method = eval(method_type_search.group(0))
At this point, an evaluated_method value would evaluate to something like : typing.Tuple[int, int]
The problem
Here's what I'm seeking to do:
Extract the type of the return
Extract what's inside (if, for example, I'm dealing with a tuple/list)
Create an instantiated structure with step 1) and 2). For example: typing.Tuple[int, int] would yield (0, 0) and typing.List[float, user_class] would yield [0.0, user_class()]
Here's what I have done so far:
# eval_method is in the form of `typing.Tuple[int, int]` like aforementioned
def test_evaluate_types(eval_method):
#This is the dictionary I plan on using to turn a type (ex: int) into its value (ex: 0).
#If any output requires an instantiated object (ex: typing.Tuple[user_class, int],
#I'll need to instantiate the user_class and turn the int into 0.
evaluate_dict: dict = {
int: 0,
List[int]: [0, 1, 2]
}
out = []
# checks if there is a structure or if its only one type (tuple[int, int] vs int)
try:
eval_method_type = eval_method._name
except AttributeError:
# if it's a simple type, return its value
return evaluate_dict[eval_method]
# This fetches what's inside a structure (ex: [<class 'int'>, <class 'int'>])
eval_method_output = eval_method.__args__
# parsing what is inside the structure and instanciating it.
for idx, output in enumerate(eval_method_output):
out.append(evaluate_dict[output])
#This WOULD casts the list into whatever structure was found earlier.
#It doesn't work and I'm stuck here.
return eval(eval_method_type + f"({out})")
I feel like I'm maybe complicating my issue, but can't seem to find a function/way to easily convert ANY type (even user type) into a chosen output like stated above.
It seems that the __origin__ dunder can be used to get the necessary tuple. So then, when I have the string Tuple[int, int], I then call an eval() on it, which gives me typing.Tuple[int, int]. Using the dunder __origin__ on the eval's result, I then get the tuple I was looking for and instantiate it directly.
string_to_eval: str = "Tuple[int, int]"
string_eval = eval(string_to_eval) # Yields the aforementionned typing.Tuple[int, int]
tuple_result = string_eval.__origin__() # Yields 'tuple'

Comparing Two Equal Values in a Function Returns False When Parameter is Integer Represented as a String

I have a function that checks if the size of a file, and a known historic size are equal:
def SizeEqual(filePath: str, size: int) -> bool:
return os.path.getsize(filePath) == size
If I pass in a variable of type int that has a value equal to that of the file size, this function will return True, but if I pass in a variable of the value represented as a str, it will return False.
Example:
os.path.getsize(someFile) # Equal to 5803896
SizeEqual(someFile, 5803896) # Returns True
strVar = '5803896'
SizeEqual(someFile, strVar) # Returns False
I had thought that because I had specified the type in the function parameter, that Python would either prevent the str type from being passed in, or implicitly convert it to an int.
What am I missing?
In python the type declaration that the function receives is based on a proposal, the user may or may not follow your instructions, but python will do nothing prevent or change passed parameters. That trait in python is called Duck Typing. If you want to prevent user to pass string you can use somethink like this:
import os
def SizeEqual(filePath: str, size: int) -> bool:
if isinstance(size, int):
return os.path.getsize(filePath) == size
else:
raise Exception("Passed value must be int.")
Some versions of python will do all the type management for you (I don't know what those versions are).
The standard Python doesn't do that. It uses Duck Typing. So, when you pass a string, it's going to keep it that way and use it that way.
There are two solutions to this:
Type check in the function
def SizeEqual(somefile, size):
try:
size = int(size)
except:
...
Just convert the parameter to int when you're passing (I prefer this way)
SizeEqual(somefile, int(size))

How do you type hint a function parameter that could be one of several types?

I'm adding type hints to some python 2 code and came across a minor dilemma. How does one hint the type of a function's return when that return could be one of several types?
I'm working with a function that looks like...
def foo(param):
# do stuff
return dict_
where param is a string and dict_ is a dictionary with keys that are always strings and values that could be either integers or strings.
My solution was to type hint the function as shown below, but wanted to check that this was the proper strategy.
def foo(param):
# type: (str) -> Dict[str, object]
# do stuff
return dict_
You want a union (also called a sum type) Union[str,int] instead of the more general object:
def foo(param):
# type: (str) -> Dict[str,Union[str,int]]
# ...
return dict_
Using object, you are stating that the values of the dictionary can be of any arbitrary type. Union[str,int] means it could be an int; or it could be a str; but it cannot be a float, or a list, or another dict, or anything other than an int or str.

python casting variables with type as argument

Is there a casting function which takes both the variable and type to cast to? Such as:
cast_var = cast_fn(original_var, type_to_cast_to)
I want to use it as an efficient way to cycle through a list of input parameters parsed from a string, some will need to be cast as int, bool, float etc...
All Python types are callable
new_val = int(old_val)
so no special function is needed. Indeed, what you are asking for is effectively just an apply function
new_val = apply(int, old_val)
which exists in Python 2, but was removed from Python 3 as it was never necessary; any expression that could be passed as the first argument to apply can always be used with the call "operator":
apply(expr, *args) == expr(*args)
Short answer:
This works:
>>> t = int
>>> s = "9"
>>> t(s)
Or a full example:
def convert(type_id, value):
return type_id(value)
convert(int, "3") # --> 3
convert(str, 3.0) # --> '3.0'

How to pass "random" amount of variables not all of them exist

I have a method to validate input:
def validate_user_input(*args):
for item in args:
if not re.match('^[a-zA-Z0-9_-]+$', item):
And I'm calling it like this:
validate_user_input(var1, var2, ..., var7)
But those are generated from user input, and some of those can be missing. What would be the proper way to do that, without creating tons of if statements?
Variables are assigned from a json input like so, and json input might not have some of the needed properties:
var1 = request.json.get('var1')
I assume they are <class 'NoneType'>
Here's the error: TypeError: expected string or buffer
If your request.json object is a dict or dict-like you can just pass a default value as second argument to get
If I understand correctly you are generating var_ variables by request.json.get('var_') which will either return a string which you want to validate or None if the field was missing.
If this is the case then you can just add a special case to validate_user_input for a None value:
def validate_user_input(*args):
for item in args:
if item is None:
continue #this is acceptable, don't do anything with it
elif not re.match('^[a-zA-Z0-9_-]+$', item):
...
Or it may make more sense to store all of the values you are interested in in a dictionary:
wanted_keys = {'var1','var2','var3'}
## set intersection works in python3
present_keys = wanted_keys & response.json.keys()
## or for python 2 use a basic list comp
#present_keys = [key for key in response.json.keys() if key in wanted_keys]
actual_data = {key: response.json[key] for key in present_keys}
Then you would pass actual_data.values() as the argument list to validate_user_input.
If it really is possible that some var-variables are undefined when you call validate_user_input, why not just initialize them all (e.g. to the empty string '' so that your regex fails) before actually defining them?

Categories

Resources