जब एक ही नाम वाला मॉड्यूल मौजूद हो, तो बिल्टिन लाइब्रेरी से आयात करना


121

स्थिति: - मेरे प्रोजेक्ट_फॉल्डर में एक मॉड्यूल है जिसे कैलेंडर कहा जाता है - मैं पायथन पुस्तकालयों से अंतर्निहित कैलेंडर वर्ग का उपयोग करना चाहूंगा - जब मैं कैलेंडर आयात कैलेंडर का उपयोग करता हूं तो यह शिकायत करता है क्योंकि यह मेरे मॉड्यूल से लोड करने की कोशिश कर रहा है।

मैंने कुछ खोज की हैं और मैं अपनी समस्या का हल नहीं ढूंढ सकता।

मेरे मॉड्यूल का नाम लिए बिना कोई भी विचार?


24
बिलिन मॉड्यूल को छिपाने के लिए मॉड्यूल का नाम नहीं देना एक सर्वोत्तम अभ्यास है।
the_drow

3
समाधान "एक अलग नाम चुनें" है। आपका नाम नहीं बदलने का दृष्टिकोण एक बुरा विचार है। आप अपने मॉड्यूल का नाम क्यों नहीं बदल सकते? नाम बदलने में क्या दिक्कत है?
एस.लॉट

वास्तव में। यह इसलिए है क्योंकि इस सवाल का कोई अच्छा जवाब नहीं है कि स्डडलिब मॉड्यूल को छायांकित करना बहुत दृढ़ता से हतोत्साहित करता है।
ncoghlan

मैं एक ही मॉड्यूल नाम का उपयोग करने से बचता था क्योंकि समाधान इसके लायक होने की तुलना में अधिक परेशानी वाला था। धन्यवाद!
टहनी

9
@the_drow यह सलाह पैमाने, शुद्ध और सरल नहीं है। PEP328 इसे आसानी से स्वीकार करता है।
कोनराड रुडोल्फ

जवाबों:


4

स्वीकृत समाधान में अब अपदस्थ दृष्टिकोण शामिल है।

इम्पोर्टलिब डॉक्यूमेंट यहाँ एक उपयुक्त पथ का एक अच्छा उदाहरण देता है जो सीधे अजगर के लिए फाइल पथ से एक मॉड्यूल लोड करने के लिए = = 1:

import importlib.util
import sys

# For illustrative purposes.
import tokenize
file_path = tokenize.__file__  # returns "/path/to/tokenize.py"
module_name = tokenize.__name__  # returns "tokenize"

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)

तो, आप किसी भी .py फ़ाइल को एक पथ से लोड कर सकते हैं और मॉड्यूल नाम सेट कर सकते हैं जो आप चाहते हैं। तो बस समायोजित करेंmodule_name आयात लिए जो भी कस्टम नाम आप मॉड्यूल के लिए चाहते हैं, उसे ।

किसी एकल फ़ाइल के बजाय पैकेज लोड file_pathकरने के लिए, पैकेज के रूट का पथ होना चाहिए__init__.py


एक आकर्षण की तरह काम करता है ... यह एक पुस्तकालय विकसित करते समय परीक्षण करने के लिए उपयोग किया जाता है, ताकि मेरे परीक्षण हमेशा विकासशील संस्करण का उपयोग कर रहे थे और प्रकाशित (और स्थापित) नहीं। विंडोज़ 10 में मैं इस तरह मेरी मॉड्यूल के लिए पथ लिखना पड़ा: file_path=r"C:\Users\My User\My Path\Module File.py"। तब मुझे module_nameजारी किए गए मॉड्यूल की तरह ही बुलाया गया था ताकि मेरे पास पूरी कामकाजी स्क्रिप्ट थी, जो इस स्निपेट को छीन लिया गया, दूसरे पीसी पर
कॉड का

141

अपने मॉड्यूल का नाम बदलना आवश्यक नहीं है। बल्कि, आयात व्यवहार को बदलने के लिए आप निरपेक्ष_समूह का उपयोग कर सकते हैं। उदाहरण के लिए स्टेम / सॉकेटहोम के साथ मैं सॉकेट मॉड्यूल को निम्नानुसार आयात करता हूं:

from __future__ import absolute_import
import socket

यह केवल पायथन 2.5 और इसके बाद के संस्करण के साथ काम करता है; यह व्यवहार को सक्षम कर रहा है जो पायथन 3.0 और उच्चतर में डिफ़ॉल्ट है। Pylint कोड के बारे में शिकायत करेगा लेकिन यह पूरी तरह से मान्य है।


4
यह मुझे सही उत्तर लगता है। अधिक के लिए 2.5 चैंज या PEP328 देखें ।
बजे पीटर एन

5
यह सही उपाय है। दुर्भाग्य से, यह तब काम नहीं करता है जब पैकेज के भीतर से कोड लॉन्च किया जाता है क्योंकि तब पैकेज को इस तरह से मान्यता नहीं दी जाती है, और स्थानीय पथ का उपयोग किया जाता है PYTHONPATHएक अन्य प्रश्न दिखाता है कि इसे कैसे हल किया जाए।
कोनराड रुडोल्फ

5
इसका उपाय है। मैंने पायथन 2.7.6 की जाँच की और यह आवश्यक है, यह अभी भी डिफ़ॉल्ट नहीं है।
हवोक

3
वास्तव में: पहला पायथन संस्करण जहां यह व्यवहार डिफ़ॉल्ट है, docs.python.org/2/library/__future__.html के
मिथ्या

1
फिर अपने मुख्य मॉड्यूल को एक के रूप में नाम न दें जो एक अंतर्निहित मॉड्यूल के साथ टकराता है।
एंटिटी हापाला

38

वास्तव में, इसे हल करना आसान है, लेकिन कार्यान्वयन हमेशा थोड़ा नाजुक होगा, क्योंकि यह अजगर आयात तंत्र के इंटर्नल्स पर निर्भर करता है और वे भविष्य के संस्करणों में परिवर्तन के अधीन हैं।

(निम्न कोड दिखाता है कि स्थानीय और गैर-स्थानीय दोनों मॉड्यूलों को कैसे लोड किया जाए और वे कैसे सह-अस्तित्व में हों)

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

सबसे अच्छा समाधान, यदि संभव हो तो, मानक-पुस्तकालय या अंतर्निहित मॉड्यूल नामों के समान नाम के साथ अपने मॉड्यूल के नामकरण से बचना है।


यह sys.modulesस्थानीय मॉड्यूल को लोड करने के लिए और बाद के प्रयासों के साथ कैसे बातचीत करेगा ?
सर्वव्यापी

@Omnifarious: यह अपने नाम के साथ मॉड्यूल को sys.modules में जोड़ेगा, जो स्थानीय मॉड्यूल को लोड करने से रोकेगा। उससे बचने के लिए आप हमेशा एक कस्टम नाम का उपयोग कर सकते हैं।
बोअज यानिव

@ बोअज़ यानिव: आपको स्थानीय कैलेंडर के लिए एक कस्टम नाम का उपयोग करना चाहिए, न कि मानक एक का। अन्य पायथन मॉड्यूल मानक एक आयात करने की कोशिश कर सकते हैं। और अगर आप ऐसा करते हैं, तो आप इसके साथ जो हासिल करते हैं, वह मूल रूप से फ़ाइल का नाम बदले बिना स्थानीय मॉड्यूल का नाम बदलना है।
सर्वव्यापी

@Omnifarious: आप इसे किसी भी तरह से कर सकते हैं। कुछ अन्य कोड स्थानीय मॉड्यूल को लोड करने और बहुत ही त्रुटि प्राप्त करने का प्रयास कर सकते हैं। आपको एक समझौता करना होगा, और यह तय करना होगा कि किस मॉड्यूल का समर्थन करना है।
बोअज़ यानिव

2
उस Boaz के लिए धन्यवाद! यद्यपि आपका स्निपेट छोटा है (और दस्तावेज़), मुझे लगता है कि मॉड्यूल का नाम बदलना कुछ आसान है, जैसे कि कुछ हैक किए गए कोड हैं जो भविष्य में लोगों (या अपने आप) को भ्रमित कर सकते हैं।
टहनी

15

इस समस्या को हल करने का एकमात्र तरीका आंतरिक आयात मशीनरी को खुद ही अपहृत करना है। यह आसान नहीं है, और जोखिम से भरा है। आपको हर कीमत पर ग्रिल के आकार के बीकन से बचना चाहिए क्योंकि पेरिल बहुत खतरनाक है।

इसके बजाय अपने मॉड्यूल का नाम बदलें।

यदि आप सीखना चाहते हैं कि आंतरिक आयात मशीनरी को कैसे हाइजैक किया जाए, तो यहां आपको यह पता लगाना है कि यह कैसे करना है:

इस जोखिम में आने के लिए कभी-कभी अच्छे कारण होते हैं। आपके द्वारा दिया गया कारण उनमें से नहीं है। अपने मॉड्यूल का नाम बदलें।

यदि आप खतरनाक रास्ता अपनाते हैं, तो आपके सामने एक समस्या यह होगी कि जब आप किसी मॉड्यूल को लोड करते हैं तो यह एक 'आधिकारिक नाम' के साथ समाप्त होता है ताकि पायथन कभी भी उस मॉड्यूल की सामग्री को फिर से पार्स करने से बच सके। मॉड्यूल वस्तु के लिए एक मॉड्यूल के 'आधिकारिक नाम' की मैपिंग स्वयं में पाई जा सकती है sys.modules

इसका मतलब यह है कि यदि आप import calendarएक जगह पर हैं, तो जो भी मॉड्यूल आयात किया जाता है, उसे आधिकारिक नाम के साथ मॉड्यूल के रूप में माना जाएगा calendarऔर अन्य सभी प्रयासों को import calendarकहीं भी शामिल किया जाएगा, जिसमें अन्य कोड भी शामिल है जो मुख्य पायथन लाइब्रेरी का हिस्सा है, उस कैलेंडर को मिलेगा।

पायथन 2.x में इंप्यूटिल मॉड्यूल का उपयोग करके ग्राहक आयातक को डिजाइन करना संभव हो सकता है।sys.modules पहले या कुछ और की तरह । लेकिन यह एक बेहद बालों वाली चीज़ है, और यह पायथन 3.x में वैसे भी काम नहीं करेगा।

एक बहुत ही बदसूरत और भयानक चीज है जिसे आप कर सकते हैं जिसमें आयात तंत्र को शामिल करना शामिल नहीं है। यह कुछ ऐसा है जो आपको शायद नहीं करना चाहिए, लेकिन यह संभवतः काम करेगा। यह आपके calendarमॉड्यूल को सिस्टम कैलेंडर मॉड्यूल और आपके कैलेंडर मॉड्यूल के हाइब्रिड में बदल देता है । मेरे द्वारा उपयोग किए जाने वाले फ़ंक्शन के कंकाल के लिए Boaz Yaniv का धन्यवाद । इसे अपनी फ़ाइल की शुरुआत में रखें :calendar.py

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__])

imputil को पदावनत माना जाता है। आप छोटा सा भूत मॉड्यूल का उपयोग करना चाहिए ।
बोआज यानिव

जो पायथन 3 के साथ पूरी तरह से संगत है, वैसे। और यह नहीं कि बालों का उपयोग करने के लिए। लेकिन आपको हमेशा पता होना चाहिए कि कोड जो एक तरह से पथ के उपचार के अजगर पर निर्भर करता है या उस क्रम में मॉड्यूल देख रहा है, जो जल्द या बाद में टूट सकता है।
बोअज़ यानि

1
ठीक है, लेकिन इस तरह के एक पृथक मामले में (मॉड्यूल नाम टक्कर) आयात तंत्र को हुक करना एक ओवरकिल है। और चूंकि यह बालों और असंगत है, इसलिए इसे अकेले छोड़ दिया जाना बेहतर है।
बोअज यानिव

1
@jspacek नहीं, अब तक बहुत अच्छा है, लेकिन टकराव केवल PyDev के डिबगर का उपयोग करते समय होगा, नियमित उपयोग में नहीं। और सुनिश्चित करें कि आप नवीनतम कोड (github में URL) की जांच करते हैं, क्योंकि यह उपरोक्त उत्तर से थोड़ा बदल गया है
MestreLion

1
@jspacek: यह एक खेल है, एक पुस्तकालय नहीं है, इसलिए मेरे मामले में पिछड़ी संगतता बिल्कुल भी चिंता का विषय नहीं है। और नाम स्थान की टक्कर केवल PyDev IDE (जो Python के codestd मॉड्यूल का उपयोग करता है ) के माध्यम से चलने के दौरान होती है , जिसका अर्थ है कि केवल डेवलपर्स का कुछ अंश कभी भी इस "मर्जिंग हैक" के साथ कोई समस्या हो सकती है। उपयोगकर्ता बिल्कुल भी प्रभावित नहीं होंगे।
MestreLion

1

मैं अपने संस्करण की पेशकश करना चाहूंगा, जो बोअज़ यानिव और ओम्निफ़ारसियस के समाधान का एक संयोजन है। यह मॉड्यूल के सिस्टम संस्करण को आयात करेगा, जिसमें पिछले उत्तरों से दो मुख्य अंतर होंगे:

  • 'डॉट' संकेतन का समर्थन करता है, उदाहरण के लिए। package.module
  • सिस्टम मॉड्यूल पर आयात स्टेटमेंट के लिए एक ड्रॉप-इन प्रतिस्थापन है, जिसका अर्थ है कि आपको बस उस एक लाइन को बदलना होगा और यदि पहले से ही कॉल किए जा रहे हैं तो वे मॉड्यूल के रूप में काम करेंगे

इसे कहीं सुलभ रखें ताकि आप इसे कॉल कर सकें (मेरी __init__.py फ़ाइल में मेरा है):

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

उदाहरण

मैं mysql.connection आयात करना चाहता था, लेकिन मेरे पास पहले से ही mysql (आधिकारिक mysql उपयोगिताओं) नामक एक स्थानीय पैकेज था। तो सिस्टम mysql पैकेज से कनेक्टर प्राप्त करने के लिए, मैंने इसे प्रतिस्थापित किया:

import mysql.connector

इसके साथ:

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__])

परिणाम

# 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)

-2

आयात पथ बदलें:

import sys
save_path = sys.path[:]
sys.path.remove('')
import calendar
sys.path = save_path

यह काम नहीं करेगा क्योंकि ऐसा करने के बाद स्थानीय मॉड्यूल को आयात करने के लिए कोई रास्ता नहीं होगा और आयात मशीनरी के साथ खुद को फ़िडलिंग करना होगा।
सर्वव्यापी

@Omnifarious: यह एक अलग समस्या है, जिसे आप कैलेंडर आयात से * करने वाले तीसरे मॉड्यूल के साथ प्राप्त कर सकते हैं।

नहीं, यह शायद काम नहीं करेगा क्योंकि अजगर मॉड्यूल का नाम कैश करता है sys.modules, और यह फिर से उसी नाम के साथ मॉड्यूल आयात नहीं करेगा।
बोअज़ यानिव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.