وحدة إعادة عرضها إذا استوردت من مسار مختلف

StackOverflow https://stackoverflow.com/questions/1459236

  •  12-09-2019
  •  | 
  •  

سؤال

في تطبيق كبير أنا أعمل، استيراد العديد من الأشخاص نفس الوحدات النمطية بشكل مختلف، مثل استيراد X أو من Y استيراد X الآثار الجانبية التي يتم استيرادها x مرتين وقد تقدم أخطاء خفية للغاية، إذا كان شخص ما يعتمد على الأخطاء الدقيقة للغاية، إذا كان شخص ما يعتمد على السمات العالمية

على سبيل المثال افترض أن لدي حزمة mypakcage مع ثلاثة ملف mymodule.py، Main.py فيه.السنة التحضيرية

MyModule.py محتويات

l = []
class A(object): pass

المحتويات الرئيسية

def add(x):
    from mypackage import mymodule
    mymodule.l.append(x)
    print "updated list",mymodule.l

def get():
    import mymodule
    return mymodule.l

add(1)
print "lets check",get()

add(1)
print "lets check again",get()

يطبع

updated list [1]
lets check []
updated list [1, 1]
lets check again []

لأن هناك الآن قوائمين في وحدتين مختلفتين، فف فئة مشابها مختلفة بالنسبة لي أنها تبدو جدية بما فيه الكفاية لأن الطبقات نفسها سيتم التعامل معها بشكل مختلف على سبيل المثال أدناه رمز يطبع كاذبة

def create():
    from mypackage import mymodule
    return mymodule.A()

def check(a):
    import mymodule
    return isinstance(a, mymodule.A)

print check(create())

سؤال:

هل هناك أي طريقة لتجنب هذا؟ باستثناء فرض هذه الوحدة يجب استيرادها بطريقة واحدة Onyl. لا يمكن التعامل مع هذه من قبل آلية استيراد Python، لقد رأيت العديد من الأخطاء المتعلقة بهذا في رمز Django وأماكن أخرى أيضا.

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

المحلول

يمكنني فقط نسخ هذا إذا كان Main.py هو الملف الذي تقوم به بالفعل. في هذه الحالة، ستحصل على الدليل الحالي للقلب الرئيسي على مسار SYS. ولكن يبدو أن لديك أيضا مجموعة مسار النظام بحيث يمكن استيراد mypackage.

لن تدرك بيثون في هذا الموقف أن MyModule و mypackage.mymodule هي نفس الوحدة، وتحصل على هذا التأثير. يوضح هذا التغيير هذا:

def add(x):
    from mypackage import mymodule
    print "mypackage.mymodule path", mymodule
    mymodule.l.append(x)
    print "updated list",mymodule.l

def get():
    import mymodule
    print "mymodule path", mymodule
    return mymodule.l

add(1)
print "lets check",get()

add(1)
print "lets check again",get()


$ export PYTHONPATH=.
$ python  mypackage/main.py 

mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
mymodule path <module 'mymodule' from '/tmp/mypackage/mymodule.pyc'>

ولكن أضف mainfile آخر، في الدليل الكوري:

realmain.py:
from mypackage import main

والنتيجة مختلفة:

mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>

لذلك أظن أن لديك ملف Python الرئيسي الخاص بك داخل الحزمة. وفي هذه الحالة الحل هو عدم القيام بذلك. :-)

نصائح أخرى

يتم استيراد كل وحدة الاسم الوحدة النمطية مرة واحدة فقط. المشكلة هي، أنت تقوم باستيرادها بشكل مختلف. في أول فأنت تقوم باستيرادها من الحزمة العالمية، وفي الثانية التي تقوم بها محلية وغير تعبئة import. وبعد Python يرى الوحدات المختلفة. أول استيراد يتم تخزين مؤقتا داخليا mypackage.mymodule والثاني كما mymodule فقط.

طريقة لحل هذا هو دائما استخدام الواردات المطلقة. وبعد هذا هو، دائما إعطاء الوحدة النمطية مسارات الاستيراد المطلقة من حزمة المستوى الأعلى فصاعدا:

def add(x):
    from mypackage import mymodule
    mymodule.l.append(x)
    print "updated list",mymodule.l

def get():
    from mypackage import mymodule
    return mymodule.l

تذكر أن نقطة الدخول الخاصة بك (الملف الذي تقوم بتشغيله، main.py) يجب أن يكون أيضا الخارج حزمة. عندما تريد أن تكون رمز نقطة الدخول داخل الحزمة، عادة ما تستخدم برنامج نصي صغير بدلا من ذلك. مثال:

runme.py, ، خارج الحزمة:

from mypackage.main import main
main()

و في main.py يمكنك إضافة:

def main():
    # your code

وجدت هذا المستند بواسطة JP Calderone لتكون نصيحة رائعة حول كيفية (لا) هيكل مشروع Python الخاص بك. بعد ذلك لن يكون لديك مشاكل. الانتباه إلى bin مجلد - إنه خارج الحزمة. سأتنسخ النص بأكمله هنا:

هيكل نظام الملفات لمشروع بيثون

يفعل:

  • اسم الدليل الذي يتعلق بمشروعك. على سبيل المثال، إذا كان مشروعك يسمى "ملتوية"اسم دليل المستوى الأعلى لملفات المصدر الخاصة به Twisted. وبعد عند إجراء إصدارات، يجب عليك تضمين لاحقة رقم الإصدار: Twisted-2.5.
  • إنشاء دليل Twisted/bin ووضع الملفات التنفيذية الخاصة بك هناك، إذا كان لديك أي. لا تعطيهم .pyالتمديد، حتى لو كانت ملفات مصدر بيثون. لا تضع أي رمز فيها باستثناء استيراد ودعوة إلى وظيفة رئيسية محددة في مكان آخر في مشاريعك.
  • إذا كان مشروعك مستعبر كملف مصدر ثعبان واحد، فقم بوضعه في الدليل واسمه بشيء يتعلق بمشروعك. علي سبيل المثال، Twisted/twisted.py. وبعد إذا كنت بحاجة إلى ملفات مصدر متعددة، فقم بإنشاء حزمة بدلا من ذلك (Twisted/twisted/مع فارغة Twisted/twisted/__init__.py) ووضع الملفات المصدر الخاصة بك في ذلك. علي سبيل المثال، Twisted/twisted/internet.py.
  • ضع اختبارات الوحدة الخاصة بك في حزمة فرعية من الحزمة الخاصة بك (ملاحظة - وهذا يعني أن خيار ملف مصدر Python الفردي أعلاه كان خدعة - تحتاج دائما إلى ملف واحد آخر على الأقل اختبارات وحدتك). علي سبيل المثال، Twisted/twisted/test/. وبعد بالطبع، اجعلها حزمة Twisted/twisted/test/__init__.pyوبعد ضع الاختبارات في الملفات مثل Twisted/twisted/test/test_internet.py.
  • يضيف Twisted/README و تwisted/setup.py لشرح وتثبيت البرنامج الخاص بك، على التوالي، إذا كنت تشعر بأنك لطيف.

لا:

  • ضع المصدر الخاص بك في دليل يسمى src أو lib. وبعد هذا يجعل من الصعب تشغيل دون تثبيت.
  • ضع الاختبارات الخاصة بك خارج حزمة بيثون الخاصة بك. هذا يجعل من الصعب تشغيل الاختبارات مقابل إصدار مثبت.
  • إنشاء حزمة لديها فقط __init__.py ثم ضع كل التعليمات البرمجية الخاصة بك __init__.py. وبعد فقط قم بإجراء وحدة واحدة بدلا من الحزمة، إنه أكثر بساطة.
  • حاول التوصل إلى خارقة سحرية لجعل بيثون قادرين على استيراد الوحدة النمطية أو الحزمة دون الحصول على المستخدم إضافة الدليل الذي يحتوي على مسار الاستيراد الخاص به (إما عبر PYTHONPATH أو بعض الآلية الأخرى). لن تتعامل بشكل صحيح مع جميع الحالات وسيغضب المستخدمون عند عدم عمل البرنامج في بيئتهم.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top