একই নামযুক্ত মডিউল উপস্থিত থাকলে বিল্টিন গ্রন্থাগার থেকে আমদানি করা


121

পরিস্থিতি: - আমার প্রজেক্ট_ফোল্ডারে ক্যালেন্ডার নামে একটি মডিউল রয়েছে - আমি পাইথন লাইব্রেরি থেকে অন্তর্নির্মিত ক্যালেন্ডার ক্লাসটি ব্যবহার করতে চাই - যখন আমি ক্যালেন্ডার আমদানি ক্যালেন্ডার থেকে ব্যবহার করি তখন এটি অভিযোগ করে কারণ এটি আমার মডিউলটি থেকে লোড করার চেষ্টা করছে।

আমি কয়েকটি অনুসন্ধান করেছি এবং আমার সমস্যার সমাধান খুঁজে পাওয়া যাবে বলে মনে হচ্ছে না।

আমার মডিউলটির নাম পরিবর্তন না করে কোনও ধারণা?


24
বিল্টিন মডিউলগুলি লুকানোর জন্য মডিউলগুলির নাম না রাখাই সেরা অনুশীলন।
the_drow

3
সমাধানটি হল "আলাদা নাম বাছাই করুন"। নাম পরিবর্তন না করার আপনার দৃষ্টিভঙ্গি একটি খারাপ ধারণা। আপনি কেন আপনার মডিউলটির নাম পরিবর্তন করতে পারবেন না? নতুন নামকরণে কী সমস্যা?
এস .লট

প্রকৃতপক্ষে. কারণ এই প্রশ্নের উত্তম উত্তর নেই যে স্ট্যাডলিব মডিউলগুলি ছায়া গোপন করে এতটাই নিরুৎসাহিত করা হয়।
ncoghlan

সমাধানগুলির তুলনায় এটির চেয়ে বেশি সমস্যা মনে হওয়ায় আমি একই মডিউলটির নামটি এড়িয়ে চললাম। ধন্যবাদ!
দ্বিগুণ

9
@ এই_ড্রো এই পরামর্শটি মাপকাঠি, খাঁটি এবং সহজ নয়। পিইপি 328 সহজেই এটি স্বীকার করে।
কনরাড রুডল্ফ

উত্তর:


4

গৃহীত সমাধানটিতে এখন-অবহিত পদ্ধতি রয়েছে।

Importlib ডকুমেন্টেশন এখানে আরো উপযুক্ত উপায় একটি ভাল উদাহরণ দেয় পাইথন> = 3.5 জন্য একটি ফাইল পাথ থেকে সরাসরি একটি মডিউল লোড করতে:

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


কবজির মতো কাজ করে ... একটি লাইব্রেরিটি বিকাশের সময় এটি পরীক্ষার জন্য ব্যবহৃত হয়, যাতে আমার পরীক্ষাগুলি সর্বদা বিকাশকারী সংস্করণ ব্যবহার করে এবং প্রকাশিত (এবং ইনস্টল করা) একটি নয়। Windows 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.০ এবং তার চেয়েও উচ্চতর ডিফল্ট। পাইলেট কোড সম্পর্কে অভিযোগ করবে তবে এটি পুরোপুরি বৈধ।


4
এটি আমার সঠিক উত্তর বলে মনে হচ্ছে। আরও 2.5 লিঙ্ক পরিবর্তন বা PEP328 দেখুন।
পিটার এনস

5
এটিই সঠিক সমাধান। দুর্ভাগ্যক্রমে, যখন প্যাকেজটির মধ্যে থেকে কোড চালু হয় তখন এটি কাজ করে না কারণ প্যাকেজটি এরূপ হিসাবে স্বীকৃত হয় না এবং স্থানীয় পথটি পূর্বনির্ধারিত হয় PYTHONPATHআরেকটি প্রশ্ন দেখায় যে কীভাবে এটি সমাধান করা যায়।
কনরাড রুডল্ফ

5
এই সমাধান। আমি পাইথন ২.7..6 যাচাই করেছিলাম এবং এটি প্রয়োজনীয়, এটি এখনও ডিফল্ট নয়।
হাভোক

3
প্রকৃতপক্ষে: প্রথম পাইথন সংস্করণ যেখানে এই আচরণ ডিফল্ট 3.0 ছিল অনুযায়ী 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স্থানীয় মডিউলটি লোড করার জন্য এবং পরবর্তী প্রচেষ্টাগুলির সাথে যোগাযোগ করবে ?
সর্বজনীন

@ ওমনিফেরিয়াস: এটি মডিউলটিকে sys.modules এ এর ​​নামের সাথে যুক্ত করবে, যা স্থানীয় মডিউলটি লোড করা রোধ করবে। এটি এড়াতে আপনি সর্বদা একটি কাস্টম নাম ব্যবহার করতে পারেন।
বোয়াজ ইয়ানিভ

@ বোয়াজ ইয়ানিভ: আপনার স্থানীয় ক্যালেন্ডারের জন্য একটি কাস্টম নাম ব্যবহার করা উচিত, মানক নয়। অন্যান্য পাইথন মডিউলগুলি মানকটি আমদানির চেষ্টা করতে পারে। এবং যদি আপনি এটি করেন তবে এটির মাধ্যমে আপনি যা অর্জন করেন তা হ'ল ফাইলটির নাম পরিবর্তন না করেই মূলত স্থানীয় মডিউলটির নামকরণ।
সর্বজ্ঞানী

@ সর্বজনীন: আপনি যে কোনও উপায়ে এটি করতে পারেন। কিছু অন্যান্য কোড স্থানীয় মডিউলটি লোড করার চেষ্টা করতে পারে এবং খুব একই ত্রুটি পেতে পারে। আপনাকে একটি আপস করতে হবে এবং কোন মডিউলটি সমর্থন করবে তা সিদ্ধান্ত নেওয়া আপনার পক্ষে।
বোয়াজ ইয়ানিভ

2
বোয়াজের জন্য ধন্যবাদ! যদিও আপনার স্নিপেটটি ছোট (এবং দস্তাবেজ) হ'ল, আমি মনে করি ভবিষ্যতে মানুষ (বা আমার) কে বিভ্রান্ত করতে পারে এমন কিছু হ্যাকি কোডের চেয়ে মডিউলটির নামকরণ করা সহজ।
twig

15

এই সমস্যাটি সমাধানের একমাত্র উপায় হ'ল অভ্যন্তরীণ আমদানি যন্ত্রপাতি নিজেই হাইজ্যাক করা। এটি সহজ নয়, এবং বিপদে পরিপূর্ণ। বিপদটি খুব বিপদজনক হওয়ার কারণে আপনার যেকোন মূল্যে গ্রিল আকারের বীকন এড়ানো উচিত।

পরিবর্তে আপনার মডিউলটির নতুন নাম দিন।

আপনি যদি অভ্যন্তরীণ আমদানি যন্ত্রপাতিটি হাইজ্যাক করতে শিখতে চান তবে আপনি কীভাবে এটি করবেন তা সন্ধানের জন্য এখানে যাবেন:

কখনও কখনও এই বিপদে পড়ার জন্য ভাল কারণ রয়েছে। আপনি যে কারণে দেন তা তাদের মধ্যে নেই। আপনার মডিউলটির নতুন নাম দিন।

আপনি যদি বিপদজনক পথটি গ্রহণ করেন তবে একটি সমস্যার মুখোমুখি হবেন যে আপনি যখন কোনও মডিউল লোড করবেন তখন এটি একটি 'অফিসিয়াল নাম' দিয়ে শেষ হয় যাতে পাইথন আবার কখনও সেই মডিউলটির বিষয়বস্তু বিশ্লেষণ না করে এড়াতে পারে। মডিউলটির 'অফিসিয়াল নাম' এর ম্যাপিং মডিউল অবজেক্টে নিজেই পাওয়া যাবে sys.modules

এর অর্থ হ'ল আপনি import calendarযদি এক জায়গায় থাকেন তবে যে কোনও মডিউল আমদানি করা হবে সেটিকে অফিসিয়াল নাম calendarএবং import calendarমূল পাইথন লাইব্রেরির অংশ হিসাবে অন্য কোড সহ অন্য কোথাও অন্য সমস্ত প্রচেষ্টা সহ মডিউল হিসাবে বিবেচনা করা হবে ।

পাইথন ২.x এর ইমপ্লিট মডিউলটি ব্যবহার করে কোনও গ্রাহক আমদানিকারককে ডিজাইন করা সম্ভব হতে পারে যা নির্দিষ্ট পাথ থেকে লোড করা মডিউলগুলির জন্য তারা যে মডিউলগুলি sys.modulesপ্রথম বা অন্য কিছুতে আমদানি করছিল তা সন্ধান করতে পারে। তবে এটি করা একটি অত্যন্ত লোমশ জিনিস এবং এটি পাইথন ৩.x তে কোনওভাবেই কাজ করবে না।

আপনি করতে পারেন এমন একটি অত্যন্ত কুৎসিত এবং ভয়ঙ্কর জিনিস রয়েছে যা আমদানি প্রক্রিয়াটিকে জড়িত করে না। এটি এমন কিছু যা আপনার সম্ভবত করা উচিত নয়, তবে এটি সম্ভবত কার্যকর হবে। এটি আপনার calendarমডিউলটিকে সিস্টেম ক্যালেন্ডার মডিউল এবং আপনার ক্যালেন্ডার মডিউলের হাইব্রিডে রূপান্তরিত করে। আমি যে ফাংশনটি ব্যবহার করি তার কঙ্কালের জন্য বোয়াজ ইয়ানিভকে ধন্যবাদ জানাই । আপনার ফাইলের শুরুতে এটি রাখুন :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__])

ইমপ্লিট অবচয় বিবেচনা করা হয়। আপনার ইমপ মডিউলটি ব্যবহার করা উচিত ।
বোয়াজ ইয়ানিভ

যা পাইথন 3 এর সাথে পুরোপুরি সামঞ্জস্যপূর্ণ। এবং মোটেও ব্যবহার করার মতো লোমশ নয়। তবে আপনার সর্বদা সচেতন হওয়া উচিত যে কোডটি অজগরকে এক উপায়ে পাথের চিকিত্সার উপর নির্ভর করে বা সেই ক্রমে মডিউলগুলি সন্ধান করে তাড়াতাড়ি বা পরে ভেঙে যেতে পারে।
বোয়াজ ইয়ানিভ 17'11

1
ঠিক আছে, তবে এই জাতীয় বিচ্ছিন্ন ক্ষেত্রে (মডিউল নাম সংঘর্ষ) আমদানি প্রক্রিয়া হুক করা একটি ওভারকিল। এবং যেহেতু এটি লোমশ এবং বেমানান, তাই এটি একা ভাল।
বোয়াজ ইয়ানিভ

1
@jspacek নাহ, এখন পর্যন্ত খুব ভাল, তবে সংঘর্ষ কেবলমাত্র পাইডেভের ডেববার ব্যবহার করার সময় ঘটবে, নিয়মিত ব্যবহারে নয়। এবং নিশ্চিত হয়ে নিন যে আপনি সর্বশেষ কোডটি (
গিথুবে

1
@ জেস্পেসেক: এটি একটি গেম, লাইব্রেরি নয়, তাই আমার ক্ষেত্রে পশ্চাদগম সামঞ্জস্যতা মোটেই উদ্বেগের বিষয় নয়। এবং পাইডেভ আইডিই (যা পাইথনের codeএসডিডি মডিউল ব্যবহার করে ) ব্যবহার করে নেমস্পেসের সংঘর্ষ কেবল তখনই ঘটে থাকে , যার অর্থ কেবল " ডেভেলপারদের একটি ভগ্নাংশই এই" মার্জিং হ্যাক "এর সাথে কোনও সমস্যা থাকতে পারে। ব্যবহারকারীরা কিছুতেই প্রভাবিত হবে না।
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

উদাহরণ

আমি মাইএসকিএলসিএনএকশনটি আমদানি করতে চেয়েছিলাম, তবে আমার কাছে ইতিমধ্যে মাইএসকিএল (অফিশিয়াল মাইএসকিএল ইউটিলিটিস) নামে একটি স্থানীয় প্যাকেজ ছিল। সুতরাং সিস্টেম মাইএসকিএল প্যাকেজ থেকে সংযোগকারীটি পেতে, আমি এটি প্রতিস্থাপন করেছি:

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

এটি কাজ করবে না কারণ এটি করার পরে নিজেই আমদানি যন্ত্রপাতি নিয়ে ফিডিং না করে স্থানীয় মডিউলটি আমদানির কোনও উপায় থাকবে না।
সর্বজনীন

@ সর্বজনীন: এটি একটি আলাদা সমস্যা, যা আপনি তৃতীয় মডিউলের সাথে পেতে পারেন যা ক্যালেন্ডার আমদানি থেকে করে *
লিন্টগুলি

না, এটি সম্ভবত কাজ করবে না কারণ পাইথন মডিউলটির নামটিকে ক্যাশে করে sys.modulesএবং এটি আবার একই নামের সাথে একটি মডিউল আমদানি করবে না।
বোজ ইয়ানিভ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.