I am new here and new to programming (I have only been programming for 2 weeks). Does anyone have any suggestions on how to test a function that takes no parameters. I have tested functions that takes parameters but not one that takes no parameters.
On the test function I try passing an arguments expected as the letter 'a' in the valid_letter() function. But it gives the error that 'a' is not defined.
def valid_letter():
'''Function valid_letter
Parameters None
continuously asks for a valid letter
if invalid data is provided
'''
while True:
column = input("What column do you wish to select from a to g? ")
if column != "a" and column != "b" and\
column != "c" and column != "d" and\
column != "e" and column != "f" and\
column != "g" and column != "h":
print("Your input is invalid")
continue
else:
return column
break
def check_valid_column(expect):
answer = valid_letter()
print("Input: {}".format(answer))
print("Expected: {}, Actual {}".format(expect, answer))
def main():
check_valid_column(a)
main()
(Note: the original function was broken due to indentation so while I was fixing it I took the liberty of rewriting it to be simpler. It should still behave exactly the same as your intended original implementation and the testing strategy, which is the real focus of the question, is exactly the same regardless of implementation details.)
One way to do this is with patch:
def valid_letter() -> str:
'''
Prompts the user for a column between 'a' and 'g'.
Continuously asks for a valid letter if invalid data is provided.
'''
while True:
column = input("What column do you wish to select from a to g? ")
if ord(column) in range(ord('a'), ord('g') + 1):
return column
print("Your input is invalid")
from unittest.mock import Mock, patch
def test_valid_letter() -> None:
with patch('builtins.input', new=Mock(return_value='a')):
assert valid_letter() == 'a'
with patch('builtins.input', new=Mock(side_effect=['z', 'q', 'g', 'c'])):
assert valid_letter() == 'g'
test_valid_letter()
The patch statements in the test replace the builtin input function with a Mock object that returns a particular argument. In the first test it simply returns 'a', and so we assert that valid_letter() will return that same value. In the second test it returns successive values from the list each time it's called; we assert that valid_letter() will continue calling it in a loop until it reaches 'g'.
Another method would be via dependency injection:
from typing import Callable
from unittest.mock import Mock
def valid_letter(input_func: Callable[[str], str]) -> str:
'''
Prompts the user for a column between 'a' and 'g', using input_func.
Continuously asks for a valid letter if invalid data is provided.
'''
while True:
column = input_func("What column do you wish to select from a to g? ")
if ord(column) in range(ord('a'), ord('g') + 1):
return column
print("Your input is invalid")
def test_valid_letter() -> None:
assert valid_letter(Mock(return_value='a')) == 'a'
assert valid_letter(Mock(side_effect=['z', 'q', 'g', 'c'])) == 'g'
test_valid_letter()
In this example, rather than having valid_letter call the builtin input function, it accepts an arbitrary input function that the caller supplies. If you call it like:
valid_letter(input)
then it behaves exactly like the original, but the caller can also pass in arbitrary replacements without having to use patch. That makes testing a bit easier, and it also allows for the possibility of a caller wrapping or replacing input to allow for a different UI style -- for example, if this function were being used in a GUI app the caller could pass in an input_func that prompts the user via a dialog box instead of the terminal.
The same testing/injection logic applies to the print function -- you might find it useful to have your test validate that print is called each time an invalid value is entered, and another caller might find it useful to use an alternative output function.
The function will work with no parameters, and will still as for user input since you have input() in your called function.
The way you call valid_letter() is fine. That is not the issue. But you intended to verify that the input was "a" (which apparently represents the correct answer).
So then you should not do check_valid_column(a) as a is not a defined variable, but check_valid_column("a"), so with the character "a".
Related
So I am trying to make a script that will traverse a string and replace certain characters. The idea is fairly simple and sudo code looks a little like this.
Input1 = ''
Input2 = ''
Input3 = ''
rawPw = Input1 + Input2 + Input 3
Remove spaces if any exist
Creates a new empty str called finalPw
Scan the rawPw string one character at a time. Each character goes to a Random bool and if True it goes to get converted. If false it appends finalPw
In it goes to conversion it checks against a list of specified characters and if it matches it goes to a specific converter method. Otherwise it will just swap upper/lower case and append finalPw
The specific converter method will use a predefined list of characters that it can be replaced by, and will use random.choice() to pick from that list to replace the character and append finalPw.
This is the code I have so far, note the upper/lower case swapping is not part of it yet, I am building and testing the code piecemeal because my last prototype was 350 lines long and a complete failure. So going from scratch here.
import random
print("Enter 3 words or series of numbers or both, each entry must be at least 5 characters in length")
def main():
input1 = 'Jim'
input2 = 'Samantha'
input3 = 'Ethan'
rawPw = (input1 + input2 + input3)
refinedPw = rawPw.replace(' ', '')
print(refinedPw)
finalPw = ''
convertTrain(refinedPw)
def switch():
switchVal = random.choice(True, False)
return switchVal
def convertTrain():
temp = main.refinedPw
onOff = False
for i in temp:
switch(i)
if i == True:
if i == 'i':
the_iExc(i)
else:
main.finalPw.append(i)
def the_iExc():
rep_iExc = ['i', '!']
repVal = random.choice(rep_iExc)
for i in len(main.refinedwPw):
slice(i)
if i == 'i':
i.replace(repVal)
return i
main()
The error I am receiving is :
line 15, in main
convertTrain(refinedPw)
TypeError: convertTrain() takes 0 positional arguments but 1 was given
I have tried changing things around a bit to see if I can't fix the positional argument, so much so that I have stripped it of all classes and am just going classless until I get it to work. For the life of me I can't seem to get it to use the appropriate number of args.
This line defines convertTrain as a function that takes no arguments:
def convertTrain():
But then inside main() you call it with an argument:
convertTrain(refinedPw)
Either change the function definition to accept an argument, or change the call to not pass an argument.
You're trying to invoke function with arguments here:
convertTrain(refinedPw)
...but there are no arguments in the function's definition
def convertTrain()
To fix this, add a new argument for the convertTrain function, like this:
def convertTrain(train) # Name of argument can be changed if needed
Read more about function declaration here: click this
My numerical analysis professor gave me a project on programming several numerical methods in python.
And he asked me to run the program for some given functions so he can see the results of the methods.
My question is :
Is there any way so we can code something which the user input any chosen function and see the wanted results?
Or we must define the wanted function in the program specifically and there isn’t any way to do so ?
You can use a class to store your functions and access it by function name using getattr:
# Store your functions here
class Functions:
def f(x):
return x ** 2
def dist(a, b):
return pow(a**2 + b**2, .5)
def g(x):
return Functions.f(x) + 1
def main():
while True:
funcname = input('Insert the function name (type "exit" to stop): ')
if funcname == 'exit':
break
# Checks if the function name exists in Functions
if not hasattr(Functions, funcname):
print('This function does not exist!')
continue
# Store the function in a variable
func = getattr(Functions, funcname)
# Ask the user for the arguments to be used in the function
args = [float(arg) for arg in input('Insert the arguments (delimited by spaces): ').split()]
# Check if passed arguments match with function arguments
expected_args = func.__code__.co_argcount
actual_args = len(args)
if actual_args != expected_args:
print(f'Invalid number of arguments, expected {expected_args}, found {actual_args}')
continue
# Call the function with the provided arguments
print('The result is', func(*args))
if __name__ == '__main__':
main()
Usage example:
Insert the function name (type "exit" to stop): f
Insert the arguments (delimited by spaces): 10
The result is 100.0
Insert the function name (type "exit" to stop): dist
Insert the arguments (delimited by spaces): 3 4
The result is 5.0
Insert the function name (type "exit" to stop): g
Insert the arguments (delimited by spaces): 1
The result is 2.0
Insert the function name (type "exit" to stop): f
Insert the arguments (delimited by spaces): 1 2
Invalid number of arguments, expected 1, found 2
There is a way. By using regular expressions you can figure out what the function is by looking at the input string. İf you used any graphics calculater like desmos or geogebra, when you enter a mathematical expression as a string,the calculator will figure out what to calculate.
To parse math from a string input, you can use regular expressions.
If your question is how to run a specific function by calling it in a command-line input, you can store the functions in a separate module and use the inspect module to get references to these functions and their names.
import some_module
from inspect import getmembers, isfunction
functions = dict(getmembers(some_module, isfunction))
print("Available functions are: ", *functions.keys())
while True:
print("Type the name of the function you want to run and press enter.")
try:
functions[input()]()
except KeyError:
print("No such function found.")
>>> Available functions are: empty_function1 empty_function2
>>> Type the name of the function you want to run and press enter.
>>> Whatever
>>> No such function found.
>>> Type the name of the function you want to run and press enter.
>>> empty_function2
>>> Hello from the function 2!
I'm new to learning Python and have enough under my belt to start attempting a beginner's Tic-Tac-Toe program.
My issue is thus: I want to have a generic input function called getInput() which will get input from the user, strip trailing white space from that input, and THEN, if a function was passed to it via the optional parameter "specialTest", getInput() will run the input through this provided function and return the output which the specialTest function spat out.
Sometimes this specialTest function will need additional arguments besides the user input. Assume for my purposes that the user input will always be the first argument and is required, and that any additional args will come afterwards.
I tried to implement this situation via *args, and I got it working if the specialTest function had no additional arguments. But the first time I try to feed it additional arguments, it fails.
So for example, getInput("Age?", specialTest=int) works. It prompts for user input and feeds it through the int() function, finally returning the output as an integer. But when I try to pass getInput() a function which has an additional argument - an ordered dictionary which contains strings as keys and dictionaries as values - the program fails with TypeTypeError: getInput() got multiple values for argument 'specialTest'. What needs to be adjusted to get this working as intended?
Code:
import collections
def getInput(msg, specialTest=None, *TestArgs):
"""Get user input and export to the desired format."""
while True:
string = input(msg + ' ').strip()
# If the user passed a function to the SpecialTest parameter,
# pass the user input through that function and return its value.
# If the SpecialTest function returns False or we hit an error,
# that means the input was invalid and we need to keep looping
# until we get valid input.
if specialTest:
try:
string = specialTest(string, *TestArgs)
if string is False: continue
except:
continue
return string
def nametoMove(name, board):
"""Convert player's move to an equivalent board location."""
location = {name: theBoard.get(name)}
# return false if the location name isn't present on the board
if location[name] is None:
return False
return location
# ---Tic-Tac-Toe routine---
# fill the board
row_name = ('top', 'mid', 'lower')
col_name = ('left', 'center', 'right')
theBoard = collections.OrderedDict()
size = 3 # 3x3 board
for x in range(size):
for y in range(size):
key = row_name[x] + ' ' + col_name[y]
value = {'row': x, 'col': y}
theBoard.update({key: value})
# get player's desired board symbol
playerSymbol = getInput("X's or O's?")
# get player's age
playerAge = getInput("Age?", specialTest=int)
# get player's move and convert to same format as theBoard object
# e.g., "top left" --> {'top left': {'row': 0, 'col': 0}}
playerMove = getInput("Move?", specialTest=nametoMove, *theBoard)
In order to support supplying the same parameter via a positional or keyword argument, Python converts any keyword arguments that can be into positional arguments. That creates the conflict in your example. Syntactically, what you want can be achieved by simply omitting the argument:
playerMove = getInput("Move?", nametoMove, *theBoard)
Or you can resolve the ambiguity with a “keyword-only” argument:
def getInput(msg, *TestArgs , specialTest=None):
Then the keyword argument cannot be converted, so there is no collision. (This can be emulated in Python 2 by using **kw to accept arbitrary keyword arguments and then checking that only the expected one is actually provided.)
But the question you should be asking is “How can I preset some arguments to a function used as a callback?”, to which the answer is either a lambda:
playerMove = getInput("Move?", specialTest=lambda s: nametoMove(s, *theBoard))
or functools.partial:
playerMove = getInput("Move?", specialTest=functools.partial(nametoMove, board=theBoard))
With either of these, you don’t need TestArgs at all. The partial approach doesn’t support supplying trailing positional arguments (like varargs), but your nametoMove doesn’t actually want those anyway (as established in the comments). So in all the approaches above you omit the *.
I have created two functions namely inputData(): and validateNumber():
In the inputData() function I enter a value and stores it in a variable called number. And then I want to pass that parameter to validateNumber(): function. But it isn't work :(
It would be fine if anyone explain me the error :)
Regards.
Here's the code:
def inputData():
number = int(input("Enter a Number: "))
print(number)
return number
def validateNumber(number):
n=2
while number > n:
if number%n==0 and n!=number:
print("Not Prime")
break
else:
print("Prime")
break
return number
inputData()
validateNumber()
You need to perform the function call as follows:
validateNumber(inputData())
or
number = inputData()
validateNumber(number)
with def validateNumber(number) you are telling python that the function validateNumber must receive one parameter when it is called. But, you are not passing the parameter to it when you call it.
If you are new to programming, check this tutorial: Python Functions, to understand:
What are functions
How to define them
How to use them.
You need to store the value of inputData() function in some variable then pass it to second function like this
>> number = inputData()
>> validateNumber(number)
You're not passing the inputted number to the validate function.
returned_input_number = inputData()
validateNumber(returned_input_number)
Also, I find it a bit odd that your validateNumber function returns a number. It might be better to return True or False (depending on if the number is valid or not). Either that, or maybe 'validate' is the wrong name for the function.
Say, for example, in tkinter, someone created a function corresponding to each of the 26 letters of the English alphabet. Then, he/she wants to be able to take a string provided by the user, make all the uppercase letters lowercase, and then execute the function(s) corresponding to each letter of the string. Here is a pseudocode example of what I am talking about:
# The functions should draw, on a tkinter canvas, the letter each function corresponds to (a pseudocode example shown below):
a -> draw_a
b -> draw_b
c -> draw_c
d -> draw_d
# So on, so forth...
str = input('Enter a string please: ').lower()
for i in str:
if i is # a letter:
# execute function corresponding to current letter in string
else:
pass # If current character is not a letter
Is it possible to do this in Python? If so, how would I implement this ability?
Use a dictionary to map letters to functions:
def draw_a(): # ...
def draw_b(): # ...
per_letter = {
'a': draw_a, 'b': draw_b, # ...
}
for char in string:
if char.isalpha():
per_letter[char]()
Note that the function object is put in the dictionary, they are not called, not until we look them up in the dictionary with per_letter[char].
I assume you want the following:
If you have an 'a', call function draw_a
In that case you can use:
letter='a'
locals()["draw_"+letter]()
or, in your case:
for i in str:
if i is # a letter:
locals()["draw_"+i]()
else:
pass # If current character is not a letter
locals() is a dict containing all defined functions and some other stuff.
Calling locals()["draw_a"] returns the funtion draw_a as variable, which you then execute using ()