我不确定你的setup.py 要求(我不知道你为什么需要那个),但至于运行时导入问题,我写了一个装饰器来做到这一点:
from __future__ import print_function
from importlib import import_module
from functools import wraps
import inspect
import sys
MAKE_NOISE = False
def external(f):
""" Decorator that looks for an external version of
the decorated function -- if one is found and
imported, it replaces the decorated function
in-place (and thus transparently, to would-be
users of the code). """
f.__external__ = 0 # Mark func as non-native
function_name = hasattr(f, 'func_name') and f.func_name or f.__name__
module_name = inspect.getmodule(f).__name__
# Always return the straight decoratee func,
# whenever something goes awry.
if not function_name or not module_name:
MAKE_NOISE and print("Bad function or module name (respectively, %s and %s)" % (
function_name, module_name), file=sys.stderr)
return f
# This function is `pylire.process.external()`.
# It is used to decorate functions in `pylire.process.*`,
# each of which possibly has a native (Cython) accelerated
# version waiting to be imported in `pylire.process.ext.*`
# … for example: if in `pylire/process/my_module.py` you did this:
#
# @external
# def my_function(*args, **kwargs):
# """ The slow, pure-Python implementation """
# pass
#
# … and you had a Cython version of `my_function()` set up
# in `pylire/process/ext/my_module.pyx` – you would get the fast
# function version, automatically at runtime, without changing code.
#
# TL,DR: you'll want to change the `pylire.process.ext` string (below)
# to match whatever your packages' structure looks like.
module_file_name = module_name.split('.')[-1]
module_name = "pylire.process.ext.%s" % module_file_name
# Import the 'ext' version of process
try:
module = import_module(module_name)
except ImportError:
MAKE_NOISE and print("Error importing module (%s)" % (
module_name,), file=sys.stderr)
return f
MAKE_NOISE and print("Using ext module: %s" % (
module_name,), file=sys.stderr)
# Get the external function with a name that
# matches that of the decoratee.
try:
ext_function = getattr(module, function_name)
except AttributeError:
# no matching function in the ext module
MAKE_NOISE and print("Ext function not found with name (%s)" % (
function_name,), file=sys.stderr)
return f
except TypeError:
# function_name was probably shit
MAKE_NOISE and print("Bad name given for ext_function lookup (%s)" % (
function_name,), file=sys.stderr)
return f
# Try to set telltale/convenience attributes
# on the new external function -- this doesn't
# always work, for more heavily encythoned
# and cdef'd function examples.
try:
setattr(ext_function, '__external__', 1)
setattr(ext_function, 'orig', f)
except AttributeError:
MAKE_NOISE and print("Bailing, failed setting ext_function attributes (%s)" % (
function_name,), file=sys.stderr)
return ext_function
return wraps(f)(ext_function)
... 这让您可以将函数装饰为 @external - 并且它们会在运行时自动替换为您提供的 Cython 优化版本。
如果您想将此想法扩展到替换整个 Cythonized 类,则可以直接在元类的 __new__ 方法中使用相同的逻辑(例如优化模块中的机会主义查找和替换)。