简体   繁体   中英

How can I use decorator for functions those have variety parameters?

Look only at the first two lines of these.

if not check_abs(_abs_dir):
    return False

These are the functions (just look at the parameters and the first two lines, the content of the functions are irrelevant for this question).

def check_is_file(_abs_dir:str):
    if not check_abs(_abs_dir):
        return False

    return os.path.isfile(
        norm_case_norm_path(_abs_dir)
    )

def create_file_or_folder(_name:str, _abs_dir:str, _is_file:bool):
    if not check_abs(_abs_dir):
        return False

    abs_dir = join(_abs_dir, _name)
    create = False

    if check_existence(abs_dir):
        if _is_file and not check_is_file(_abs_dir):
            create = True
        if not _is_file and not check_is_folder(_abs_dir):
            create = True
    else:
        create = True

    if create:
        if _is_file:
            open(abs_dir, "a").close()
        elif not _is_file:
            os.makedirs(abs_dir)
        return create

    return create

How can I make these codes

if not check_abs(_abs_dir):
    return False

into decorators for check_is_file() and create_file_or_folder() ? Both functions have different parameters and positions.

The problem is you don't know which argument to check since it's in a different place in each function you want to decorate. However you can do what you want by using the inspect module to examine the each function's signature, check to make sure it has an argument with the designated name, and if so, map the formal parameter names to the actual argument values passed to the function each time it's called.

Having that information makes it possible to determine the value of the argument associated with target parameter and process it as needed—which in the your code and in the example below is to call the check_abs() function with the value of the _abs_dir argument that passed and check the function's return value.

(Note: Modified to support multiple named parameters.)

from functools import wraps
import inspect

def check_abs(_abs_dir):
    print('check_abs({!r}) called'.format(_abs_dir))
    return True

def check_abs_param(*params):
    def decorator(function):
        sig = inspect.signature(function)
        if any(map(lambda param: param not in sig.parameters, params)):
            raise NameError('One or more expected parameter names missing from '
                            'declaration "{}{}:".'.format(function.__name__, sig))
        @wraps(function)
        def wrapped(*args, **kwargs):
            bound = sig.bind(*args, **kwargs)  # Map parameter names to argument values.
            for param in params:
                arg_value = bound.arguments[param]  # Get argument value.
                if not check_abs(arg_value):
                    return False if sig.return_annotation == bool else None

            return function(*args, **kwargs)
        return wrapped
    return decorator

# tests
try:
    @check_abs_param('_abs_dir')
    def check_is_bogus(somearg):  # param name does not match decorator
        print('check_is_bogus() called\n')
except NameError as exc:
    print(exc)
    print('NameError exception raised from "check_is_bogus(somearg):" declaration, as '
          'expected.\n')
else:
    print('Error: Expected exception NOT raised from "check_is_bogus(somearg):" '
          'declaration.\n')

@check_abs_param('_abs_dir')
def check_is_file(_abs_dir:str):
    print('check_is_file() called\n')

@check_abs_param('_abs_dir')
def create_file_or_folder(_name:str, _abs_dir:str, _is_file:bool):
    print('create_file_or_folder() called\n')

@check_abs_param('source_dir_abs', 'dest_dir_abs')
def copy_file_or_folder(source_dir_abs:str, dest_dir_abs:str) -> bool:
    print('copy_file_or_folder() called\n')

#check_is_bogus('first_dir')  # can't call, definition failed
check_is_file('second_dir')
create_file_or_folder('name', 'third_dir', 'False')
copy_file_or_folder('source_dir', 'dest_dir')

Output:

One or more expected parameter names missing from declaration "check_is_bogus(somearg):".
NameError exception raised from "check_is_bogus(somearg):" declaration, as expected.

check_abs('second_dir') called
check_is_file() called

check_abs('third_dir') called
create_file_or_folder() called

check_abs('source_dir') called
check_abs('dest_dir') called
copy_file_or_folder() called

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