Python 3.8:如何移除__init__.py实现无侵入式专业版功能扩展
Great question! Let's work through a clean solution that meets all your requirements: sharing a single codebase, enabling pro features just by adding files, no overwrites/deletions, and zero pro-specific code in the standard edition.
Core Idea
We'll use Python's auto-import mechanism (via .pth files) and dynamic function wrapping to inject pro features without touching standard edition code. Here's how to implement it step by step:
1. Standard Edition Setup (No Pro Code Anywhere)
First, adjust your standard edition to have clean, pro-free code:
Directory Structure:
fooble.py foo/ __init__.py # Standard-only, no pro references test_decorate.py
foo/__init__.py (Standard Edition):
This file only imports the standard function—no pro logic at all:
# Pure standard edition code: import core function from .test_decorate import do_something
foo/test_decorate.py (Unchanged):
def do_something(name): print(f"STANDARD do something {name}")
fooble.py (Unchanged):
from foo import do_something do_something('xxx')
2. Pro Edition: Only Add These Files
You won't modify any standard edition files. Just add two new files for the pro version:
File 1: foo/test_decorate_pro.py
This module wraps the standard function dynamically:
import wrapt import foo @wrapt.decorator def pro_do_something(wrapped, instance, args, kwargs): print(f"PRO do something args[0]", args, kwargs) return wrapped(*args, **kwargs) # Replace the standard function with the wrapped pro version foo.do_something = pro_do_something(foo.do_something)
File 2: foo-pro-autoimport.pth
This tells Python to automatically load the pro module on startup. Place this file in your Python site-packages directory (or use your packaging tool like setuptools to install it automatically):
import foo.test_decorate_pro
3. How It Works
- Standard Only: When only the standard edition is installed,
foo.do_somethingis the original standard function—no pro code runs, no extra files needed. - Pro Enabled: When you add the pro files, Python reads the
.pthfile on startup and auto-importsfoo.test_decorate_pro. This module dynamically replacesfoo.do_somethingwith the wrapped pro version. Any code that importsdo_something(likefooble.py) will automatically use the pro feature without changes.
Alternative: No Standard __init__.py
If you really want to remove the standard edition's foo/__init__.py, adjust things to use a namespace package:
Standard Edition Structure:
fooble.py foo/ test_decorate.py # No __init__.py here
Update fooble.py:
from foo.test_decorate import do_something do_something('xxx')
Pro Files:
Keep the same foo/test_decorate_pro.py (modified slightly to target the module directly):
import wrapt import sys from foo.test_decorate import do_something as original_do_something @wrapt.decorator def pro_do_something(wrapped, instance, args, kwargs): print(f"PRO do something args[0]", args, kwargs) return wrapped(*args, **kwargs) # Replace the function in the standard module sys.modules['foo.test_decorate'].do_something = pro_do_something(original_do_something)
Add the same .pth file to auto-import this module.
This setup still keeps pro code completely separate, requires only adding files, and avoids any overwrites.
内容的提问来源于stack exchange,提问作者Duke Dougal

