简体   繁体   中英

pass value by reference into python function

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?

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'

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 list s are mutable, the change to the list inside the validate function will also be "visible" outside of that function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM