it-swarm.dev

كيف يمكنني إنشاء أساليب الفصل بشكل منفصل

إذا قمت بتعريف برنامج الثعبان الصغير

class a():
    def _func(self):
        return "asdf"

    # Not sure what to resplace __init__ with so that a.func will return asdf
    def __init__(self, *args, **kwargs):
         setattr(self, 'func', classmethod(self._func))

if __== "__main__":
    a.func

أتلقى خطأ التتبع

Traceback (most recent call last):
  File "setattr_static.py", line 9, in <module>
    a.func
AttributeError: class a has no attribute 'func'

ما أحاول اكتشافه هو ، كيف يمكنني تعيين طريقة الفصل ديناميكيًا إلى فصل دراسي دون إنشاء مثيل كائن؟


تصحيح:

الجواب لهذه المشكلة هو

class a():
    pass

def func(cls, some_other_argument):
    return some_other_argument

setattr(a, 'func', classmethod(func))

if __== "__main__":
    print(a.func)
    print(a.func("asdf"))

إرجاع الإخراج التالي

<bound method type.func of <class '__main__.a'>>
asdf
55
user1876508

يمكنك إضافة classmethod ديناميكيًا إلى الفصل الدراسي بواسطة تعيين بسيط إلى كائن الفئة أو بواسطة setattr على كائن الفئة. أنا هنا أستخدم اصطلاح python الذي تبدأ فيه الفصول بالأحرف الكبيرة للحد من الارتباك:

# define a class object (your class may be more complicated than this...)
class A(object):
    pass

# a class method takes the class object as its first variable
def func(cls):
    print 'I am a class method'

# you can just add it to the class if you already know the name you want to use
A.func = classmethod(func)

# or you can auto-generate the name and set it this way
the_name = 'other_func' 
setattr(A, the_name, classmethod(func))
62
tdelaney

هناك بعض المشاكل هنا:

  • يتم تشغيل __init__ فقط عند إنشاء مثيل ، على سبيل المثال obj = a(). هذا يعني أنه عند قيامك بـ a.func ، لم يحدث استدعاء setattr()
  • لا يمكنك الوصول إلى سمات الفصل الدراسي مباشرةً من داخل أساليب ذلك الفصل الدراسي ، لذا بدلاً من استخدام _func فقط داخل __init__ ، ستحتاج إلى استخدام self._func أو self.__class__._func
  • self سيكون مثيل لـ a ، إذا قمت بتعيين سمة على المثيل ، فلن يكون متاحًا إلا لهذا المثيل ، وليس للفئة. لذلك حتى بعد الاتصال بـ setattr(self, 'func', self._func) ، فإن a.func سيرفع AttributeError
  • باستخدام staticmethod الطريقة التي لن تفعل بها أي شيء ، staticmethod سيعود دالة ناتجة ، لا يعدل الوسيطة. لذا بدلاً من ذلك ، فأنت تريد شيئًا مثل setattr(self, 'func', staticmethod(self._func)) (ولكن مع الأخذ في الاعتبار التعليقات أعلاه ، فإن هذا لن ينجح)

والسؤال الآن هو ، ما الذي تحاول فعله فعلاً؟ إذا كنت تريد حقًا إضافة سمة إلى الفصل الدراسي عند تهيئة مثيل ، فيمكنك فعل شيء مثل التالي:

class a():
    def _func(self):
        return "asdf"

    def __init__(self, *args, **kwargs):
        setattr(self.__class__, 'func', staticmethod(self._func))

if __== '__main__':
    obj = a()
    a.func
    a.func()

ومع ذلك ، هذا لا يزال نوعا من غريب. يمكنك الآن الوصول إلى a.func والاتصال بها دون أي مشاكل ، ولكن الوسيطة self إلى a.func ستكون دائمًا أحدث مثيل تم إنشاؤه مؤخرًا من a. لا يمكنني التفكير حقًا في أي طريقة عقلانية لتحويل أسلوب مثيل مثل _func() إلى طريقة ثابتة أو طريقة فصل دراسي للفئة.

نظرًا لأنك تحاول إضافة وظيفة بشكل ديناميكي إلى الفصل ، فربما يكون شيء مثل التالي أقرب إلى ما تحاول فعله فعلاً؟

class a():
    pass

def _func():
    return "asdf"

a.func = staticmethod(_func)  # or setattr(a, 'func', staticmethod(_func))

if __== '__main__':
    a.func
    a.func()
8
Andrew Clark

يمكنك أن تفعل ذلك بهذه الطريقة

class a():
    def _func(self):
        return "asdf"

setattr(a, 'func', staticmethod(a._func))

if __== "__main__":
    a.func()
2
eri

1. الفكرة الأساسية: استخدام فئة إضافية لعقد الأساليب

لقد وجدت طريقة ذات مغزى للقيام بالعمل:

أولاً ، نعرّف BaseClass:

class MethodPatcher:
    @classmethod
    def patch(cls, target):
        for k in cls.__dict__:
            obj = getattr(cls, k)
            if not k.startswith('_') and callable(obj):
                setattr(target, k, obj)

الآن بعد أن أصبح لدينا فصل أصلي:

class MyClass(object):
    def a(self):
        print('a')

بعد ذلك نحدد الطريقة الجديدة التي نريد إضافتها في فئة Patcher جديدة:

(لا تجعل اسم الطريقة يبدأ بـ _ في هذه الحالة)

class MyPatcher(MethodPatcher):
    def b(self):
        print('b')

ثم اتصل:

MyPatcher.patch(MyClass)

لذلك ، ستجد الطريقة الجديدة b(self) تضاف إلى MyClass الأصلي:

obj = MyClass()
obj.a()  # which prints an 'a'
obj.b()  # which prints a 'b'

2. جعل بناء الجملة أقل مطول ، ونحن نستخدم ديكور فئة

الآن إذا كان لدينا MethodPatcher decalred ، فنحن بحاجة إلى القيام بأمرين:

  • تحديد فئة فرعية ChildClass من ModelPatcher والتي تحتوي على طرق إضافية لإضافتها
  • استدعاء ChildClass.patch(TargetClass)

لذلك سرعان ما وجدنا أن الخطوة الثانية يمكن تبسيطها باستخدام الديكور:

نحدد ديكور:

def patch_methods(model_class):
    def do_patch(cls):
        cls.patch(model_class)
    return do_patch

ويمكننا استخدامه مثل:

@patch_methods(MyClass)
class MyClassPatcher(MethodPatcher):

    def extra_method_a(self):
        print('a', self)

    @classmethod
    def extra_class_method_b(cls):
        print('c', cls)

    # !!ATTENTION!! the effect on declaring staticmethod here may not work as expected:
    # calling this method on an instance will take the self into the first argument.
    # @staticmethod
    # def extra_static_method_c():
    #    print('c')

3. التفاف معا

لذلك ، يمكننا الآن وضع تعريف MethodPatcher و patch_method في وحدة نمطية واحدة:

# method_patcher.py

class MethodPatcher:
    @classmethod
    def patch(cls, target):
        for k in cls.__dict__:
            obj = getattr(cls, k)
            if not k.startswith('_') and callable(obj):
                setattr(target, k, obj)

def patch_methods(model_class):
    def do_patch(cls):
        cls.patch(model_class)
    return do_patch

حتى نتمكن من استخدامه بحرية:

from method_patcher import ModelPatcher, patch_model

4. الحل النهائي: إعلان أكثر بساطة

لقد وجدت قريبًا أن فئة MethodPatcher ليست ضرورية ، في حين أن مصمم @patch_method يمكنه القيام بالعمل ، لذلك أخيرًا نحن بحاجة فقط إلى patch_method:

def patch_methods(model_class):
    def do_patch(cls):
        for k in cls.__dict__:
            obj = getattr(cls, k)
            if not k.startswith('_') and callable(obj):
                setattr(model_class, k, obj)
    return do_patch

ويصبح الاستخدام:

@patch_methods(MyClass)
class MyClassPatcher:

    def extra_method_a(self):
        print('a', self)

    @classmethod
    def extra_class_method_b(cls):
        print('c', cls)

    # !!ATTENTION!! the effect on declaring staticmethod here may not work as expected:
    # calling this method on an instance will take the self into the first argument.
    # @staticmethod
    # def extra_static_method_c():
    #    print('c')
2
Alfred Huang

تحتاج إلى setattr(self, 'func', staticmethod(self._func))

تحتاج إلى تهيئة الفئة variable=a() للاتصال __init__ لا يوجد الحرف الأولي في الفئة الثابتة

1
eri

أنا أستخدم Python 2.7.5 ، ولم أتمكن من الحصول على الحلول المذكورة أعلاه. هذا ما انتهى بي:

# define a class object (your class may be more complicated than this...)
class A(object):
    pass

def func(self):
    print 'I am class {}'.format(self.name)

A.func = func

# using classmethod() here failed with:
#       AttributeError: type object '...' has no attribute 'name'
0
Cognitiaclaeves