python - مترجمة - منتديات بايثون




ما هي طريقة نظيفة ، pythonic أن يكون لها العديد من الصانعين في بيثون؟ (8)

أفضل إجابة هي تلك التي وردت أعلاه حول الحجج الافتراضية ، لكنني استمتعت بكتابة هذا ، وهي بالتأكيد تناسب الفاتورة الخاصة بـ "مُنشئين متعددين". استخدام على مسؤوليتك الخاصة.

ماذا عن الطريقة new .

"تعمل التطبيقات النموذجية على إنشاء مثيل جديد للفئة عن طريق استدعاء الأسلوب الجديد () الخاص بالفئة superclass باستخدام super (currentclass، cls). جديد (cls [، ...]) مع الوسيطات المناسبة ومن ثم تعديل المثيل الذي تم إنشاؤه حديثًا عند الضرورة قبل يعيدها ".

حتى تتمكن من الحصول على طريقة جديدة لتعديل تعريف الفئة الخاص بك عن طريق إرفاق طريقة المنشئ المناسبة.

class Cheese(object):
    def __new__(cls, *args, **kwargs):

        obj = super(Cheese, cls).__new__(cls)
        num_holes = kwargs.get('num_holes', random_holes())

        if num_holes == 0:
            cls.__init__ = cls.foomethod
        else:
            cls.__init__ = cls.barmethod

        return obj

    def foomethod(self, *args, **kwargs):
        print "foomethod called as __init__ for Cheese"

    def barmethod(self, *args, **kwargs):
        print "barmethod called as __init__ for Cheese"

if __name__ == "__main__":
    parm = Cheese(num_holes=5)

لا أستطيع العثور على إجابة محددة لهذا. AFAIK ، لا يمكن أن يكون لديك عدة وظائف __init__ في فئة Python. إذن كيف يمكنني حل هذه المشكلة؟

لنفترض أن لدي صفًا يسمى Cheese مع الخاصية number_of_holes . كيف يمكنني الحصول على طريقتين لإنشاء كائنات الجبن ...

  • واحد يأخذ عدد من الثقوب مثل: parmesan = Cheese(num_holes = 15)
  • و التي لا تأخذ أي حجج و فقط عشوائية الخاصية number_of_holes : gouda = Cheese()

يمكنني التفكير في طريقة واحدة فقط للقيام بهذا ، ولكن يبدو ذلك غريباً:

class Cheese():
    def __init__(self, num_holes = 0):
        if (num_holes == 0):
            # randomize number_of_holes
        else:
            number_of_holes = num_holes

ماذا تقول؟ هل هناك طريقة أخرى؟


استخدم num_holes=None افتراضي ، بدلاً من ذلك. ثم تحقق مما إذا كان num_holes is None ، وإذا كان الأمر كذلك ، قم num_holes is None . هذا ما أراه بشكل عام ، على أي حال.

قد تستدعي أساليب البناء المختلفة بشكل جذري أسلوباً طبقيًا يعيد مثيلًا من cls .


سأستخدم الميراث. خاصة إذا كان هناك اختلافات أكثر من عدد الثقوب. خاصة إذا كان يجب على Gouda أن يكون لديها مجموعة مختلفة من الأعضاء ثم Parmesan.

class Gouda(Cheese):
    def __init__(self):
        super(Gouda).__init__(num_holes=10)


class Parmesan(Cheese):
    def __init__(self):
        super(Parmesan).__init__(num_holes=15) 

في الواقع None أفضل بكثير للقيم "السحرية":

class Cheese():
    def __init__(self, num_holes = None):
        if num_holes is None:
            ...

الآن إذا كنت تريد الحرية الكاملة لإضافة المزيد من المعلمات:

class Cheese():
    def __init__(self, *args, **kwargs):
        #args -- tuple of anonymous arguments
        #kwargs -- dictionary of named arguments
        self.num_holes = kwargs.get('num_holes',random_holes())

لتوضيح مفهوم *args و **kwargs (يمكنك بالفعل تغيير هذه الأسماء):

def f(*args, **kwargs):
   print 'args: ', args, ' kwargs: ', kwargs

>>> f('a')
args:  ('a',)  kwargs:  {}
>>> f(ar='a')
args:  ()  kwargs:  {'ar': 'a'}
>>> f(1,2,param=3)
args:  (1, 2)  kwargs:  {'param': 3}

http://docs.python.org/reference/expressions.html#calls


لماذا تعتقد أن الحل الخاص بك هو "عالي الكعب"؟ شخصياً ، أفضّل مُنشئًا واحدًا له قيم افتراضية على مُنشِئات متعددة الزائد في حالات مثل حالتك (لا تدعم بايثون التحميل الزائد على أي حال):

def __init__(self, num_holes=None):
    if num_holes is None:
        # Construct a gouda
    else:
        # custom cheese
    # common initialization

بالنسبة للحالات المعقدة بالفعل مع الكثير من المنشئات المختلفة ، قد يكون من الأنظف استخدام وظائف مصنع مختلفة بدلاً من ذلك:

@classmethod
def create_gouda(cls):
    c = Cheese()
    # ...
    return c

@classmethod
def create_cheddar(cls):
    # ...

في مثال الجبن الخاص بك قد ترغب في استخدام الفئة الفرعية جودا من الجبن رغم ...


هذه أفكار جيدة للتنفيذ الخاص بك ، ولكن إذا كنت تقدم واجهة صنع الجبن للمستخدم. إنهم لا يهتمون بعدد الثقوب التي يمتلكها الجبن أو ما يدخل في صنع الجبن. مستخدم رمزك يريد فقط "gouda" أو "parmesean" أليس كذلك؟

فلماذا لا تفعل هذا:

# cheese_user.py
from cheeses import make_gouda, make_parmesean

gouda = make_gouda()
paremesean = make_parmesean()

ومن ثم يمكنك استخدام أي من الطرق المذكورة أعلاه لتنفيذ الوظائف فعليًا:

# cheeses.py
class Cheese(object):
    def __init__(self, *args, **kwargs):
        #args -- tuple of anonymous arguments
        #kwargs -- dictionary of named arguments
        self.num_holes = kwargs.get('num_holes',random_holes())

def make_gouda():
    return Cheese()

def make_paremesean():
    return Cheese(num_holes=15)

هذه تقنية تغليف جيدة ، وأعتقد أنها أكثر بيثونية. بالنسبة لي ، هذه الطريقة للقيام بالأشياء تتناسب أكثر مع كتابة البطة. أنت ببساطة تسأل عن كائن gouda وأنت لا تهتم حقا ما هو الفصل.


يجب على المرء بالتأكيد أن يفضل الحلول التي تم نشرها بالفعل ، ولكن بما أنه لم يذكر أحد هذا الحل حتى الآن ، أعتقد أنه من الجدير ذكره للتأكد من اكتماله.

يمكن تعديل نهج @classmethod لتوفير مُنشئ بديل لا يستدعي المُنشئ الافتراضي ( __init__ ). بدلاً من ذلك ، يتم إنشاء مثيل باستخدام __new__ .

يمكن استخدام هذا إذا كان لا يمكن تحديد نوع التهيئة استناداً إلى نوع وسيطة منشئ ، ولا تشارك المنشئات التعليمات البرمجية.

مثال:

class MyClass(set):

    def __init__(self, filename):
        self._value = load_from_file(filename)

    @classmethod
    def from_somewhere(cls, somename):
        obj = cls.__new__(cls)  # Does not call __init__
        obj._value = load_from_somewhere(somename)
        return obj

class Cheese:
    def __init__(self, *args, **kwargs):
        """A user-friendly initialiser for the general-purpose constructor.
        """
        ...

    def _init_parmesan(self, *args, **kwargs):
        """A special initialiser for Parmesan cheese.
        """
        ...

    def _init_gauda(self, *args, **kwargs):
        """A special initialiser for Gauda cheese.
        """
        ...

    @classmethod
    def make_parmesan(cls, *args, **kwargs):
        new = cls.__new__(cls)
        new._init_parmesan(*args, **kwargs)
        return new

    @classmethod
    def make_gauda(cls, *args, **kwargs):
        new = cls.__new__(cls)
        new._init_gauda(*args, **kwargs)
        return new




constructor