To rephrase my question for below:
What is the point of telling the parameter is string when it cannot convert to string when integer was entered as input?
I understand we can use str() to convert integer to string but that's not the answering I'm looking for.
After running the code by entering gss inside the paramteter,
I received 1. However, when I look up the type of this results, it shows as NoneType.
Why is this not string?
gss=1
convert_gss_to_str(gss)
1
type(convert_gss_to_str(gss))
Nonetype
I ran the below codes thinking that the integer 1 will be converted to string '1'.
However, I received this error:
TypeError: convert_gss() missing 1 required positional argument: 'gss'
Any suggestion what I am doing wrong?
gss = 1
def convert_gss_to_str(gss: str):
print(gss)
convert_gss_to_str()
def convert_gss_to_str(gss: str):
print(gss)
This function takes one non-optional parameter gss and does not return anything, therefore its return type is Nonetype. If you want to do actual conversion you can use builtin function str() as suggested by Sawel.
Type conversion is not really necessary for print() as it will print integers anyways
def convert_gss_to_str(gss: str):
...
It's just a type hint.
For more information, read PEP484
While these annotations are available at runtime through the usual annotations attribute, no type checking happens at runtime. Instead, the proposal assumes the existence of a separate off-line type checker which users can run over their source code voluntarily. Essentially, such a type checker acts as a very powerful linter.
Just use the builtin function str.
>>> gss = 1
>>> str(gss)
"1"
Related
Let's say I have a python module with the following function:
def is_plontaria(plon: str) -> bool:
if plon is None:
raise RuntimeError("None found")
return plon.find("plontaria") != -1
For that function, I have the unit test that follows:
def test_is_plontaria_null(self):
with self.assertRaises(RuntimeError) as cmgr:
is_plontaria(None)
self.assertEqual(str(cmgr.exception), "None found")
Given the type hints in the function, the input parameter should always be a defined string. But type hints are... hints. Nothing prevents the user from passing whatever it wants, and None in particular is a quite common option when previous operations fail to return the expected results and those results are not checked.
So I decided to test for None in the unit tests and to check the input is not None in the function.
The issue is: the type checker (pylance) warns me that I should not use None in that call:
Argument of type "None" cannot be assigned to parameter "plon" of type "str" in function "is_plontaria"
Type "None" cannot be assigned to type "str"
Well, I already know that, and that is the purpose of that test.
Which is the best way to get rid of that error? Telling pylance to ignore this kind of error in every test/file? Or assuming that the argument passed will be always of the proper type and remove that test and the None check in the function?
This is a good question. I think that silencing that type error in your test is not the right way to go.
Don't patronize the user
While I would not go so far as to say that this is universally the right way to do it, in this case I would definitely recommend getting rid of your None check from is_plontaria.
Think about what you accomplish with this check. Say a user calls is_plontaria(None) even though you annotated it with str. Without the check he causes an AttributeError: 'NoneType' object has no attribute 'find' with a traceback to the line return plon.find("plontaria") != -1. The user thinks to himself "oops, that function expects a str". With your check he causes a RuntimeError ideally telling him that plon is supposed to be a str.
What purpose did the check serve? I would argue none whatsoever. Either way, an error is raised because your function was misused.
What if the user passes a float accidentally? Or a bool? Or literally anything other than a str? Do you want to hold the user's hand for every parameter of every function you write?
And I don't buy the "None is a special case"-argument. Sure, it is a common type to be "lying around" in code, but that is still on the user, as you pointed out yourself.
If you are using properly type annotated code (as you should) and the user is too, such a situation should never happen. Say the user has another function foo that he wants to use like this:
def foo() -> str | None:
...
s = foo()
b = is_plontaria(s)
That last line should cause any static type checker worth its salt to raise an error, saying that is_plontaria only accepts str, but a union of str and None was provided. Even most IDEs mark that line as problematic.
The user should see that before he even runs his code. Then he is forced to rethink and either change foo or introduce his own type check before calling your function:
s = foo()
if isinstance(s, str):
b = is_plontaria(s)
else:
# do something else
Qualifier
To be fair, there are situations where error messages are very obscure and don't properly tell the caller what went wrong. In those cases it may be useful to introduce your own. But aside from those, I would always argue in the spirit of Python that the user should be considered mature enough to do his own homework. And if he doesn't, that is on him, not you. (So long as you did your homework.)
There may be other situations, where raising your own type-errors makes sense, but I would consider those to be the exception.
If you must, use Mock
As a little bonus, in case you absolutely do want to keep that check in place and need to cover that if-branch in your test, you can simply pass a Mock as an argument, provided your if-statement is adjusted to check for anything other than str:
from unittest import TestCase
from unittest.mock import Mock
def is_plontaria(plon: str) -> bool:
if not isinstance(plon, str):
raise RuntimeError("None found")
return plon.find("plontaria") != -1
class Test(TestCase):
def test_is_plontaria(self) -> None:
not_a_string = Mock()
with self.assertRaises(RuntimeError):
is_plontaria(not_a_string)
...
Most type checkers consider Mock to be a special case and don't complain about its type, assuming you are running tests. mypy for example is perfectly happy with such code.
This comes in handy in other situations as well. For example, when the function being tested expects an instance of some custom class of yours as its argument. You obviously want to isolate the function from that class, so you can just pass a mock to it that way. The type checker won't mind.
Hope this helps.
You can disable type checking for on a specific line with a comment.
def test_is_plontaria_null(self):
with self.assertRaises(RuntimeError) as cmgr:
is_plontaria(None) # type: ignore
self.assertEqual(str(cmgr.exception), "None found")
Alright, so i was coding when i stumbled upon a problem:
def myFunction(string):
print(string + "test")
Now, when i put a string, it runs perfectly fine. But, when i put in an int:
myFunction(str(1)):
It still works? Now, i know i put a str() function to the "1" value. But, if i wanted to have a function that takes in a parameter with the data type string, and type an integer value to that parameter, it still works. How can i do it?
One option is to use f-strings, which allows myFunction to concatenate a type other than a string, with a string:
def myFunction(o: object):
print(f'{o}test')
Or, even better yet:
def myFunction(o: object):
print(o, 'test', sep='')
Then the desired call should work without needing to wrap the value in str() explicitly:
>>> myFunction(1)
1test
If you prefer to be more explicit, I'd suggest changing it like below; what this does is call str(o) internally, only it looks a bit nicer.
def myFunction(o: object):
print(f'{o!s}test')
For example, the following, the first parameter should be restricted to a string, and the second parameter should be a function. However, this is wrong syntax for both. Anyone can help suggest the correct syntax to impose the type restriction?
def GetArg(msg:String, converter:lambda, default):
print("{}, the default is '{}':".format(msg, default))
return converter(stdin.readline().strip())
It gives error
Traceback (most recent call last):
File "E:/stdin_ext.py", line 4, in <module>
def GetArg(msg:String, converter, default:String):
NameError: name 'String' is not defined
and
File "E:/stdin_ext.py", line 4
def GetArg(msg:String, converter:lambda, default:String):
^
SyntaxError: invalid syntax
You can use the typing module.
from typing import Callable
def get_arg(msg: str, converter: Callable[[str], str], default) -> str:
print(f"{msg}, the default is '{default}':")
return converter(stdin.readline().strip())
assuming your function converter takes a string a returns a string, and that get_arg returns a string. Note the code has also been modified to follow python's naming convention and uses f strings over older style format strings.
Also note that these type hints are just that, hints. They are not checked statically or at runtime. Although, certain IDE's will help you ensure they are correct.
You should use str instead of String and Callable instead of lambda:
from typing import Callable
def GetArg(msg:str, converter:Callable, default):
print("{}, the default is '{}':".format(msg, default))
return converter(stdin.readline().strip())
That is, unless you have a specific class called String and you are expecting an argument of its type.
When you write lambda you are defining a new lambda, not specifying a type for a function, whence the error.
I believe it is also important to point out that types in python are only a useful notation but it does not raise any error by itself, for example I could still call GetArg(1,2,3) and don't get any error just by the type hinting (of course I would get an error trying to pass an argument to an int afterwards).
Python 2 will implicitly convert str to unicode in some circumstances. This conversion will sometimes throw a UnicodeError depending on what you try to do with the resulting value. I don't know the exact semantics, but it's something I'd like to avoid.
Is it possible to use another type besides unicode or a command-line argument similar to --strict-optional (http://mypy-lang.blogspot.co.uk/2016/07/mypy-043-released.html) to cause programs using this implicit conversion to fail to type check?
def returns_string_not_unicode():
# type: () -> str
return u"a"
def returns_unicode_not_string():
# type: () -> unicode
return "a"
In this example, only the function returns_string_not_unicode fails to type check.
$ mypy --py2 unicode.py
unicode.py: note: In function "returns_string_not_unicode":
unicode.py:3: error: Incompatible return value type (got "unicode", expected "str")
I would like both of them to fail to typecheck.
EDIT:
type: () -> byte seems to be treated the same way as str
def returns_string_not_unicode():
# type: () -> bytes
return u"a"
This is, unfortunately, an ongoing and currently unresolved issue -- see https://github.com/python/mypy/issues/1141 and https://github.com/python/typing/issues/208.
A partial fix is to use typing.Text which is (unfortunately) currently undocumented (I'll work on fixing that though). It's aliased to str in Python 3 and to unicode in Python 2. It won't resolve your actual issue or cause the second function to fail to typecheck, but it does make it a bit easier to write types compatible with both Python 2 and Python 3.
In the meantime, you can hack together a partial workaround by using the recently-implemented NewType feature -- it lets you define a psuedo-subclass with minimal runtime cost, which you can use to approximate the functionality you're looking for:
from typing import NewType, Text
# Tell mypy to treat 'Unicode' as a subtype of `Text`, which is
# aliased to 'unicode' in Python 2 and 'str' (aka unicode) in Python 3
Unicode = NewType('Unicode', Text)
def unicode_not_str(a: Unicode) -> Unicode:
return a
# my_unicode is still the original string at runtime, but Mypy
# treats it as having a distinct type from `str` and `unicode`.
my_unicode = Unicode(u"some string")
unicode_not_str(my_unicode) # typechecks
unicode_not_str("foo") # fails
unicode_not_str(u"foo") # fails, unfortunately
unicode_not_str(Unicode("bar")) # works, unfortunately
It's not perfect, but if you're principled about when you elevate a string into being treated as being of your custom Unicode type, you can get something approximating the type safety you're looking for with minimal runtime cost until the bytes/str/unicode issue is settled.
Note that you'll need to install mypy from the master branch on Github to use NewType.
Note that NewType was added as of mypy version 0.4.4.
I am trying to call a function from a string in Python as explained in
Calling a function of a module from a string with the function's name in Python.
Unfortunately, this doesn't work, and the Python interpreter throws an error:
TypeError: 'str' object is not callable
def current(self, t):
if self.iMode == None:
return self.i
else:
return getattr(self, 'iMode')(t)
The error refers to the last line. iMode has been set to sinx(t), that has been declared in the class.
Can anyone help me please?
From the error message it is obvious that your attribute was set to 'sinx(t)' (the string literal).
You should set it the function reference sinx instead, which is a callable.
However, as zhangyangu already said, in you example using getattr() is not needed. Maybe you really want to use a parameter (string reference) instead of the literal 'iMode'?
From the error, your iMode is a string. The iMode is not a method. There must be something wrong with your declaration. And in the class you can use self.iMode, no need to use getattr.
I think you may look for the function like eval.