Make function parameters optional - python

I want the function to return a value when there is an error. I created this sample code, but it has two parameters. How can I make the function parameters optional? Because when I run the code below, my IDE returns an error that the parameters are required.
def test(error, parameter1):
if parameter1 == True:
print("true")
else:
print("false")
array = []
try:
array[0]
except Exception as e:
error = e
return error
value1 = 1
value2 = 2
if value1 == 1:
#I want this to print the error of the function above
print(test(error))
if value2 == 2:
# I want this to pass parameter to the function above
test(parameter1=True)

Optional paramteters are declared with =None, i.e.:
def test(error=None, parameter1=None):
...
in your function you'll have to check if the parameters are not none, i.e.:
if error is not None:
....

You could set the function parameters some default values so when no parameters are passed, the default values become the values of the variables:
def test(error = None, parameter1 = None):
if parameter1 == True:
print("true")
else:
print("false")
array = []
try:
array[0]
except Exception as e:
error = e
return error
Here None becomes a default value and if a parameter is passed, the value changes from None to the given value.

Related

Python confusion with return value in try-except-finally

Here is a piece of my code:
def main():
num = 0
try:
raise Exception('This is the error message.')
except Exception:
num += 1
return num
finally:
num += 1
a = main()
print(a)
The returning value is 1 instead of 2, this does not make a lot of sense for me.
I thought it would return 2 since finally should execute before returning the value.
Can someone help me to understand this?
You're running into the difference between an identifier and a value. num += 1 is creating a new int object and assigning the num identifier to point to it. It does not change the int object the identifier is already pointing to. (For small values the int objects are cached but that's an implementation detail)
You can see the difference with an operation that does mutate the object in the below code:
def y():
l = []
try:
raise Exception
except Exception:
print("except")
l.append(1)
return l
finally:
print("finally")
l.append(2)
print(y())
# except
# finally
# [1, 2]
The finally is executed (this is clearly defined in the documentation), but as you return an immutable object, the modification is unseen as your returned name is now part of a different scope.
This would work as you expect with a mutable object, for instance a list:
def main():
lst = [0]
try:
raise Exception('This is the error message.')
except Exception:
lst[0] += 1
return lst
finally:
lst[0] += 1
a = main()
print(a)
Output: [2]
The finally block does execute before returning the value, but the return value has already been computed.
return num evaluates num, getting 1, then the finally block starts. The finally block increments num, but the return value has already been evaluated, so it doesn't matter. At the end, the 1 computed earlier is returned.
It's because you are returning in except block
Using return word will end code execution in this function.
To avoid it you could write like this:
def main():
num = 0
try:
raise Exception('This is the error message.')
except Exception:
num += 1
finally:
num += 1
return num
a = main()
print(a)

unction return values only working once in python

The code below runs and prints out the values returned from the two functions test_value(address) and checkRepeatCount(address) when i try to add the two value returned i None as the result. Anyway to fix this?
def check(address):
## define point variables
pass_invalid_char = test_value(address)
pass_repeat_count = checkRepeatCount(address)
if pass_invalid_char == False:
return print("Invalid character")
else:
pass
total = pass_invalid_char+pass_repeat_count
print(total)
check("hello")
Function 1 test_value
def test_value(value):
return print(30)
Function 2 checkRepeatCount
def checkRepeatCount(value):
return print(20)
Thats how im returning the function values
In both functions was returning a print statement with value
def test_value(value):
return print(30)
I was supposed just to return the number on its own like so
def test_value(value):
return 30

Python automatically adds singlequotes around float value in List [duplicate]

This question already has answers here:
Possible to enforce type hints?
(2 answers)
Closed 12 months ago.
I struggle with Python(3.9.7) automatically adding singlequotes around a float-value, which leads to a failing assertion.
My goal is to use pytest to assert an expected output of a parsing-function. The parser takes a json-object (shortened code sample below) and returns a list of Signal objects. I copied the print-out from logging.debug(f"{signal_list}") inside the parsing function, and assigned it to expected_result in my test-function:
#Arrange
message = json.dumps({"data":{"name":"battery_volt","alias":"Volt","unit":"volt","value":12.0,"time":1644587969}})
expected_result = [Signal(id=None, name='battery_volt', description=None, value=12.0, type=None, unit='volt', time='2022-02-11T13:59:29')]
print(expected_result)
p = Parser(message)
#Act
result = p.parse_message()
#Assert
assert result == expected_result
Irritatingly, pytest -vv throws an AssertionError:
E Full diff:
E - [Signal(id=None, name='battery_volt', description=None, value='12.0', type=None, unit='volt', time='2022-02-11T13:59:29')]
E ? - -
E + [Signal(id=None, name='battery_volt', description=None, value=12.0, type=None, unit='volt', time='2022-02-11T13:59:29')]
The upper line seems to be the value of expected_result, because print(expected_result)
also adds the singlequotes around the 12.0
I assume the copied output from logging.debug(f"{signal_list}") isn't the same as the real value of result. I tried typecasting expected_result as list, str()-converting both result and expected_result inside the test, but expected_result always has '12.0' and result has 12.0.
I desperatly need a hint how to do this kind of assertion the correct way.
EDIT:
Here is the parsing function:
def parse_message(self):
message = json.loads(self.message)
#logging.debug(f"message is: {message}")
message_data = message.get('data', {})
parsed_data = []
try:
device = message_data.get('device', None)
if device is not None:
vehicle = self.parse_vehicle(device)
parsed_data.append(vehicle)
else:
logging.error("'device' was None!")
except Exception as e:
logging.error(e)
signals = []
try:
data = message_data.get('data', None)
if data is not None:
signals = self.parse_signals(data)
gps = message_data.get('gps', None)
if gps is not None:
gps_signal = self.parse_gps(gps)
signals.append(gps_signal)
parsed_data.append(signals)
except Exception as e:
logging.error(e)
return parsed_data
if __name__ == "__main__":
setup_logging()
message = json.dumps({"consumerid":redacted,"data":{"device":{"time":1644587969,"imei":"redacted","man_id":redacted,"car_id":999,"vin":"redacted"},"data":[{"name":"battery_volt","alias":"Volt","unit":"volt","value":12.0,"time":1644587969}],"gps":{"lat":51.437515,"lon":6.9281199,"dir":252,"alt":88,"sat":19,"time":1644587969}},"event":"redacted","itemid":redacted,"itemtype":1,"senderip":"redacted"})
p = Parser(message)
signal_list = p.parse_message()
logging.debug(f"{signal_list}")
Please note that the passed json-objects are more complex than the code-sample in the original post.
class Signal(BaseModel):
id: int = None
name: str = None
description: str = None
value: str = None
type: str = None
unit: str = None
time: str = None
EDIT2 - Assignment of Signal.value happens here:
def parse_signals(self, data):
signals = []
#logging.debug(f"data is : {data}")
for data_object in data:
signal = Signal()
try:
signal.name = data_object.get('name', None)
#get dtc-count value as seperate return-element, needed by the controller to handle dtc-codes
if signal.name == 'dtc_count':
self.dtc_count == data_object.get('value', None)
signal.description = data_object.get('description', None)
signal.value = data_object.get('value', None)
signal.time = datetime.datetime.utcfromtimestamp(data_object.get('time', None)).isoformat()
signal.unit = data_object.get('unit', None)
if signal.unit == "dtc":
signal.type = "1"
if signal.name is not None:
signals.append(signal)
#logging.debug(signal.__dict__)
except Exception as e:
logging.error(f"While parsing signal {data_object}, the following error occured: {e}")
return signals
When parse_message is called as __name__ == "main":, the testcode beneath outputs value=12.0
Ok, turns out python type hints don't enforce like I expected:
When parse_message is called by way of __name__ == "main":, the testcode beneath outputs value=12.0 Apparently, despite the type-hint :str for Signal.value, 12.0 was assigned as float. When I tried to sys.stdout.write(signal_list), I got a TypeError.
I now simply str()-convert in parse_signals() like this
signal.value = str(data_object.get('value', None))
resulting in having my value in single quotes consistently.

Access an iterator in 'for' loop, before the loop

I am trying to access an iterator 'obj' before the 'for' loop like this
class MyClass:
CLASS_CONST1 = 'some_rand_const'
def __init__(self, type, age):
self.type = type
self.age = age
var1 = 5
age = 7
# # I tried to individually add the following lines here, none of them work
# obj = MyClass
# obj = MyClass()
condition = obj.type == MyClass.CLASS_CONST1
if var1 > 10:
condition = obj.type == MyClass.CLASS_CONST1 and obj.age == age
list_of_objects = [MyClass('rand1', 'rand2'), MyClass('rand1', 'rand2'), MyClass('rand1', 'rand2')]
for obj in list_of_objects:
if condition:
# do some stuff
pass
The issue is that it is accessed before it is defined (it gets defined in the for loop). And I dont want introduce the condition lines inside the 'for' loop because the lines would be executed in every iteration, and there is no need for that.
The idea is that all this goes into a function and 'var1' and 'age' are arguments of the function.
obj = MyClass just assigns the class object (not instance) to another variable. obj = MyClass() will throw an error because you haven't provided values for type and age which are required in __init__. Did you try obj = MyClass(var1, age) ? You did it later for list_of_objects.
Anyway, you've tried to create condition as a variable that is supposed to apply itself during the iteration. That's not how Python works. It's given a static value when it's evaluated the one time. To have it apply to all objects, have condition as a function which either take the object or the two variables type and var as parameters and then return the result of the check:
var1 = 5
age = 7
def condition(obj):
# will return the True/False result of the check below
return obj.type == MyClass.CLASS_CONST1 and obj.age == age
for obj in list_of_objects:
if condition(obj): # call the function with that object
# do some stuff
pass
From your code, it's unclear what you wanted in condition. Maybe it was this?
var1 = 5 # or put these inside `condition` so they are local
age = 7 # to condition and not globals.
# Or pass them in as parameters and modify `condition` to accept
# the additional params
def condition(obj):
result = obj.type == MyClass.CLASS_CONST1
if result and var1 > 10:
# don't re-check `obj.type == MyClass.CLASS_CONST1`
result = obj.age == age
return result
You declare condition as a simple boolean variable, while its value has to depend on the current values of obj. You could use a bunch of functions and assign condition to the relevant one, or as you conditions are simple, you could use lambdas:
condition = obj.type == MyClass.CLASS_CONST1
if var1 > 10:
condition = lambda obj: obj.type == MyClass.CLASS_CONST1 and obj.age == age
else:
condition = lambda obj: obj.type == MyClass.CLASS_CONST1
and then use it as a variable function:
for obj in list_of_objects:
if condition(obj):
# do some stuff
pass

Python - how to handle outcome variables that are conditional set correctly

Consider the following:
def funcA():
some process = dynamicVar
if dynamicVar == 1:
return dynamicVar
else:
print "no dynamicVar"
def main():
outcome = funcA()
If the 'some process' part results in a 1, the var dynamicVar is passed back as outcome to the main func. If dynamicVar is anything but 1, the routine fails as no arguments are being return.
I could wrap the outcome as a list:
def funcA():
outcomeList = []
some process = dynamicVar
if dynamicVar == 1:
outcomeList.append(dynamicVar)
return outcomeList
else:
print "no dynamicVar"
return outcomeList
def main():
outcome = funcA()
if outcome != []:
do something using dynamicVar
else:
do something else!
or maybe as a dictionary item. Each of the 2 solutions I can think of involve another set of processing in the main / requesting func.
Is this the 'correct' way to handle this eventuality? or is there a better way?
What is the proper way of dealing with this. I was particularly thinking about trying to catch try: / except: errors, so in that example the uses are reversed, so something along the lines of:
def funcA():
some process = dynamicVar
if dynamicVar == 1:
return
else:
outcome = "no dynamicVar"
return outcome
def main():
try:
funcA()
except:
outcome = funcA.dynamicVar
In Python, all function that do not return a value will implicitly return None. So you can just check if outcome is not None in main().
I believe when you write a function, it's return value should be clear and expected. You should return what you say you will return. That being said, you can use None as a meaningful return value to indicate that the operation failed or produced no results:
def doSomething():
"""
doSomething will return a string value
If there is no value available, None will be returned
"""
if check_something():
return "a string"
# this is being explicit. If you did not do this,
# None would still be returned. But it is nice
# to be verbose so it reads properly with intent.
return None
Or you can make sure to always return a default of the same type:
def doSomething():
"""
doSomething will return a string value
If there is no value available, and empty string
will be returned
"""
if check_something():
return "a string"
return ""
This handles the case with a bunch of complex conditional tests that eventually just fall through:
def doSomething():
if foo:
if bar:
if biz:
return "value"
return ""

Categories