سؤال

أواجه مشكلة في استبدال وظيفة من وحدة مختلفة بوظيفة أخرى وهي تقودني إلى الجنون.

دعنا نقول أن لدي شريط نمطية يشبه هذا:

from a_package.baz import do_something_expensive

def a_function():
    print do_something_expensive()

ولدي وحدة أخرى تبدو هكذا:

from bar import a_function
a_function()

from a_package.baz import do_something_expensive
do_something_expensive = lambda: 'Something really cheap.'
a_function()

import a_package.baz
a_package.baz.do_something_expensive = lambda: 'Something really cheap.'
a_function()

أتوقع الحصول على النتائج:

Something expensive!
Something really cheap.
Something really cheap.

لكن بدلاً من ذلك أحصل على هذا:

Something expensive!
Something expensive!
Something expensive!

ما الخطأ الذي افعله؟

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

المحلول

قد يساعد ذلك في التفكير في كيفية عمل مساحات أسماء الثعبان: إنها قواميس بشكل أساسي. لذلك عندما تفعل هذا:

from a_package.baz import do_something_expensive
do_something_expensive = lambda: 'Something really cheap.'

أعتقد أنه من مثل هذا:

do_something_expensive = a_package.baz['do_something_expensive']
do_something_expensive = lambda: 'Something really cheap.'

نأمل أن تتمكن من إدراك سبب عدم عمل هذا بعد ذلك :-) بمجرد استيراد اسم إلى مساحة اسم ، فإن قيمة الاسم في مساحة الاسم التي قمت باستيرادها من عند غير ذي صلة. أنت تعدل فقط قيمة do_something_expless في مساحة اسم الوحدة المحلية ، أو في مساحة اسم A_Package.baz أعلاه. ولكن نظرًا لأن BAR يستورد do_something_expless مباشرة ، بدلاً من الرجوع إليه من مساحة اسم الوحدة النمطية ، تحتاج إلى الكتابة إلى مساحة الاسم:

import bar
bar.do_something_expensive = lambda: 'Something really cheap.'

نصائح أخرى

هناك ديكور أنيقة حقًا لهذا: Guido Van Rossum: Python-Dev List: Monkeypatching Idioms.

هناك أيضا dectools الحزمة ، التي رأيتها Pycon 2010 ، والتي قد تكون قادرة على استخدامها في هذا السياق أيضًا ، ولكن قد يسير هذا في الواقع في الاتجاه الآخر (Monkeypatching على المستوى التعريفي ... حيث لا)

إذا كنت ترغب فقط في تصحيحها لمكالمتك وخلاف ذلك ، اترك الكود الأصلي الذي يمكنك استخدامه https://docs.python.org/3/library/unittest.mock.html#patch (منذ Python 3.3):

with patch('a_package.baz.do_something_expensive', new=lambda: 'Something really cheap.'):
    print do_something_expensive()
    # prints 'Something really cheap.'

print do_something_expensive()
# prints 'Something expensive!'

في المقتطف الأول ، تصنع bar.do_something_expensive الرجوع إلى كائن الوظيفة الذي a_package.baz.do_something_expensive يشير في تلك اللحظة. إلى "monkeypatch" حقًا الذي ستحتاج إلى تغيير الوظيفة نفسها (يمكنك فقط تغيير الأسماء التي تشير إليها) ؛ هذا ممكن ، لكنك لا تريد فعل ذلك.

في محاولاتك لتغيير سلوك a_function, ، لقد فعلت شيئين:

  1. في المحاولة الأولى ، تقوم بعمل اسم عالمي do_something_expless في الوحدة النمطية. ومع ذلك ، أنت تتصل a_function, ، الذي لا يبحث في الوحدة النمطية لحل الأسماء ، لذلك لا يزال يشير إلى نفس الوظيفة.

  2. في المثال الثاني ، تقوم بتغيير ماذا a_package.baz.do_something_expensive يشير إلى ، ولكن bar.do_something_expensive لا يرتبط بطريقة سحرية. لا يزال هذا الاسم يشير إلى كائن الوظيفة الذي نظر إليه عندما تم تشغيله.

إن أبسط النهج غير المباشر هو التغيير bar.py ليقول

import a_package.baz

def a_function():
    print a_package.baz.do_something_expensive()

قد يكون الحل الصحيح أحد شيئين:

  • إعادة تعريف a_function لاتخاذ وظيفة كوسيطة واتصل بذلك ، بدلاً من محاولة التسلل وتغيير الوظيفة التي يتم ترميزها بشدة للإشارة إلى ، أو
  • تخزين الوظيفة المراد استخدامها في مثيل للفئة ؛ هذه هي الطريقة التي نفعل بها حالة قابلة للتغيير في بيثون.

استخدام Globals (هذا هو ما هو تغيير مستوى الوحدة النمطية من الوحدات الأخرى) هو أ شيء سيء وهذا يؤدي إلى رمز لا يمكن التخلص منه ، مربك ، غير قابل للاستمتاع ، الذي لا يمكن أن يكون من الصعب تتبع تدفقه.

do_something_expensive في ال a_function() الوظيفة هي مجرد متغير داخل مساحة اسم الوحدة النمطية التي تشير إلى كائن دالة. عند إعادة تعريف الوحدة النمطية ، تقوم بذلك في مساحة اسم مختلفة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top