简体   繁体   中英

Python decorator to find key exist, and pass it as argument

I was wondering if I could use a decorator to see if an input to a function is:

  1. A dictionary, if not dictionary then run the method
  2. If it is dictionary, extract specified keys from it, and pass it to method

example:

@get_values(['username', 'password'])
    def log_me_in(username, password)
       # login logic
       if username == password:
           return True
       return False

ways to call it:

log_me_in(username = 10, password = 10)
>>>> True
log_me_in(10, 10)
>>>> True
log_me_in({'username': 10, 'password': 10})
>>>> True
log_me_in({'username': 10, 'password': 10, 'something': 10})
>>>> True
log_me_in({'username': 10, 'something': 10})
>>>> EXCEPTION
log_me_in({})
>>>> EXCEPTION
log_me_in([])
>>>> EXCEPTION

If you want to pass the values as positional arguments (according to the positions in the params list passed to the decorator):

def getvalues(params):
    getter = itemgetter(*params)
    def deco(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if len(args) == 1 and not kwargs and isinstance(args[0], Mapping):
                return func(*getter(args[0]))
            else:
                return func(*args, **kwargs)
        return wrapper
    return deco

If you want to pass them as keyword arguments instead:

def getvalues(params):
    def deco(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if len(args) == 1 and not kwargs and isinstance(args[0], Mapping):
                return func(**{key: value for key, value in args[0].items() 
                               if key in params})
            else:
                return func(*args, **kwargs)
        return wrapper
    return deco

I had to invent my own rules to fill in all of the gaps in the specification, but I think they're somewhat reasonable.

Also, as I mentioned in a comment, I think it would be friendlier if getvalues took its the parameter names as separate arguments instead of a list. To fix that, just change the first line to def getvalues(*params): .

It is possible, yes. But is it necessary? You could get a similar result by passing in keyword arguments with ** :

>>> log_me_in(**{'username': 10, 'password': 10})
True

The one difference is that passing something would not be allowed. Python errors out on unrecognized keyword arguments. See:

>>> log_me_in(username=10, password=10)
True
>>> log_me_in(10, 10)
True
>>> log_me_in(**{'username': 10, 'password': 10})
True
>>> log_me_in(**{'username': 10, 'password': 10, 'something': 10})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: log_me_in() got an unexpected keyword argument 'something'
>>> log_me_in(**{'username': 10, 'something': 10})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: log_me_in() got an unexpected keyword argument 'something'
>>> log_me_in(**{})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: log_me_in() missing 2 required positional arguments: 'username' and 'password'
>>> log_me_in(*[])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: log_me_in() missing 2 required positional arguments: 'username' and 'password'

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