هل هناك طريقة بسيطة وأنيقة لتحديد المفردات؟[ينسخ]

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

سؤال

هذا السؤال لديه بالفعل إجابة هنا:

يبدو أن هناك طرقًا عديدة للتعريف مفردات في بايثون.هل هناك رأي إجماعي بشأن Stack Overflow؟

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

المحلول

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

إذا كنت ترغب في استخدام فصل دراسي، فلا توجد طريقة لإنشاء فئات خاصة أو مُنشئات خاصة في Python، لذلك لا يمكنك الحماية ضد عمليات إنشاء مثيلات متعددة، بخلاف مجرد اتفاقية استخدام واجهة برمجة التطبيقات (API) الخاصة بك.سأظل أضع الأساليب في وحدة نمطية، وأعتبر الوحدة الوحدة المفردة.

نصائح أخرى

هذا هو تطبيقي الخاص للمفردات.كل ما عليك فعله هو تزيين الفصل؛للحصول على المفردة، عليك بعد ذلك استخدام Instance طريقة.هنا مثال:

@Singleton
class Foo:
   def __init__(self):
       print 'Foo created'

f = Foo() # Error, this isn't how you get the instance of a singleton

f = Foo.instance() # Good. Being explicit is in line with the Python Zen
g = Foo.instance() # Returns already created instance

print f is g # True

وهذا هو الكود:

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Also, the decorated class cannot be
    inherited from. Other than that, there are no restrictions that apply
    to the decorated class.

    To get the singleton instance, use the `instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)

يمكنك تجاوز __new__ طريقة مثل هذا:

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(
                                cls, *args, **kwargs)
        return cls._instance


if __name__ == '__main__':
    s1 = Singleton()
    s2 = Singleton()
    if (id(s1) == id(s2)):
        print "Same"
    else:
        print "Different"

هناك طريقة مختلفة قليلاً لتنفيذ المفردة في Python وهي نمط بورغ بقلم Alex Martelli (موظف Google وعبقري Python).

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state

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

نهج الوحدة يعمل بشكل جيد.إذا كنت في حاجة ماسة إلى مفرد فإنني أفضل نهج Metaclass.

class Singleton(type):
    def __init__(cls, name, bases, dict):
        super(Singleton, cls).__init__(name, bases, dict)
        cls.instance = None 

    def __call__(cls,*args,**kw):
        if cls.instance is None:
            cls.instance = super(Singleton, cls).__call__(*args, **kw)
        return cls.instance

class MyClass(object):
    __metaclass__ = Singleton

انظر هذا التنفيذ من بيب318, تنفيذ النمط المفرد باستخدام الديكور:

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...

كما إجابة مقبولة يقول، الطريقة الأكثر اصطلاحية هي فقط استخدام وحدة.

ومع أخذ ذلك في الاعتبار، إليك إثباتًا للمفهوم:

def singleton(cls):
    obj = cls()
    # Always return the same object
    cls.__new__ = staticmethod(lambda cls: obj)
    # Disable __init__
    try:
        del cls.__init__
    except AttributeError:
        pass
    return cls

انظر نموذج بيانات بايثون لمزيد من التفاصيل حول __new__.

مثال:

@singleton
class Duck(object):
    pass

if Duck() is Duck():
    print "It works!"
else:
    print "It doesn't work!"

ملحوظات:

  1. يجب عليك استخدام فئات النمط الجديد (مشتقة من object) لهذا.

  2. تتم تهيئة المفردة عند تعريفها، بدلاً من المرة الأولى التي يتم استخدامها فيها.

  3. هذا مجرد مثال للعبة.لم أستخدم هذا مطلقًا في كود الإنتاج، ولا أخطط لذلك.

لست متأكدًا تمامًا من هذا الأمر، لكن مشروعي يستخدم "مفردات اصطلاحية" (وليست مفردات مفروضة)، أي إذا كان لدي فصل دراسي يسمى DataController, ، أحدد هذا في نفس الوحدة:

_data_controller = None
def GetDataController():
    global _data_controller
    if _data_controller is None:
        _data_controller = DataController()
    return _data_controller

إنها ليست أنيقة، لأنها مكونة من ستة أسطر كاملة.لكن كل ما عندي من المفردات يستخدم هذا النمط، وهو على الأقل واضح جدًا (وهو بيثوني).

ال وثائق بايثون يغطي هذا:

class Singleton(object):
    def __new__(cls, *args, **kwds):
        it = cls.__dict__.get("__it__")
        if it is not None:
            return it
        cls.__it__ = it = object.__new__(cls)
        it.init(*args, **kwds)
        return it
    def init(self, *args, **kwds):
        pass

ربما سأعيد كتابتها لتبدو أكثر مثل هذا:

class Singleton(object):
    """Use to create a singleton"""
    def __new__(cls, *args, **kwds):
        """
        >>> s = Singleton()
        >>> p = Singleton()
        >>> id(s) == id(p)
        True
        """
        self = "__self__"
        if not hasattr(cls, self):
            instance = object.__new__(cls)
            instance.init(*args, **kwds)
            setattr(cls, self, instance)
        return getattr(cls, self)

    def init(self, *args, **kwds):
        pass

يجب أن يكون نظيفًا نسبيًا لتوسيع هذا:

class Bus(Singleton):
    def init(self, label=None, *args, **kwds):
        self.label = label
        self.channels = [Channel("system"), Channel("app")]
        ...

في المرة الوحيدة التي كتبت فيها أغنية مفردة في بايثون، استخدمت فئة حيث تحتوي جميع وظائف الأعضاء على مصمم أسلوب الفصل.

class foo:
  x = 1

  @classmethod
  def increment(cls, y = 1):
    cls.x += y

هناك أيضًا بعض المقالات المثيرة للاهتمام على مدونة Google Testing، والتي تناقش سبب كون المفردة سيئة/قد تكون سيئة وأنها نمط مضاد:

يعد إنشاء مصمم ديكور فردي (المعروف أيضًا باسم تعليق توضيحي) طريقة أنيقة إذا كنت تريد تزيين (تعليق) الفصول الدراسية من الآن فصاعدا.ثم قمت فقط بوضع @singleton قبل تعريف الفصل الخاص بك.

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...

فيما يلي مثال من Python IAQ الخاص بـ Peter Norvig كيف أفعل نمط المفرد في بايثون؟ (يجب عليك استخدام خاصية البحث في متصفحك للعثور على هذا السؤال، لا يوجد رابط مباشر، آسف)

كما أن بروس إيكل لديه مثال آخر في كتابه التفكير في بايثون (مرة أخرى لا يوجد رابط مباشر للكود)

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

class NothingSpecial:
    pass

_the_one_and_only = None

def TheOneAndOnly():
    global _the_one_and_only
    if not _the_one_and_only:
        _the_one_and_only = NothingSpecial()
    return _the_one_and_only

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

class NothingSpecial:
    pass

THE_ONE_AND_ONLY = NothingSpecial()

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

تم تنفيذ نمط Singleton باستخدام Python من باب المجاملة ActiveState.

يبدو أن الحيلة تكمن في وضع الفصل الذي من المفترض أن يحتوي على مثيل واحد فقط داخل فصل دراسي آخر.

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

إذا كنت تريد حقًا فصلًا مفردًا، فهناك تطبيق معقول موصوف في الضربة الأولى على جوجل لـ "Python Singleton"، على وجه التحديد:

class Singleton:
    __single = None
    def __init__( self ):
        if Singleton.__single:
            raise Singleton.__single
        Singleton.__single = self

ويبدو أن تفعل خدعة.

حسنًا، يمكن أن يكون المفرد جيدًا أو شريرًا، أعلم.هذا هو تطبيقي، وأنا ببساطة أقوم بتوسيع النهج الكلاسيكي لإدخال ذاكرة تخزين مؤقت بالداخل وإنتاج العديد من المثيلات من نوع مختلف أو العديد من المثيلات من نفس النوع، ولكن مع وسيطات مختلفة.

لقد أسميتها Singleton_group، لأنها تجمع الحالات المتشابهة معًا وتمنع إنشاء كائن من نفس الفئة، بنفس الوسائط:

# Peppelinux's cached singleton
class Singleton_group(object):
    __instances_args_dict = {}
    def __new__(cls, *args, **kwargs):
        if not cls.__instances_args_dict.get((cls.__name__, args, str(kwargs))):
            cls.__instances_args_dict[(cls.__name__, args, str(kwargs))] = super(Singleton_group, cls).__new__(cls, *args, **kwargs)
        return cls.__instances_args_dict.get((cls.__name__, args, str(kwargs)))


# It's a dummy real world use example:
class test(Singleton_group):
    def __init__(self, salute):
        self.salute = salute

a = test('bye')
b = test('hi')
c = test('bye')
d = test('hi')
e = test('goodbye')
f = test('goodbye')

id(a)
3070148780L

id(b)
3070148908L

id(c)
3070148780L

b == d
True


b._Singleton_group__instances_args_dict

{('test', ('bye',), '{}'): <__main__.test object at 0xb6fec0ac>,
 ('test', ('goodbye',), '{}'): <__main__.test object at 0xb6fec32c>,
 ('test', ('hi',), '{}'): <__main__.test object at 0xb6fec12c>}

كل كائن يحمل ذاكرة التخزين المؤقت المفردة ...قد يكون هذا أمرًا شريرًا، لكنه يعمل بشكل رائع بالنسبة للبعض :)

class Singleton(object[,...]):

    staticVar1 = None
    staticVar2 = None

    def __init__(self):
        if self.__class__.staticVar1==None :
            # create class instance variable for instantiation of class
            # assign class instance variable values to class static variables
        else:
            # assign class static variable values to class instance variables

الحل البسيط الذي يعتمد على القيمة الافتراضية لمعلمات الوظيفة.

def getSystemContext(contextObjList=[]):
    if len( contextObjList ) == 0:
        contextObjList.append( Context() )
        pass
    return contextObjList[0]

class Context(object):
    # Anything you want here

الأخ غير الشقيق لسينجلتون

أنا أتفق تمامًا مع staale وأترك ​​هنا نموذجًا لإنشاء أخ غير شقيق:

class void:pass
a = void();
a.__class__ = Singleton

a سيتم الإبلاغ الآن على أنها من نفس فئة المفردة حتى لو لم تبدو كذلك.لذا فإن الأفراد الذين يستخدمون فصولًا معقدة ينتهي بهم الأمر بالاعتماد على أننا لا نعبث معهم كثيرًا.

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

class Singleton:
    def __new__(cls): raise AssertionError # Singletons can't have instances

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

class Singeltone(type):
    instances = dict()

    def __call__(cls, *args, **kwargs):
        if cls.__name__ not in Singeltone.instances:            
            Singeltone.instances[cls.__name__] = type.__call__(cls, *args, **kwargs)
        return Singeltone.instances[cls.__name__]


class Test(object):
    __metaclass__ = Singeltone


inst0 = Test()
inst1 = Test()
print(id(inst1) == id(inst0))

في الحالات التي لا تريد فيها الحل القائم على metaclass أعلاه، ولا تحب النهج البسيط القائم على الديكور الوظيفي (على سبيل المثال.لأنه في هذه الحالة لن تعمل الأساليب الثابتة في فئة المفردة)، يعمل هذا الحل الوسط:

class singleton(object):
  """Singleton decorator."""

  def __init__(self, cls):
      self.__dict__['cls'] = cls

  instances = {}

  def __call__(self):
      if self.cls not in self.instances:
          self.instances[self.cls] = self.cls()
      return self.instances[self.cls]

  def __getattr__(self, attr):
      return getattr(self.__dict__['cls'], attr)

  def __setattr__(self, attr, value):
      return setattr(self.__dict__['cls'], attr, value)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top