0

I have allowed = ['someoption','someother'],

,and default = 'default_value'

v comes from user input.

then when I do :

v = v if v.lower() in allowed else default, it works as expected,

v is taking values only if user specified option listed in allowed list, else is set to default_value.

However, I wanted to isolate validation into function by defining it like:

def validate(value, rules, default)
    value if value.lower() in rules else default

, but now when I do validate(v, allowed, default), and try to enter value not in allowed list, instead of getting v hold the default_value, I get whatever I entered.

So I expected for v to be passed as reference and being changed, but that did not happened. What should I do, to get the expected result?

2 Answers 2

2

Simple solution

First, small correction to your validate function:

def validate(value, rules, default)
    return value if value.lower() in rules else default

and then simply assign the value to your result

v = "something"
allowed = ["someoption", "someother"]
default = "default_value"
v = validate(v, allowed, default)

Just keep it simple.

Regarding "pass by reference" - Python does not have this concept. If you pass into a function immutable type of value (like a string), it goes in the way which could be called "by value". If you pass in mutable object (like list), it goes in the way which could be called "by reference". There is no way, you would instruct Python to change how this is done.

ugly_validate with changing result "in place"

If you would really insist on modifying the result being passed in, this (ugly) way could work:

>>> def ugly_validate(value_lst, rules, default):
...     if value_lst[0].lower() not in rules:
...         value_lst[0] = default
...
>>> v = "something"
>>> allowed = ["someoption", "someother"]
>>> default = "default_value"
>>> v_lst = [v]
>>> ugly_validate(v_lst, allowed, default)
>>> v_lst
['default_value']
>>> v = "someoption"
>>> v_lst = [v]
>>> ugly_validate(v_lst, allowed, default)
>>> v_lst
['someoption']

Alternative solution using "identity dictionary" of allowed values

>>> allowed_dct = {key: key for key in allowed}
>>> allowed_dct
{'someoption': 'someoption', 'someother': 'someother'}
>>> res = allowed_dct.get(v, default)
>>> res
'someoption'
>>> v = "unknown"
>>> res = allowed_dct.get(v, default)
>>> res
'default_value'

Using dedicated class

>>> class AllowedValues():
...     def __init__(self, allowed_values, default):
...         self.allowed_values = allowed_values
...         self.default = default
...     def get(self, value):
...         if value.lower() in self.allowed_values:
...             return value
...         else:
...             return self.default
...
>>> allowed
['someoption', 'someother']
>>> judge = AllowedValues(allowed, default)
>>> v = "someoption"
>>> v = judge.get(v)
>>> v
'someoption'
>>> v = "unknonw"
>>> v = judge.get(v)
>>> v
'default_value'
Sign up to request clarification or add additional context in comments.

4 Comments

I know I can do like that with assignment, but that does not answer my question, I would like to change what I pass to function by reference.
@branquito If you are going to pass in a string, there is no way to modify it from within your function. You would have to pass in a list with given value and it could modify that content.
ok I understand that, but how would you go about logic, on having some validation function which you would use whenever you have case like this one. If I have to do it by assignment, then it's easier to have directly in code what I already had. v = v if v.lower() in allowed else default
Basically v = v if v.lower() in allowed else default does same kind of assignment as v = validate(v, allowed, default) in the answer. If you only need it once, no need for function but if you have it in multiple places, you might want to use a function so if you need to change it later, you only have to touch one place.
1

In Python everything is passed by asssigment. In your case, the value name is assigned the same object that v is pointing to but as soon as you change value, since what it is pointing to (a string) is immutable, it actually atarts poonting to a new string object and no longer to what v Is pointing to.

The best way to do what you want to do is for the validate function to return the new value (if it's not in rules.

Alternatively (but not recommended), if you make v point to a list containing the user-entered string, then since lists are mutable, the change to the list inside the validate function will also be "visible" outside of that function.

2 Comments

so there is no way to make validation function, which would be used in general, so that I do not repeat myself for each similar case on 5 user inputs?
Not the way you want to do it. You can't change a string because it's immutable. Unless you put it in a list and then change the value within that list. But that's awfully ugly. I think retuning the value is the best option. Then you should change the name of function as well because one would expect a validate function to return True/False based on whether the value is valid or not; but not change it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.