سؤال

لنفترض أن لدي هيكل الدليل التالي:

a\
    __init__.py
    b\
        __init__.py
        c\
            __init__.py
            c_file.py
        d\
            __init__.py
            d_file.py

في ال a الحزمة __init__.py, ، ال c تم استيراد الحزمة. لكن c_file.py الواردات a.b.d.

فشل البرنامج قائلا b غير موجود متى c_file.py يحاول الاستيراد a.b.d. (وهو غير موجود حقًا ، لأننا كنا في منتصف استيراده.)

كيف يمكن علاج هذه المشكلة؟

هل كانت مفيدة؟

المحلول

إذا كان A يعتمد على C و C يعتمد على A ، أليس في الواقع نفس الوحدة إذن؟

يجب أن تدرس حقًا سبب تقسيم A و C إلى حزمتين ، لأنه إما أن لديك بعض التعليمات البرمجية التي يجب أن تقسمها إلى حزمة أخرى (لجعلها تعتمد على هذه الحزمة الجديدة ، ولكن ليس بعضها البعض) ، أو يجب عليك دمجها في حزمة واحدة.

نصائح أخرى

يمكنك تأجيل الاستيراد ، على سبيل المثال في a/__init__.py:

def my_function():
    from a.b.c import Blah
    return Blah()

وهذا هو ، تأجيل الاستيراد حتى تكون هناك حاجة حقا. ومع ذلك ، سأكون أيضًا إلقاء نظرة فاحصة على تعريفات/استخدامات الحزمة الخاصة بي ، حيث قد يشير التبعية الدورية مثل الإشارة إلى مشكلة في التصميم.

لقد تساءلت عن ذلك عدة مرات (عادةً أثناء التعامل مع النماذج التي تحتاج إلى معرفة بعضها البعض). الحل البسيط هو فقط استيراد الوحدة النمطية بأكملها ، ثم الرجوع إلى الشيء الذي تحتاجه.

لذا بدلاً من القيام بذلك

from models import Student

في واحد ، و

from models import Classroom

في الآخر ، فقط افعل

import models

في أحدهم ، ثم اتصل على models.classroom عندما تحتاجه.

المشكلة هي أنه عند الركض من دليل ، بشكل افتراضي فقط ، تكون الحزم التي هي الدلائل الفرعية مرئية كواردات للمرشحين ، لذلك لا يمكنك استيراد ABD ، ومع ذلك ، يمكنك استيراد BD نظرًا لأن B عبارة عن حزمة فرعية من A.

إذا كنت تريد حقًا استيراد ABD c/__init__.py يمكنك تحقيق ذلك عن طريق تغيير مسار النظام ليكون دليلًا واحدًا فوق A وتغيير الاستيراد في a/__init__.py لتكون استيراد ABC

لك a/__init__.py يجب أن تبدو هكذا:

import sys
import os
# set sytem path to be directory above so that a can be a 
# package namespace
DIRECTORY_SCRIPT = os.path.dirname(os.path.realpath(__file__)) 
sys.path.insert(0,DIRECTORY_SCRIPT+"/..")
import a.b.c

تنشأ صعوبة إضافية عندما تريد تشغيل وحدات في C كنصوص. هنا الحزم A و B غير موجودة. يمكنك اختراق __int__.py في دليل C لتوجيه sys.path إلى الدليل الأعلى ثم استيراد __init__ في أي وحدات داخل C لتكون قادرة على استخدام المسار الكامل لاستيراد ABD ، أشك في أنه من الممارسات الجيدة الاستيراد __init__.py لكنها نجحت في حالات الاستخدام الخاصة بي.

أقترح النمط التالي. سيسمح استخدامه بإكمال تلقائي ونوع التلميح للعمل بشكل صحيح.

Cyclic_import_a.py

import playground.cyclic_import_b

class A(object):
    def __init__(self):
        pass

    def print_a(self):
        print('a')

if __name__ == '__main__':
    a = A()
    a.print_a()

    b = playground.cyclic_import_b.B(a)
    b.print_b()

Cyclic_import_b.py

import playground.cyclic_import_a

class B(object):
    def __init__(self, a):
        self.a: playground.cyclic_import_a.A = a

    def print_b(self):
        print('b1-----------------')
        self.a.print_a()
        print('b2-----------------')

لا يمكنك استيراد الفصول الدراسية A&B باستخدام هذا الجملة

from playgroud.cyclic_import_a import A
from playground.cyclic_import_b import B

لا يمكنك إعلان نوع المعلمة A في الفئة B __ init __ ، ولكن يمكنك "إلقاء" على هذا النحو:

def __init__(self, a):
    self.a: playground.cyclic_import_a.A = a

حل آخر هو استخدام وكيل لـ d_file.

على سبيل المثال ، دعنا نقول أنك تريد مشاركة فئة بلاه مع C_File. وهكذا يحتوي D_File على:

class blah:
    def __init__(self):
        print("blah")

إليك ما تدخله في c_file.py:

# do not import the d_file ! 
# instead, use a place holder for the proxy of d_file
# it will be set by a's __init__.py after imports are done
d_file = None 

def c_blah(): # a function that calls d_file's blah
    d_file.blah()

وفي أ فيه.السنة التحضيرية:

from b.c import c_file
from b.d import d_file

class Proxy(object): # module proxy
    pass
d_file_proxy = Proxy()
# now you need to explicitly list the class(es) exposed by d_file
d_file_proxy.blah = d_file.blah 
# finally, share the proxy with c_file
c_file.d_file = d_file_proxy

# c_file is now able to call d_file.blah
c_file.c_blah() 
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top