Ошибки компоновщика могут произойти, если заголовочный файл и связанная с ним общая библиотека (файл .lib) не синхронизируются. Позволь мне объяснить.
Как работают линкеры? Линкер соответствует объявлению функции (объявленному в заголовке) с его определением (в общей библиотеке) путем сравнения их подписи. Вы можете получить ошибку компоновщика, если компоновщик не найдет определение функции, которое идеально подходит.
Возможно ли получить ошибку компоновщика, даже если объявление и определение, похоже, совпадают? Да! Они могут выглядеть одинаково в исходном коде, но это действительно зависит от того, что видит компилятор. По сути, вы можете столкнуться с такой ситуацией:
// header1.h
typedef int Number;
void foo(Number);
// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically
Обратите внимание, что хотя обе декларации функций выглядят одинаково в исходном коде, но они действительно различаются в зависимости от компилятора.
Вы можете спросить, как это получается в такой ситуации? Включите пути, конечно! Если при компиляции разделяемой библиотеки путь include приводит к header1.h
, и вы в конечном итоге используете header2.h
в своей собственной программе, вы оставите царапины на своем заголовке, задаваясь вопросом, что произошло (каламбур).
Пример того, как это может произойти в реальном мире, объясняется ниже.
У меня есть два проекта: graphics.lib
и main.exe
. Оба проекта зависят от common_math.h
. Предположим, что библиотека экспортирует следующую функцию:
// graphics.lib
#include "common_math.h"
void draw(vec3 p) { ... } // vec3 comes from common_math.h
И затем вы идете вперед и включаете библиотеку в свой собственный проект.
// main.exe
#include "other/common_math.h"
#include "graphics.h"
int main() {
draw(...);
}
Boom! Вы получаете ошибку компоновщика, и вы понятия не имеете, почему она терпит неудачу. Причина в том, что общая библиотека использует разные версии одного и того же include common_math.h
(я сделал это очевидным здесь в этом примере, включив другой путь, но это может быть не всегда так очевидно. Возможно, путь include отличается в настройки компилятора).
Обратите внимание, что в этом примере компоновщик сказал бы вам, что не смог найти draw()
, когда на самом деле вы знаете, что он явно экспортируется библиотекой. Вы могли часами царапать себе голову, думая, что пошло не так. Дело в том, что компоновщик видит другую подпись, потому что типы параметров немного отличаются. В этом примере vec3
является другим типом в обоих проектах в отношении компилятора. Это может произойти из-за того, что они происходят из двух немного разных файлов include (возможно, включенные файлы поступают из двух разных версий библиотеки).
DUMPBIN - ваш друг, если вы используете Visual Studio. Я уверен, что другие компиляторы имеют другие подобные инструменты.
Процесс выглядит следующим образом:
[1] По проекту я имею в виду набор исходных файлов, которые связаны друг с другом для создания либо библиотеки, либо исполняемого файла .
РЕДАКТИРОВАТЬ 1: Переписать первый раздел, который будет легче понять. Пожалуйста, прокомментируйте ниже, чтобы сообщить мне, нужно ли что-то еще исправлять. Спасибо!
Для использования Python 3.5+:
import importlib.util
spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py")
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
foo.MyClass()
Для Python 3.3 и 3.4 используйте:
from importlib.machinery import SourceFileLoader
foo = SourceFileLoader("module.name", "/path/to/file.py").load_module()
foo.MyClass()
(Хотя это было устарело в Python 3.4.)
Использование Python 2:
import imp
foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Существуют эквивалентные функции удобства для скомпилированных файлов Python и DLL.
См. также. http://bugs.python.org/issue21436 .
довольно простой способ: предположим, что вы хотите импортировать файл с относительным путем ../../MyLibs/pyfunc.py
libPath = '../../MyLibs'
import sys
if not libPath in sys.path: sys.path.append(libPath)
import pyfunc as pf
Но если вы сделаете это без охраны, вы, наконец, сможете получить очень длинный путь
Простое решение с использованием importlib
вместо пакета imp
(проверено на Python 2.7, хотя оно должно работать и для Python 3):
import importlib
dirname, basename = os.path.split(pyfilepath) # pyfilepath: '/my/path/mymodule.py'
sys.path.append(dirname) # only directories should be added to PYTHONPATH
module_name = os.path.splitext(basename)[0] # '/my/path/mymodule.py' --> 'mymodule'
module = importlib.import_module(module_name) # name space of defined module (otherwise we would literally look for "module_name")
Теперь вы можете напрямую использовать пространство имен импортируемого модуля, например:
a = module.myvar
b = module.myfunc(a)
Преимущество этого решения в том, что нам даже не нужно знать фактическое имя модуля, который мы хотели бы импортировать, чтобы использовать его в нашем коде. Это полезно, например. в случае, если путь модуля является настраиваемым аргументом.
def import_file(full_path_to_module):
try:
import os
module_dir, module_file = os.path.split(full_path_to_module)
module_name, module_ext = os.path.splitext(module_file)
save_cwd = os.getcwd()
os.chdir(module_dir)
module_obj = __import__(module_name)
module_obj.__file__ = full_path_to_module
globals()[module_name] = module_obj
os.chdir(save_cwd)
except:
raise ImportError
import_file('/home/somebody/somemodule.py')
Преимущество добавления пути к sys.path (с использованием imp) заключается в том, что он упрощает работу при импорте нескольких модулей из одного пакета. Например:
import sys
# the mock-0.3.1 dir contains testcase.py, testutils.py & mock.py
sys.path.append('/foo/bar/mock-0.3.1')
from testcase import TestCase
from testutils import RunTests
from mock import Mock, sentinel, patch
Добавление этого в список ответов, поскольку я не мог найти ничего, что сработало. Это позволит импортировать скомпилированные (pyd) модули python в 3.4:
import sys
import importlib.machinery
def load_module(name, filename):
# If the Loader finds the module name in this list it will use
# module_name.__file__ instead so we need to delete it here
if name in sys.modules:
del sys.modules[name]
loader = importlib.machinery.ExtensionFileLoader(name, filename)
module = loader.load_module()
locals()[name] = module
globals()[name] = module
load_module('something', r'C:\Path\To\something.pyd')
something.do_something()
Я придумал слегка измененную версию замечательного ответа @ SebastianRittau (для Python> 3.4, я думаю), который позволит вам загружать файл с любым расширением в качестве модуля, используя spec_from_loader
вместо spec_from_file_location
:
from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader
spec = spec_from_loader("module.name", SourceFileLoader("module.name", "/path/to/file.py"))
mod = module_from_spec(spec)
spec.loader.exec_module(mod)
Преимущество кодирования пути в явном SourceFileLoader
состоит в том, что механизм не будет пытаться выяснить тип файла из расширения. Это означает, что вы можете загружать что-то вроде файла .txt
с помощью этого метода, но вы не могли бы сделать это с помощью spec_from_file_location
без указания загрузчика, потому что .txt
не находится в importlib.machinery.SOURCE_SUFFIXES
.
Импортировать модули модулей во время выполнения (рецепт Python)
http://code.activestate.com/recipes/223972/
###################
## #
## classloader.py #
## #
###################
import sys, types
def _get_mod(modulePath):
try:
aMod = sys.modules[modulePath]
if not isinstance(aMod, types.ModuleType):
raise KeyError
except KeyError:
# The last [''] is very important!
aMod = __import__(modulePath, globals(), locals(), [''])
sys.modules[modulePath] = aMod
return aMod
def _get_func(fullFuncName):
"""Retrieve a function object from a full dotted-package name."""
# Parse out the path, module, and function
lastDot = fullFuncName.rfind(u".")
funcName = fullFuncName[lastDot + 1:]
modPath = fullFuncName[:lastDot]
aMod = _get_mod(modPath)
aFunc = getattr(aMod, funcName)
# Assert that the function is a *callable* attribute.
assert callable(aFunc), u"%s is not callable." % fullFuncName
# Return a reference to the function itself,
# not the results of the function.
return aFunc
def _get_class(fullClassName, parentClass=None):
"""Load a module and retrieve a class (NOT an instance).
If the parentClass is supplied, className must be of parentClass
or a subclass of parentClass (or None is returned).
"""
aClass = _get_func(fullClassName)
# Assert that the class is a subclass of parentClass.
if parentClass is not None:
if not issubclass(aClass, parentClass):
raise TypeError(u"%s is not a subclass of %s" %
(fullClassName, parentClass))
# Return a reference to the class itself, not an instantiated object.
return aClass
######################
## Usage ##
######################
class StorageManager: pass
class StorageManagerMySQL(StorageManager): pass
def storage_object(aFullClassName, allOptions={}):
aStoreClass = _get_class(aFullClassName, StorageManager)
return aStoreClass(allOptions)
Вы имеете в виду загрузку или импорт?
Вы можете управлять списком sys.path, указывая путь к вашему модулю, а затем импортировать свой модуль. Например, с учетом модуля:
/foo/bar.py
Вы можете сделать:
import sys
sys.path[0:0] = '/foo' # puts the /foo directory at the start of your path
import bar
Это должно работать
path = os.path.join('./path/to/folder/with/py/files', '*.py')
for infile in glob.glob(path):
basename = os.path.basename(infile)
basename_without_extension = basename[:-3]
# http://docs.python.org/library/imp.html?highlight=imp#module-imp
imp.load_source(basename_without_extension, infile)
Вы можете использовать модуль pkgutil
(в частности метод walk_packages
), чтобы получить список пакетов в текущем каталоге. Оттуда тривиально использовать механизм importlib
для импорта необходимых вам модулей:
import pkgutil
import importlib
packages = pkgutil.walk_packages(path='.')
for importer, name, is_package in packages:
mod = importlib.import_module(name)
# do whatever you want with module now, it's been imported!
В Linux, добавляя символическую ссылку в каталог, в котором находится ваш скрипт python, [.g0]
ie:
ln -s /absolute/path/to/module/module.py /absolute/path/to/script/module.py
python создаст /absolute/path/to/script/module.pyc
и обновит его, если вы меняете содержимое /absolute/path/to/module/module.py
, а затем включаете следующее в mypythonscript.py
from module import *
Похоже, вы не хотите специально импортировать файл конфигурации (в котором есть много побочных эффектов и дополнительных осложнений), вы просто хотите запустить его и иметь доступ к результирующему пространству имен. Стандартная библиотека предоставляет API специально для этого в виде runpy.run_path :
from runpy import run_path
settings = run_path("/path/to/file.py")
Этот интерфейс доступен в Python 2.7 и Python 3.2 +
Вы также можете сделать что-то подобное и добавить каталог, в котором находится файл конфигурации, в путь загрузки Python, а затем просто сделать обычный импорт, предполагая, что вы знаете имя файла заранее, в этом случае " config ".
Беспокойно, но он работает.
configfile = '~/config.py'
import os
import sys
sys.path.append(os.path.dirname(os.path.expanduser(configfile)))
import config
Чтобы импортировать модуль из заданного имени файла, вы можете временно расширить путь и восстановить путь к системе в конце блока :
filename = "directory/module.py"
directory, module_name = os.path.split(filename)
module_name = os.path.splitext(module_name)[0]
path = list(sys.path)
sys.path.insert(0, directory)
try:
module = __import__(module_name)
finally:
sys.path[:] = path # restore
Эта область Python 3.4 кажется чрезвычайно извилистой, чтобы ее понять! Однако с небольшим взломом с использованием кода от Криса Кэллоуэя в качестве запуска мне удалось что-то сделать. Вот основная функция.
def import_module_from_file(full_path_to_module):
"""
Import a module given the full path/filename of the .py file
Python 3.4
"""
module = None
try:
# Get module name and path from full path
module_dir, module_file = os.path.split(full_path_to_module)
module_name, module_ext = os.path.splitext(module_file)
# Get module "spec" from filename
spec = importlib.util.spec_from_file_location(module_name,full_path_to_module)
module = spec.loader.load_module()
except Exception as ec:
# Simple error printing
# Insert "sophisticated" stuff here
print(ec)
finally:
return module
Это означает использование устаревших модулей из Python 3.4. Я не претендую на то, чтобы понять, почему, но, похоже, работает изнутри программы. Я нашел решение Криса работало в командной строке, но не внутри программы.
Если ваш модуль верхнего уровня не является файлом, а упакован как каталог с __init__.py, то принятое решение почти работает, но не совсем. В Python 3.5+ необходим следующий код (обратите внимание на добавленную строку, начинающуюся с «sys.modules»):
MODULE_PATH = "/path/to/your/module/__init__.py"
MODULE_NAME = "mymodule"
spec = importlib.util.spec_from_file_location("mymodule", MODULE_PATH)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
Без этой строки, когда exec_module выполняется, он пытается связать относительный импорт в ваш верхний уровень __init__.py до имени модуля верхнего уровня - в этом случае «mymodule». Но «mymodule» еще не загружен, поэтому вы получите сообщение об ошибке «SystemError: Parent module« mymodule »не загружен, не может выполнять относительный импорт». Поэтому вам нужно привязать имя до его загрузки. Причиной этого является фундаментальный инвариант относительной системы импорта: «Инвариантный холдинг состоит в том, что если у вас есть sys.modules ['spam'] и sys.modules ['spam.foo'] (как и после указанного импорта ), последний должен появиться как атрибут foo предыдущего «, как обсуждалось здесь .
Вот код, который работает во всех версиях Python: от 2.7-3.5 и, возможно, даже от других.
config_file = "/tmp/config.py"
with open(config_file) as f:
code = compile(f.read(), config_file, 'exec')
exec(code, globals(), locals())
Я его протестировал. Это может быть уродливым, но до сих пор это единственное, что работает во всех версиях.
Я сделал пакет, который использует imp
для вас. Я называю это import_file
, и именно так оно используется:
>>>from import_file import import_file
>>>mylib = import_file('c:\\mylib.py')
>>>another = import_file('relative_subdir/another.py')
Вы можете получить его по адресу:
http://pypi.python.org/pypi / import_file
или в
Я не говорю, что это лучше, но для полноты я хотел предложить функцию exec
, доступную как в python 2, так и 3. exec
позволяет вам для выполнения произвольного кода в глобальной области или во внутренней области, предоставляемой в качестве словаря.
Например, если у вас есть модуль, хранящийся в "/path/to/module
"с функцией foo()
, вы может запустить его, выполнив следующие действия:
module = dict()
with open("/path/to/module") as f:
exec(f.read(), module)
module['foo']()
Это делает несколько более явным, что вы загружаете код динамически и предоставляете вам дополнительную мощность, например, возможность предоставления настраиваемых встроенных функций.
И если вам нужен доступ через атрибуты, а не ключи, вы можете создать пользовательский класс dict для глобальных переменных, который обеспечивает такой доступ, например:
class MyModuleClass(dict):
def __getattr__(self, name):
return self.__getitem__(name)
Лучший способ, я думаю, из официальной документации ( 29.1. imp - доступ к внутренним импортам ):
import imp
import sys
def __import__(name, globals=None, locals=None, fromlist=None):
# Fast path: see if the module has already been imported.
try:
return sys.modules[name]
except KeyError:
pass
# If any of the following calls raises an exception,
# there's a problem we can't handle -- let the caller handle it.
fp, pathname, description = imp.find_module(name)
try:
return imp.load_module(name, fp, pathname, description)
finally:
# Since we may exit via an exception, close fp explicitly.
if fp:
fp.close()