هل هناك طريقة بسيطة وأنيقة لتحديد المفردات؟[ينسخ]
-
09-06-2019 - |
سؤال
هذا السؤال لديه بالفعل إجابة هنا:
- إنشاء مفردة في بيثون 20 إجابة
يبدو أن هناك طرقًا عديدة للتعريف مفردات في بايثون.هل هناك رأي إجماعي بشأن 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!"
ملحوظات:
يجب عليك استخدام فئات النمط الجديد (مشتقة من
object
) لهذا.تتم تهيئة المفردة عند تعريفها، بدلاً من المرة الأولى التي يتم استخدامها فيها.
هذا مجرد مثال للعبة.لم أستخدم هذا مطلقًا في كود الإنتاج، ولا أخطط لذلك.
لست متأكدًا تمامًا من هذا الأمر، لكن مشروعي يستخدم "مفردات اصطلاحية" (وليست مفردات مفروضة)، أي إذا كان لدي فصل دراسي يسمى 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)