it-swarm.com.de

Import aus der eingebauten Bibliothek, wenn ein Modul mit demselben Namen existiert

Situation: - In meinem project_folder gibt es ein Modul namens calendar - Ich möchte die integrierte Calendar-Klasse aus den Python Libraries verwenden - Wenn ich aus dem Kalender importiere, beschwert es sich, weil es versucht von meinem Modul laden.

Ich habe ein paar Mal gesucht und kann anscheinend keine Lösung für mein Problem finden.

Irgendwelche Ideen, ohne mein Modul umbenennen zu müssen?

105
twig

Das zu lösen ist eigentlich ziemlich einfach, aber die Implementierung wird immer ein bisschen zerbrechlich sein, da es von den Interna des python import-Mechanismus abhängt und diese in zukünftigen Versionen geändert werden können.

(Der folgende Code zeigt, wie sowohl lokale als auch nicht-lokale Module geladen werden und wie sie koexistieren können.)

def import_non_local(name, custom_name=None):
    import imp, sys

    custom_name = custom_name or name

    f, pathname, desc = imp.find_module(name, sys.path[1:])
    module = imp.load_module(custom_name, f, pathname, desc)
    f.close()

    return module

# Import non-local module, use a custom name to differentiate it from local
# This name is only used internally for identifying the module. We decide
# the name in the local scope by assigning it to the variable calendar.
calendar = import_non_local('calendar','std_calendar')

# import local module normally, as calendar_local
import calendar as calendar_local

print calendar.Calendar
print calendar_local

Die beste Lösung ist, wenn möglich, zu vermeiden, dass Sie Ihre Module mit demselben Namen wie die Standardbibliothek oder die Namen der integrierten Module benennen.

35
Boaz Yaniv

Es ist nicht erforderlich, den Namen Ihres Moduls zu ändern. Sie können stattdessen absolute_import verwenden, um das Importverhalten zu ändern. Zum Beispiel mit stem/socket.py importiere ich das Socket-Modul wie folgt:

from __future__ import absolute_import
import socket

Dies funktioniert nur mit Python 2.5 und höher; Aktivierungsverhalten ist die Standardeinstellung in Python 3.0 und höher. Pylint wird sich über den Code beschweren, aber er ist vollkommen gültig .

133
Damian

Die einzige Möglichkeit, dieses Problem zu lösen, besteht darin, die internen Importmaschinen selbst zu übernehmen. Das ist nicht einfach und voller Gefahren. Sie sollten das gralförmige Leuchtfeuer unbedingt vermeiden, da die Gefahr zu gefährlich ist.

Benennen Sie stattdessen Ihr Modul um.

Wenn Sie lernen möchten, wie Sie die internen Importmaschinen hijacken, finden Sie hier Informationen dazu:

Es gibt manchmal gute Gründe, in diese Gefahr zu geraten. Der Grund, den Sie angeben, gehört nicht dazu. Benennen Sie Ihr Modul um.

Wenn Sie den gefährlichen Weg einschlagen, besteht ein Problem darin, dass beim Laden eines Moduls ein 'offizieller Name' angegeben wird, sodass Python den Inhalt dieses Moduls nie wieder analysieren muss. Eine Zuordnung des 'offiziellen Namens' eines Moduls zum Modulobjekt selbst finden Sie in _sys.modules_.

Dies bedeutet, dass, wenn Sie an einem Ort _import calendar_ sind, jedes Modul, das importiert wird, als Modul mit dem offiziellen Namen calendar betrachtet wird und alle anderen Versuche, _import calendar_ irgendwo anders, einschließlich in anderer Code, der Teil der Hauptbibliothek Python ist, erhält diesen Kalender.

Möglicherweise können Sie einen Kundenimporteur mit dem imputil-Modul in Python 2.x entwerfen, das dazu führte, dass Module, die von bestimmten Pfaden geladen wurden, die Module, die sie importierten, in einem anderen Format als _sys.modules_ zuerst oder so ähnlich. Aber das ist eine extrem haarige Sache, die in Python 3.x sowieso nicht funktioniert.

Es gibt eine extrem hässliche und schreckliche Sache, die Sie tun können, ohne den Importmechanismus einzuschalten. Dies ist etwas, was Sie wahrscheinlich nicht tun sollten, aber es wird wahrscheinlich funktionieren. Es verwandelt Ihr calendar - Modul in eine Mischung aus dem Systemkalendermodul und Ihrem Kalendermodul. Vielen Dank an Boaz Yaniv für das Skelett der von mir verwendeten Funktion . Fügen Sie dies am Anfang Ihrer _calendar.py_ -Datei ein:

_import sys

def copy_in_standard_module_symbols(name, local_module):
    import imp

    for i in range(0, 100):
        random_name = 'random_name_%d' % (i,)
        if random_name not in sys.modules:
            break
        else:
            random_name = None
    if random_name is None:
        raise RuntimeError("Couldn't manufacture an unused module name.")
    f, pathname, desc = imp.find_module(name, sys.path[1:])
    module = imp.load_module(random_name, f, pathname, desc)
    f.close()
    del sys.modules[random_name]
    for key in module.__dict__:
        if not hasattr(local_module, key):
            setattr(local_module, key, getattr(module, key))

copy_in_standard_module_symbols('calendar', sys.modules[copy_in_standard_module_symbols.__module__])
_
14
Omnifarious

Ich möchte meine Version anbieten, die eine Kombination aus der Lösung von Boaz Yaniv und Omnifarious ist. Es wird die Systemversion eines Moduls importiert, mit zwei Hauptunterschieden zu den vorherigen Antworten:

  • Unterstützt die 'Punkt'-Notation, z. package.module
  • Ist ein Drop-In-Ersatz für die Importanweisung für Systemmodule, dh Sie müssen nur diese eine Zeile ersetzen, und wenn das Modul bereits angerufen wird, funktioniert es wie es ist

Stellen Sie dies an einen zugänglichen Ort, damit Sie es aufrufen können (ich habe meins in meiner __init__.py-Datei):

class SysModule(object):
    pass

def import_non_local(name, local_module=None, path=None, full_name=None, accessor=SysModule()):
    import imp, sys, os

    path = path or sys.path[1:]
    if isinstance(path, basestring):
        path = [path]

    if '.' in name:
        package_name = name.split('.')[0]
        f, pathname, desc = imp.find_module(package_name, path)
        if pathname not in __path__:
            __path__.insert(0, pathname)
        imp.load_module(package_name, f, pathname, desc)
        v = import_non_local('.'.join(name.split('.')[1:]), None, pathname, name, SysModule())
        setattr(accessor, package_name, v)
        if local_module:
            for key in accessor.__dict__.keys():
                setattr(local_module, key, getattr(accessor, key))
        return accessor
    try:
        f, pathname, desc = imp.find_module(name, path)
        if pathname not in __path__:
            __path__.insert(0, pathname)
        module = imp.load_module(name, f, pathname, desc)
        setattr(accessor, name, module)
        if local_module:
            for key in accessor.__dict__.keys():
                setattr(local_module, key, getattr(accessor, key))
            return module
        return accessor
    finally:
        try:
            if f:
                f.close()
        except:
            pass

Beispiel

Ich wollte mysql.connection importieren, hatte aber bereits ein lokales Paket namens mysql (die offiziellen mysql-Dienstprogramme). Um den Connector aus dem System-MySQL-Paket zu erhalten, habe ich Folgendes ersetzt:

import mysql.connector

Mit diesem:

import sys
from mysql.utilities import import_non_local         # where I put the above function (mysql/utilities/__init__.py)
import_non_local('mysql.connector', sys.modules[__name__])

Ergebnis

# This unmodified line further down in the file now works just fine because mysql.connector has actually become part of the namespace
self.db_conn = mysql.connector.connect(**parameters)
1
casey