第10.5节 使用__all__定义Python模块导入白名单

本文详细介绍了Python模块中的__all__机制,这是一种模块成员的白名单控制机制,用于指定哪些成员可以通过from模块名import*的方式导入。文章通过实例演示了__all__机制的工作原理,对比了不同导入方式下的成员访问情况。

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

一、 引言
《第10.4节 Python模块的弱封装机制》介绍了Python模块的的弱封装机制,除了使用弱封装机制来从一定程度上防止导入特定成员外,Python模块中还提供可另外一种类似白名单的机制来控制导入的成员,这个机制就是在模块中定义__all__变量,将__all__的值设置成一个列表,只有列表中的模块成员才能被导入。

二、 方法介绍
在模块内部定义一个模块内的全局变量__all__,其元素是每个需要允许导出的成员名字符串。
all = [‘成员名1’,…,‘成员名n’]

三、 案例
1、 我们定义一个imptest模块,包括三个成员变量和5个成员函数,内容如下:

#imptest.py
__all__=['f','_f1','var2','_var3']
var1,var2,_var3='imptest var1','imptest var2','imptest _var3'
_var2
def f():
    print("execute ftest function in imptest....")
def _f1():
    print("execute _f1(单下划线开头) function in imptest....")
def __f2():
    print("execute __f2(双下划线开头) function in imptest....")
def __f3__():
    print("execute __f3__(双下划线开头结尾) function in imptest....")
def f4():
    print("execute f4 function in imptest....")
    
print("Now in imptest module!")  

2、 使用“from 模块名 import *”导入imptest模块的成员并执行验证是否导入成功

>>> from imptest import *
Now in imptest module!
>>> var1
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    var1
NameError: name 'var1' is not defined
>>> var2
'imptest var2'
>>> _var3
'imptest _var3'
>>> f()
execute ftest function in imptest....
>>> _f1()
execute _f1(单下划线开头) function in imptest....
>>> __f2()
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    __f2()
NameError: name '__f2' is not defined
>>>

执行截图:
在这里插入图片描述
从上述执行情况来看,只有在__all__列表中的成员才能通过“from imptest import *”导入,带下划线的也会正常导入,没有在__all__列表中的成员计算无下划线开头也不能导入。
3、 直接使用“import 模块名”导入
源代码:

>>> import imptest
Now in imptest module!
>>> imptest.f()
execute ftest function in imptest....
>>> imptest._f1()
execute _f1(单下划线开头) function in imptest....
>>> imptest.__f2()
execute __f2(双下划线开头) function in imptest....
>>> imptest.__f3__()
execute __f3__(双下划线开头结尾) function in imptest....
>>> imptest.var1,imptest.var2,imptest._var3
('imptest var1', 'imptest var2', 'imptest _var3')
>>>

执行截屏:
在这里插入图片描述
从上述执行情况来看,使用“import 模块名”导入后,所有成员都可以正常访问,不受__all__列表的影响。

四、 总结
使用__all__定义模块访问白名单:

  1. 只对“from 模块名 import *”导入产生影响,对“import 模块名”或“from 模块名 import 成员名”不产生影响;
  2. 在__all__列表中的元素不论是否带下划线开头,“from 模块名 import *”都会导入,不受模块的缺省封装机制影响,可以说这是另一种方式的封装;
  3. 在模块定义__all__变量后,可以使用“模块.__all__”查看模块建议使用的模块成员。

__all__变量可以认为给模块定义了一个开放的公共接口。通常来说,只有__all__变量列出的模块属性,才是该模块建议外界使用的。因此,为一个大模块定义__all__ 变量,就可以给调用程序建议过滤不需要使用的变量、函数和类,只使用__all__定义的白名单属性。

前面章节介绍过 dir(模块名)可返回模块或类所包含的全部程序单元(包括变量、函数、类和方法等),但直接使用 dir() 函数默认会列出模块内所有的属性,包括以下划线开头的属性,如果模块定义了__all__ 变量,则建议调用者只关注__all__ 变量限定的属性。

是在app.py中进行修改,替换libinjection.so app.py: from flask import Flask, request, jsonify import ctypes import numpy as np from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing.sequence import pad_sequences import pickle import json from urllib.parse import unquote import html import sys import base64 import re from utils.makelog import log_detection import os import logging from logging.handlers import RotatingFileHandler os.environ['TF_KERAS'] = '1' os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 1=警告,2=错误,3=静默 os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0' # 关闭 oneDNN 提示 app = Flask(__name__) log_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'utils') os.makedirs(log_dir, exist_ok=True) # 配置文件日志处理器(10MB轮换,保留10个备份) file_handler = RotatingFileHandler( os.path.join(log_dir, 'app.log'), maxBytes=10*1024*1024, backupCount=10 ) file_handler.setFormatter(logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' )) # 设置日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL) app.logger.setLevel(logging.INFO) file_handler.setLevel(logging.INFO) app.logger.addHandler(file_handler) # --- 加载 libinjection --- try: libinjection = ctypes.CDLL('/usr/local/lib/libinjection.so', mode=ctypes.RTLD_GLOBAL) libinjection.libinjection_sqli.argtypes = [ ctypes.c_char_p, ctypes.c_size_t, ctypes.c_char_p, ctypes.c_size_t ] libinjection.libinjection_sqli.restype = ctypes.c_int app.logger.info("Libinjection 加载成功") print("Libinjection 加载成功(控制台输出)") except Exception as e: app.logger.error(f"Libinjection 加载失败: {str(e)}", exc_info=True) exit(1) # --- 加载深度学习模型和辅助对象 --- # try: # model = load_model('/usr/local/flasktest/model/model1.h5') # app.logger.info("模型 model1.h5 加载成功") # with open('/usr/local/flasktest/model/model1.pkl', 'rb') as f: # tokenizer, tfidf_vectorizer = pickle.load(f) # app.logger.info("模型辅助对象 model1.pkl 加载成功") # max_seq_length = int(np.load("/usr/local/flasktest/model/max_seq_length1.npy")) # app.logger.info(f"最大序列长度加载成功: {max_seq_length}") # with open('/usr/local/flasktest/model/best_threshold1.json', 'r') as f: # best_threshold = json.load(f)['threshold'] # app.logger.info(f"检测阈值加载成功: {best_threshold}") # except Exception as e: # app.logger.error(f"模型加载失败: {str(e)}", exc_info=True) # exit(1) # --- 解码辅助函数 --- def try_base64_decode(s): try: if len(s) % 4 != 0: return s decoded = base64.b64decode(s).decode('utf-8', errors='ignore') if all(32 <= ord(c) <= 126 or c in '\t\r\n' for c in decoded): return decoded return s except Exception: return s def deep_url_decode(s, max_depth=3): decoded = s for _ in range(max_depth): new_decoded = unquote(decoded) if new_decoded == decoded: break decoded = new_decoded return decoded # --- 提取 HTTP 请求中的潜在 SQL 内容 --- def extract_sql_candidates(data): candidates = [] def extract_strings(obj): EXCLUDED_KEYS = {'uri', 'path', 'security', 'PHPSESSID', 'session_id','Login', 'login', 'submit', 'Submit'} STATIC_RESOURCES = {'.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.ico', '.woff', '.woff2'} if isinstance(obj, dict): for key, value in obj.items(): if key in EXCLUDED_KEYS: continue # 检查值是否为静态资源(无需检测) if isinstance(value, str) and any(ext in value.lower() for ext in STATIC_RESOURCES): continue extract_strings(value) # 递归调用,仅传递值 elif isinstance(obj, list): for item in obj: extract_strings(item) elif isinstance(obj, str): text = obj # 多层 URL 解码 text = deep_url_decode(text) # HTML 实体解码 text = html.unescape(text) # Unicode 转义解码 try: text = text.encode().decode('unicode_escape') except Exception: pass # Base64 解码 text = try_base64_decode(text) if len(text) < 1000: candidates.append(text) extract_strings(data) return candidates # --- 检测策略 --- # def should_use_libinjection(text): # return text.isdigit() or ' ' not in text or len(text) > 600 # --- 检测逻辑 --- def detect_one(query): if re.match(r'^\/.*\.(php|html|js)$', query): return { "检测结果": "正常", "检测方式": "URI过滤", "可信度": 1.0 } result_buf = ctypes.create_string_buffer(8) is_libi_sqli = libinjection.libinjection_sqli(query.encode('utf-8'), len(query),result_buf,ctypes.sizeof(result_buf)) if is_libi_sqli: return { "检测结果": "存在SQL注入", "检测方式": "Libinjection", } else: return { "检测结果": "正常", "检测方式": "Libinjection", } # seq = tokenizer.texts_to_sequences([query]) # padded = pad_sequences(seq, maxlen=max_seq_length) # tfidf_vec = tfidf_vectorizer.transform([query]).toarray() # prob = model.predict([padded, tfidf_vec], verbose=0)[0][0] # if prob > best_threshold: # return { # "检测结果": "存在SQL注入", # "检测方式": "CNN-BiLSTM", # "可信度": float(prob), # } # else: # return { # "检测结果": "正常", # "检测方式": "CNN-BiLSTM", # "可信度":float(1 - prob), # } @app.route('/') def home(): return "SQL 注入检测系统已启动" @app.route('/detect', methods=['POST']) def detect(): app.logger.info(f"接收到请求: {request.json}") try: data = request.get_json() if not data: return jsonify({"error": "缺少 JSON 请求体"}), 400 ip = request.remote_addr candidates = extract_sql_candidates(data) results = [] for query in candidates: result = detect_one(query) log_detection(ip, query, result) results.append(result) return jsonify({"detections": results}) except Exception as e: return jsonify({"error": f"检测过程中发生错误: {str(e)}"}), 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)
最新发布
06-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LaoYuanPython

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值