سؤال

أرغب في إنشاء فصول لاستخدامها كمصممين للديكور مع الحفاظ على المبادئ التالية:

  1. يجب أن يكون من الممكن تكديس العديد من أدوات تزيين الفئات هذه فوق وظيفة واحدة.
  2. يجب أن يكون مؤشر اسم الوظيفة الناتج غير قابل للتمييز من نفس الوظيفة بدون ديكور، باستثناء النوع/الفئة فقط.
  3. لا ينبغي أن يكون ترتيب مصممي الديكور ذا صلة ما لم يتم تكليفهم فعليًا من قبل مصممي الديكور.أي.يمكن تطبيق مصممي الديكور المستقلين بأي ترتيب.

هذا لمشروع Django، والحالة المحددة التي أعمل عليها الآن تحتاج الطريقة إلى 2 ديكور، ولتظهر كدالة بايثون عادية:

@AccessCheck
@AutoTemplate
def view(request, item_id) {}

يقوم @AutoTemplate بتغيير الوظيفة بحيث تقوم فقط بإرجاع قاموس لاستخدامه في السياق بدلاً من إرجاع HttpResponse.يتم استخدام RequestContext، ويتم استنتاج اسم القالب من اسم الطريقة والوحدة النمطية.

يضيف @AccessCheck عمليات فحص إضافية للمستخدم بناءً على item_id.

أعتقد أن هذا مجرد الحصول على المنشئ بشكل صحيح ونسخ السمات المناسبة، ولكن ما هي السمات هذه؟

لن يعمل الديكور التالي كما أصف:

class NullDecl (object):
    def __init__ (self, func):
        self.func = func
    def __call__ (self, * args):
        return self.func (*args)

كما هو موضح بالكود التالي:

@NullDecl
@NullDecl
def decorated():
    pass

def pure():
    pass

# results in set(['func_closure', 'func_dict', '__get__', 'func_name',
# 'func_defaults', '__name__', 'func_code', 'func_doc', 'func_globals'])
print set(dir(pure)) - set(dir(decorated));

بالإضافة إلى ذلك، حاول إضافة "print func.اسم" في منشئ NullDecl، وسيعمل مع مصمم الديكور الأول، ولكن ليس الثاني - لأن الاسم سيكون مفقودًا.

مشتق ممتلئإجابة قليلاً، ويبدو أنها تعمل بشكل جيد:

class NullDecl (object):
    def __init__ (self, func):
        self.func = func
        for n in set(dir(func)) - set(dir(self)):
            setattr(self, n, getattr(func, n))

    def __call__ (self, * args):
        return self.func (*args)
    def __repr__(self):
        return self.func
هل كانت مفيدة؟

المحلول

ستبدو فئة الديكور التي لا تفعل شيئًا كما يلي:

class NullDecl (object):
   def __init__ (self, func):
      self.func = func
      for name in set(dir(func)) - set(dir(self)):
        setattr(self, name, getattr(func, name))

   def __call__ (self, *args):
      return self.func (*args)

وبعد ذلك يمكنك تطبيقه بشكل طبيعي:

@NullDecl
def myFunc (x,y,z):
   return (x+y)/z

نصائح أخرى

ال وحدة الديكور يساعدك على كتابة الديكورات التي تحافظ على التوقيع.

و ال مكتبة بايثون ديكوراتور قد توفر أمثلة مفيدة لمصممي الديكور.

لإنشاء ديكور يغلف الوظائف بطريقة تجعلها غير قابلة للتمييز عن الوظيفة الأصلية، استخدم functools.wraps.

مثال:


def mydecorator(func):
    @functools.wraps(func):
    def _mydecorator(*args, **kwargs):
        do_something()
        try:
            return func(*args, **kwargs)
        finally:
            clean_up()
    return _mydecorator

# ... and with parameters
def mydecorator(param1, param2):
    def _mydecorator(func):
        @functools.wraps(func)
        def __mydecorator(*args, **kwargs):
            do_something(param1, param2)
            try:
                return func(*args, **kwargs)
            finally:
                clean_up()
        return __mydecorator
    return _mydecorator

(أفضلي الشخصي هو إنشاء ديكورات باستخدام الوظائف، وليس الفئات)

ترتيب الديكورات كالتالي:


@d1
@d2
def func():
    pass

# is equivalent to
def func():
    pass

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