I have some inkling of how this might work, but I do not know the exact syntax of how to do it.
An example:
def function(string):
string = string + "A"
How will the function know that what I am inputting is a string?
Is it something along the lines of...
string = type.str
There are some ways to check the type of a variable. First, you could check if the parameter is an instance of str:
def isstring(parameter):
return isinstance(parameter, str)
Another way could be to try to concatenate a string (in this case "") with the parameter and catch eventually exceptions (TypeError exception):
def isstring(parameter):
try:
parameter += ""
return True
except TypeError:
return False
Another way could be to use type (similar to isinstance, but difference mostly when polymorphism is involved):
def isstr(parameter):
return type(parameter) == str
You could also use assert, but if you want just to check if a parameter is of a certain type, I would adopt the first case.
No, you could NOT, python is dynamic but not static, so it could not know the argument type at "compile time", instead, you could check the input type by assert.
def fun(string):
assert isinstance(string, basestring) # we usually check if it type is basestring as this works fine for both ASCII string and Unicode.
string = string + "A"
return string
if __name__ == '__main__':
fun(2)
Related
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))
I have a function which could get a String formatted like this:
"true"^^<http://www.w3.org/2001/XMLSchema#boolean>
"100"^^<http://www.w3.org/2001/XMLSchema#int>
Now i want to split the String on the ^^ Characters and convert the first part of the string based on the second part. I also want to remove the " first before converting.
This is my code which i use for this:
def getValue(tObject):
toReturn = tObject.split("^^")
if len(toReturn) == 2:
if toReturn[1] == "<http://www.w3.org/2001/XMLSchema#boolean>":
return bool(toReturn[0].replace('"', ""))
elif toReturn[1] == "<http://www.w3.org/2001/XMLSchema#int>":
return int(toReturn[0].replace('"', ""))
return None
But i'm not so happy with it. Is there maybe a more elegant (pythonic) way to archive this?
You can use a regex, to
check if the given value is valid
retrieve the value to cast, and the way to cast
PATTERN = re.compile(r'"(.*)"\^\^<http:.*#(\w+)>')
types = {"boolean": bool, "int": int}
def getValue(value):
m = PATTERN.fullmatch(value)
return types[m.group(2)](m.group(1)) if m else None
Instead of if len(...) you could just try to unpack the result and except a ValueError. Then you can use a dict for the types and str.strip instead of str.replace:
types = {'boolean': bool, 'int': int}
try:
value, type_hint = tObject.split('^^')
except ValueError:
return None
else:
return types[type_hint.rstrip('>').rsplit('#', 1)[1]](value.strip('"'))
Firstly, you could remove return None, since the function returns None by default.
Secondly, you could use toReturn[1].endswith("boolean>") to match the end of the string, instead of matching the whole string with toReturn[1] == "<http://www.w3.org/2001/XMLSchema#boolean>". Same with the int string as well.
Thirdly, you could store the return value in one variable before the if..elif, then you don't have to calculate it twice for each condition.
Code:
def getValue(tObject):
toReturn = tObject.split("^^")
if len(toReturn) == 2:
return_value = toReturn[0].replace('"', "")
if toReturn[1].endswith("boolean>"):
return bool(return_value)
elif toReturn[1].endswith("int>"):
return int(return_value)
This might not be much of a logic improvement, but the code does look less cluttered now. If you wan't more terse, "pythonic" ways of doing this problem, the other answers might be more suitable.
I want a python type-hint friendly way to create a Type that has constrained range of values.
For example, a URL Type based on type str that would only accept strings that look like an "http" URL.
# this code is made up and will not compile
class URL(typing.NewType('_URL', str)):
def __init__(self, value: str, *args, **kwargs):
if not (value.startswith('http://') or value.startswith('https://')):
raise ValueError('string is not an acceptable URL')
overriding built-in immutable types works well
overriding str; http URL strings
Here is an example overriding str. This does not require the typing module but still works with type-hinting.
This str derived class asserts the initialized string looks like an http URL string.
class URL(str):
def __new__(cls, *value):
if value:
v0 = value[0]
if not type(v0) is str:
raise TypeError('Unexpected type for URL: "%s"' % type(v0))
if not (v0.startswith('http://') or v0.startswith('https://')):
raise ValueError('Passed string value "%s" is not an'
' "http*://" URL' % (v0,))
# else allow None to be passed. This allows an "empty" URL instance, e.g. `URL()`
# `URL()` evaluates False
return str.__new__(cls, *value)
This results in a class that will only allow some strings. Otherwise, it behaves like an immutable str instance.
# these are okay
URL()
URL('http://example.com')
URL('https://example.com')
URL('https://')
# these raise ValueError
URL('example') # ValueError: Passed string value "example" is not an "http*://" URL
URL('') # ValueError: Passed string value "" is not an "http*://" URL
# these evaluate as you would expect
for url in (URL(), # 'False'
URL('https://'), # 'True'
URL('https://example.com'), # 'True'
):
print('True') if url else print('False')
(update: later on I found the purl Python library)
Another example,
overriding int; constrained integer range Number
This int derived class only allows values 1 through 9 inclusive.
This has a special feature, too. In case an instance is initialized with nothing (Number()) then that value equates to 0 (this behavior is derived from the int class). In that case, the __str__ should be a '.' (program requirement).
class Number(int):
"""integer type with constraints; part of a Sudoku game"""
MIN = 1 # minimum
MAX = 9 # maximum
def __new__(cls, *value):
if value:
v0 = int(value[0])
if not (cls.MIN <= v0 <= cls.MAX):
raise ValueError('Bad value "%s" is not acceptable in'
' Sudoku' % (v0,))
# else:
# allow None to be passed. This allows an "empty" Number instance that
# evaluates False, e.g. `Number()`
return int.__new__(cls, *value)
def __str__(self):
"""print the Number accounting for an "empty" value"""
if self == 0:
return '.'
return int.__str__(self)
This ensures errant inputs are handled sooner rather than later. Otherwise, it behaves just like an int.
# these are okay
Number(1)
Number(9)
Number('9')
# this will evaluate True, just like an int
Number(9) == int(9)
Number('9') == int(9)
Number('9') == float(9)
# this is okay, it will evaluate False
Number()
print('True') if Number() else print('False') # 'False'
# these raise ValueError
Number(0) # ValueError: Bad value "0" is not acceptable in Sudoku
Number(11) # ValueError: Bad value "11" is not acceptable in Sudoku
Number('11') # ValueError: Bad value "11" is not acceptable in Sudoku
And the special "feature"
print(Number(1)) # '1' (expected)
print(Number()) # '.' (special feature)
Technique for inheriting immutable types is derived from this SO answer.
Subclassing builtin types can lead to some odd cases (consider code which checks exactly type(...) is str)
Here is a pure-typing approach which is typesafe and fully preserves the type of your strings:
from typing import NewType
_Url = NewType('_Url', str)
def URL(s: str) -> _Url:
if not s.startswith('https://'):
raise AssertionError(s)
return _Url(s)
print(type(URL('https://example.com')) is str) # prints `True`
The approach here "hides" the runtime checking behind a function which looks like a constructor from an api perspective, but in reality is just a tiny type (I couldn't find a canonical reference to "tiny types" this appears to just be the best resource I could find).
I have the following little function written in Python:
def encode(str):
out = ""
for i in str:
ret += str(ord(i.upper()) - 64)
return ret
Basically, what I want to do is get the number of the letter in the alphabat and concatenate it to the 'out' string. With this code I get a traceback at line 4: 'str' object is not applicable.
Could someone please explain me why it throws this error and how I can fix this? (Sorry if this was already asked once, I couldn't find it, probably also because I'm pretty new to Python and programming)
Never name your variable on the pre-defined built-in name.
In your code, str is not a built-in function. It's the variable you have used as parameter in your function.
Another problem is, you have declared out variable, and using ret which will give you error. Change out = "" to ret = "".
Don't call your variable str, you're shadowing the built-in function.
Also, you need to fix the naming of out/ret.
I personally would write this function as follows:
def encode(s):
return ''.join(str(ord(c.upper()) - 64) for c in s)
(I don't really follow what the str(ord(...)) is meant to be doing, so I've just copied it from your code.)
As the others have said, do not use str as a variable.
I suspect this is what you want though:
def encode(s):
return "".join(chr(ord(c.upper()) - 64) for c in s)
This is equivalent to:
def encode(s):
out = ""
for c in s:
ret += chr(ord(c.upper()) - 64)
return ret
You were looking for the chr() function, which converts a numerical ASCII/Unicode value into a 1-character string. Running str() would convert 5 to "5".
Also on a stylistic note, it's customary to do for c in s when iterating over a string, and for i in x when iterating over a sequence of integers.
In using a function, I wish to ensure that the type of the variables are as expected. How to do it right?
Here is an example fake function trying to do just this before going on with its role:
def my_print(begin, text, end):
"""Print 'text' in UPPER between 'begin' and 'end' in lower
"""
for i in (begin, text, end):
assert isinstance(i, str), "Input variables should be strings"
out = begin.lower() + text.upper() + end.lower()
print out
def test():
"""Put your test cases here!
"""
assert my_print("asdf", "fssfpoie", "fsodf")
assert not my_print("fasdf", 33, "adfas")
print "All tests passed"
test()
Is assert the right approach? Should I use try/except instead?
Also, my assert set of tests does not seem to work properly :S
Thanks pythoneers
The isinstance built-in is the preferred way if you really must, but even better is to remember Python's motto: "it's easier to ask forgiveness than permission"!-) (It was actually Grace Murray Hopper's favorite motto;-). I.e.:
def my_print(text, begin, end):
"Print 'text' in UPPER between 'begin' and 'end' in lower"
try:
print begin.lower() + text.upper() + end.lower()
except (AttributeError, TypeError):
raise AssertionError('Input variables should be strings')
This, BTW, lets the function work just fine on Unicode strings -- without any extra effort!-)
You might want to try this example for version 2.6 of Python.
def my_print(text, begin, end):
"Print text in UPPER between 'begin' and 'end' in lower."
for obj in (text, begin, end):
assert isinstance(obj, str), 'Argument of wrong type!'
print begin.lower() + text.upper() + end.lower()
However, have you considered letting the function fail naturally instead?
Doing type('') is effectively equivalent to str and types.StringType
so type('') == str == types.StringType will evaluate to "True"
Note that Unicode strings which only contain ASCII will fail if checking types in this way, so you may want to do something like assert type(s) in (str, unicode) or assert isinstance(obj, basestring), the latter of which was suggested in the comments by 007Brendan and is probably preferred.
isinstance() is useful if you want to ask whether an object is an instance of a class, e.g:
class MyClass: pass
print isinstance(MyClass(), MyClass) # -> True
print isinstance(MyClass, MyClass()) # -> TypeError exception
But for basic types, e.g. str, unicode, int, float, long etc asking type(var) == TYPE will work OK.
isinstance(x, str) is best if you can use it, but it does not work with generics. For example you cannot do:
isinstance(x, dict[str, int])
It will give a runtime error:
TypeError: isinstance() argument 2 cannot be a parameterized generic
If you are only interested in asserting a type for a static type checker you can use cast:
from typing import cast
x_as_dict = cast(dict[str, int], x)
Unlike isinstance() it doesn't actually do the type check so you have to check all of the keys and values yourself if necessary.
(I realise this isn't exactly what you asked for but "type assertion" is also used to refer to things like cast() so I'm avoiding another question that will get closed as duplicate.)