Does python typing support multiple types? - python-3.x

I was looking at typings set up on the web but I was curious if I can have multiple types. Example:
self.test: str or None = None
It shows valid on my intellisense but I wasnt sure if it computed it different.
I was trying to implement something equivalent to typescript.
test: number|null = null;
But I didn't see specific types in this regard. Pretty much all my items have the option of being None/Null.

You should use Union https://docs.python.org/3/library/typing.html#typing.Union
from typing import Union
self.test: Union[str, None]
You can use Optional[X] as a shorthand for Union[X, None].

Related

NamedTuple - сhecking types of fields at runtime

Is there a neat solution to raise an error if a value is passed to the NamedTuple field that does not match the declared type?
In this example, I intentionally passed page_count str instead of int. And the script will work on passing the erroneous value forward.
(I understand that linter will draw your attention to the error, but I encountered this in a case where NamedTuple fields were filled in by a function getting values from config file).
I could check the type of each value with a condition, but it doesn't look really clean. Any ideas? Thanks.
from typing import NamedTuple
class ParserParams(NamedTuple):
api_url: str
page_count: int
timeout: float
parser_params = ParserParams(
api_url='some_url',
page_count='3',
timeout=10.0,
)
By design, Python is a dynamically typed language which means any value can be assigned to any variable. Typing is only supported as hints - the errors might be highlighted in your IDE, but they do not enforce anything.
This means that if you need type checking you have to implement it yourself. On the upside, this can probably be automated, i.e. implemented only once instead of separately for every field. However, NamedTuple does not provide such checking out of the box.

Why doesn't PyCharm hint work with decorators?

PyCharm Version: 2019.1.2
Python Version: 3.7
I am trying to use least code to reproduce the problem. And this is the snippet of my code:
def sql_reader():
def outer(func):
def wrapped_function(*args, **kwargs):
func(*args, **kwargs)
return [{"a": 1, "b": 2}]
return wrapped_function
return outer
#sql_reader()
def function_read():
return "1"
result = function_read()
for x in result:
print(x['a'])
print(result)
Basically, what I am doing is to "decorate" some function to output different types. For example, in this snippet, the function being decorated is returning 1 which is int. However, in decorator, I change the behavior and return list of dict.
Functionally speaking, it works fine. But it looks like my IDE always complains about it which is annoying as below:
Is there anyway I can get rid of this warning message?
With all due respect, you are using an over 3 year old version of PyCharm. I struggle to see a reason for this. The community edition is free and requires no root privileges to unpack and run on Linux systems. You should seriously consider upgrading to the latest version.
Same goes for Python by the way. You can install any version (including the latest) via Pyenv without root privileges. Although the Python version requirement may be subject to external restrictions for the code you are working on, so that is just a suggestion. But for the IDE I see no reason to use such an outdated version.
Since I am not using your PyCharm version, I can not reproduce your problem. Version 2022.2.3 has no such issues with your code. Be that as it may, there are a few things you can do to make life easier for static type checkers (and by extension for yourself).
The first thing I would always suggest is to use functools.wraps, when you are wrapping functions via a decorator. This preserves a lot of useful metadata about the wrapped function and even stores a reference to it in the wrapper's __wrapped__ attribute.
The second is proper type annotations. This should go for any code you write, unless it really is just a quick-and-dirty draft script that you will only use once and then throw away. The benefits of proper annotations especially in conjunction with modern IDEs are huge. There are many resources out there explaining them, so I won't go into details here.
In this concrete case, proper type hints will remove ambiguity about the return types of your functions and should work with any IDE (bugs non withstanding). In my version of PyCharm the return type of your wrapper function is inferred to be Any because no annotations are present, which prevents any warning like yours to pop up, but also doesn't allow any useful auto-suggestions to be provided.
Here is what I would do with your code: (should be compatible with Python 3.7)
from functools import wraps
from typing import Any, Callable, Dict, List
AnyFuncT = Callable[..., Any]
ResultsT = List[Dict[str, int]]
def sql_reader() -> Callable[[AnyFuncT], Callable[..., ResultsT]]:
def outer(func: AnyFuncT) -> Callable[..., ResultsT]:
#wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> ResultsT:
func(*args, **kwargs)
return [{"a": 1, "b": 2}]
return wrapper
return outer
#sql_reader()
def function_read() -> str:
return "1"
Adding reveal_type(function_read()) underneath and calling mypy on this file results in the following:
note: Revealed type is "builtins.list[builtins.dict[builtins.str, builtins.int]]"
Success: no issues found in 1 source file
As you can see, at least mypy now correctly infers the type returned by the wrapper function we put around function_read. Your IDE should also correctly infer the types involved, but as I said I cannot verify this with my version.
Moreover, now PyCharm will give you auto-suggestions for methods available on the types involved:
results = function_read()
first = results[0]
value = first["a"]
If I now start typing results., PyCharm will suggest things like append, extend etc. because it recognizes result as a list. If I type first., it will suggest keys, values etc. (inferring it as a dictionary) and if I type value. it will give options like imag, real and to_bytes, which are available for integers.
More information: typing module docs

How do I use Type Hinting to show a List<Foo>

I have a bunch of classes which carry out functionality, but with type hinting I wanted to now have it show an instance of that class, Or a list of instances of that class.
class Foo:
pass
class Bar:
pass
class Win:
pass
I then started seeing Typings, and was doing things like:
get_all_foos() -> list:
return []
which makes sense, but i wanted it to be more verbose. Something akin to:
get_all_foos() -> list<Foo>:
return []
I was looking online and it seems that I might need to use the LIST typing, instead of base list.
from typing import List
get_all_foo() -> List[Foo]:
pass
While I dont think i was getting any errors, I wanted to make sure this was the correct way to do things per Python standard or if it suitable to just do:
get_all_foo() -> list[Foo]:
pass
The answer, based on research on the python docs, is as follows.
A list is something which can be implemented. It is a datastructure.
A List is a typing, which is used for type hinting. It is not a datastructure.
For people familiar with other typed languages, my mistake was that I was mistaking a List also as an alternate Datastructure with a similar implementation of list.
So that means, that you can have a function:
get_all() -> List[Foo]: pass
BUT the return type can't be a List, for reasons stated above.
It would still be a list as that is the actual datastructure. it is purely for decorator purposes. I was having issues conceptually with the fact that I was trying to enforce typing by making things be a List as the samples were showing, but since it wasnt instantiable, it all made sense.
Therefore you would easily have
def get_all() -> List[Foo]: return []
which returns a list. I was thinking both the hint and return should be the same type.

Is there any site where I can see the python method signatures?

I'm amazed to not have come across any such site so far.
For example, when I'm using the Python's dict() function. I want to what are the different parameters that it can accept. Something like the following:
return: dict, dict(list)
return: dict, dict(dict)
return: dict, dict(dict + dict)
return: dict, dict(tupple of format = (element=value, element2=value2))
Everywhere I search on the Internet it just brings me to limited examples rather than showing me a signature.
Here on Stackoverflow.com, I came across a question that stated we cannot use dict(dict). That dict() function can only be used with a list.
Is there any site/link that shows most of the ways that dict can be used or the signature of dict in the above format?
Follow the official documentation of Python:
https://docs.python.org/3.6/
You will get most of the ideas from it.

Unable to chain-access tuple types

Given:
struct NameType([u8;64]);
name: (NameType, NameType);
I can do:
let def = &name.0 OR &name.1
but I cannot do:
let def = &name.0.0 OR &name.1.0
to access the internals. I have to do it twice:
let abc = &name.0;
let def = &abc.0;
why am I unable to chain it to access inner sub-tuples, tuple-structs etc?
rustc 1.0.0-nightly (ecf8c64e1 2015-03-21) (built 2015-03-22)
As mentioned in the comments, foo.0.0 will be parsed as having a number. This was originally mentioned in the RFC, specifically this:
I'd rather not change the lexer to permit a.0.1. I'd rather just have that be an error and have people write out the names. We could always add it later.
You can certainly file a bug, but as a workaround, use parenthesis:
(foo.0).0
In my opinion, you shouldn't be nesting tuples that deep anyway. I'd highly recommend giving names to fields before you slowly go insane deciding if you wanted foo.0.1.2 or foo.1.2.0.
In addition to above answers, I have also found out that a gap would work wonders :) So;
foo.0. 0 OR foo.0 . 0 etc all work
is fine. Don't know how much it means but there is a way to chain it if somebody wants to though (without resorting to brackets)

Resources