Python动态引入模块(importlib/ __import__)

本文介绍了如何使用Python的importlib模块动态引入对象,包括自定义函数和扩展文件。此外,还讨论了__import__函数的实现,并提到了在接口自动化和变量替换场景中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • importlib

importlib允许程序员创建他们自定义的对象。imp的模块在Python3.4中被去掉。

用途扩展自定义函数

#!/usr/bin/env python
#-*- coding = utf-8 -*-

import importlib
import imp
import sys

# Syntax sugar.
_ver = sys.version_info

#: Python 2.x?
is_py2 = (_ver[0] == 2)

#: Python 3.x?
is_py3 = (_ver[0] == 3)

def get_imported_module_from_file(file_path):
    """ import module from python file path and return imported module
    """
    if is_py3:
        imported_module = importlib.machinery.SourceFileLoader(
            'module_name', file_path).load_module()
    elif is_py2:
        imported_module = imp.load_source('module_name', file_path)
    else:
        raise RuntimeError("Neither Python 3 nor Python 2.")

    return imported_module

if __name__ == "__main__":
    imported_module = get_imported_module_from_file("buildin.py")
    # 动态引入对象的属性
    print(dir(imported_module))

    # todo 调用引入文件的方法
    phone = imported_module.getPhone()
    print(f"手机号 : {phone}")

引入扩展文件buildin.py

#!/usr/bin/env python
#-*- coding = utf-8 -*-

def setPhone(phone):
    return phone

def getPhone():
    return "15365878954"

结果

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'getPhone', 'setPhone']

手机号 : 15365878954
  • _import_ 实现
    imported_module = __import__("buildin")
    print(dir(imported_module))
    phone = imported_module.getPhone()
    print(f"手机号 : {phone}")

结果

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'getPhone', 'setPhone']
手机号 : 15365878954

备注:
引入参数的getPhone属性直接写死的,但实际更多的是动态的,可以用到Python的属性方法getattr。

    # 动态引用模块2
    imported_module = __import__("buildin")
    print(dir(imported_module))

    test = getattr(imported_module, "getPhone")
    phone = test()
    print(f"手机号 : {phone}")

接口自动化,特殊处理

  1. 变量替换
  2. 自定义函数引入
# -*- coding:utf-8 _*-
"""
@author:hszhang
@file: getIPByProject.py
@time: 2019/05/15 21:40
@contact: hszhang@.....
@software: PyCharm

              ┏┓      ┏┓
            ┏┛┻━━━┛┻┓
            ┃      ☃      ┃
            ┃  ┳┛  ┗┳  ┃
            ┃      ┻      ┃
            ┗━┓      ┏━┛
                ┃      ┗━━━┓
                ┃  神兽保佑    ┣┓
                ┃ 永无BUG!   ┏┛
                ┗┓┓┏━┳┓┏┛
                  ┃┫┫  ┃┫┫
                  ┗┻┛  ┗┻┛
"""
import logging
import re
import ast

logger = logging.getLogger("evalDictVariables")

################# 参数变量替换(start) #################
def getDependVariable(data):
    # todo 目前写死了
    return "test"

def extractVariables(data):
    """ extract all variable names from content, which is in format $variable
    @param (str) data
    @return (list) variable name list

    e.g. $variable => ["variable"]
         /blog/$postid => ["postid"]
         /$var1/$var2 => ["var1", "var2"]
         abc => []
    """
    variable_regexp = r"\$([\w_]+)"
    try:
        return re.findall(variable_regexp, data)
    except TypeError:
        return []

def evalStringVariables(data):
    """
    $variable变量替换
    """
    if isinstance(data, str):
        for variable_name in extractVariables(data):
            variable_value = getDependVariable(data)
            if "${}".format(variable_value) == data:
                # data is a variable
                data = variable_value
            else:
                # data contains one or many variables
                data = data.replace(
                    "${}".format(variable_name),
                    str(variable_value), 1
                )
    elif isinstance(data, list):
        for element in data:
            evalStringVariables(element)
    elif isinstance(data, dict):
        for key, value in data.items():
            evalStringVariables(key)
            evalStringVariables(value)
    # else:  # todo 可以省略,直接执行下面的return
    #     return data

    return data
################# 参数变量替换(over)  #################


################# 自定义方法(start) #################
def parseStringValue(str_value):
    """ parse string to number if possible
    e.g. "123" => 123
         "12.2" => 12.3
         "abc" => "abc"
         "$var" => "$var"
    """
    try:
        return ast.literal_eval(str_value)
    except ValueError:
        return str_value
    except SyntaxError:
        # e.g. $var, ${func}
        return str_value

def parseFunction(content):
    """ parse function name and args from string content.
    @param (str) content
    @return (dict) function name and args

    e.g. func() => {'func_name': 'func', 'args': [], 'kwargs': {}}
         func(5) => {'func_name': 'func', 'args': [5], 'kwargs': {}}
         func(1, 2) => {'func_name': 'func', 'args': [1, 2], 'kwargs': {}}
         func(a=1, b=2) => {'func_name': 'func', 'args': [], 'kwargs': {'a': 1, 'b': 2}}
         func(1, 2, a=3, b=4) => {'func_name': 'func', 'args': [1, 2], 'kwargs': {'a':3, 'b':4}}
    """
    function_meta = {
        "args": [],
        "kwargs": {}
    }
    function_regexp_compile = re.compile(r"^([\w_]+)\(([\$\w\.\-_ =,]*)\)$")
    matched = function_regexp_compile.match(content)
    function_meta["func_name"] = matched.group(1)

    args_str = matched.group(2).replace(" ", "")
    if args_str == "":
        return function_meta

    args_list = args_str.split(',')
    for arg in args_list:
        if '=' in arg:
            key, value = arg.split('=')
            function_meta["kwargs"][key] = parseStringValue(value)
        else:
            function_meta["args"].append(parseStringValue(arg))

    return function_meta

def extractFunctions(data):
    """ extract all functions from string content, which are in format ${fun()}
    @param (str) content
    @return (list) functions list

    e.g. @func(5)@ => ["func(5)"]
         ${func(a=1, b=2)} => ["func(a=1, b=2)"]
         /api/1000?_t=${get_timestamp()} => ["get_timestamp()"]
         /api/${add(1, 2)} => ["add(1, 2)"]
         "/api/${add(1, 2)}?_t=${get_timestamp()}" => ["add(1, 2)", "get_timestamp()"]
    """
    function_regexp = r"\@([\w_]+\([\w\.\-_ =,]*\))\@"
    try:
        return re.findall(function_regexp, data)
    except TypeError:
        return []

def callFunc(data):
    """
    import module
    :param data:
    :return:
    """
    imported_module = __import__("buildIn")
    print(dir(imported_module))
    if isinstance(data, str):
        for func_content in extractFunctions(data):
            function_meta = parseFunction(func_content)

            func_name = getattr(imported_module, function_meta['func_name'])
            args = function_meta.get('args', [])

            kwargs = function_meta.get('kwargs', {})

            return func_name(*args, **kwargs)

    return data



################# 自定义方法(over)  #################

def evalString(data):
    if data[:1] == '@' and data[-1:] == '@':  # 这个就是匹配入参传的函数的模式,如 {'a':'@methon(参数1,参数2)@'}}
        # todo 引入参数
        pass
        eval_data = callFunc(data)
        # logger.debug(f'入参{key}传入函数为:' + value[1:value.find('(')] + f',对应值为:{diction[key]}')
        return eval_data
    elif data[:6].upper() == 'SELECT':  # 这个就是入参传的sql语句模式,如{'a':'select value from a'}
        # todo sql 查询
        pass
        # diction[key] = self.valueTrans(self.db.query(v))
        # logger.debug(f'入参{key}传入sql为:' + value + f',对应值为:{diction[key]}')
    elif '$' in data:  # 这个就是入参引用依赖值的模式,引用前面接口的某个key返回值,如{'a':'$key'} 或 {'a':'$key$'}
        # todo 变量
        eval_data = evalStringVariables(data)
        logger.debug(f'处理前{data},处理后:{eval_data}')
        return eval_data

    return data

# todo 请求参数转义
def evalDict(diction, default=None):
    """
    处理参数
    :param diction:
    :param default:
    :return:
    """
    for key, value in diction.items():
        if isinstance(key,str) and isinstance(value, str):
            diction[key] = evalString(value)
        elif isinstance(value, dict):  # value为字典值,继续往下递归
            evalDict(value, default)
        elif isinstance(value, list):  # value为列表值,继续往下递归
            for data in value:
                if isinstance(data, dict):
                    evalDict(data, default)
    return diction



if __name__ == "__main__":
    # todo 自测1
    data = {"body" : "$postid"}
    print(evalDict(data))
    # todo 自测2
    data1 = {"body": "$postid1/$postid2"}
    print(evalDict(data1))
    # todo 自测3
    data2 = {"body": {"data" : "$postid1/$postid2"}}
    print(evalDict(data2))

    # todo 自测4
    data3 = {"body": {"data": "@getPhone()@"}}
    print(evalDict(data3))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值