Er det en enkel, elegant måte å definere enkeltfødte?

stemmer
378

Det synes å være mange måter å definere enkeltfødte i Python. Er det en konsensus mening om Stack Overflow?

Publisert på 28/08/2008 klokken 09:03
kilden bruker
På andre språk...                            


22 svar

stemmer
311

Jeg forstår ikke helt se behovet, som en modul med funksjoner (og ikke en klasse) ville tjene godt som en enkelt. Alle variablene ville være bundet til modulen, som ikke kunne startes gjentatte ganger uansett.

Hvis du ønsker å bruke en klasse, er det ingen måte å skape private klasser eller private konstruktører i Python, så du kan ikke beskytte mot flere instantiations, annet enn bare via konvensjonen i bruk av API din. Jeg vil likevel bare sette metoder i en modul, og vurdere modulen som enkelt.

Svarte 28/08/2008 kl. 09:10
kilden bruker

stemmer
270

Her er min egen gjennomføring av enkeltfødte. Alt du trenger å gjøre er å dekorere klasse; å få enkelt, du må da bruke Instancemetoden. Her er et eksempel:

@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

Og her er koden:

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)
Svarte 08/09/2011 kl. 09:46
kilden bruker

stemmer
173

Du kan overstyre __new__metode som dette:

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"
Svarte 27/11/2009 kl. 19:37
kilden bruker

stemmer
103

En litt annen tilnærming til å implementere enkelt inne Python er borg mønster av Alex Martelli (Google-ansatt og Python geni).

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

Så i stedet for å tvinge alle tilfeller å ha samme identitet, de deler tilstand.

Svarte 28/08/2008 kl. 14:53
kilden bruker

stemmer
75

Modulen tilnærmingen fungerer godt. Hvis jeg trenger absolutt en singleton jeg foretrekker Metaclass tilnærming.

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
Svarte 28/08/2008 kl. 19:39
kilden bruker

stemmer
41

Se denne implementeringen fra PEP318 , gjennomføring av Singleton mønster med en dekoratør:

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

@singleton
class MyClass:
    ...
Svarte 02/05/2010 kl. 02:47
kilden bruker

stemmer
26

Som aksepterte svaret sier, er den mest idiomatiske måte å bare bruke en modul .

Med det i tankene, her er en proof of concept:

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

Se Python datamodell for mer informasjon om __new__.

Eksempel:

@singleton
class Duck(object):
    pass

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

Merknader:

  1. Du må bruke nye stil klasser (utlede fra object) for dette.

  2. Singleton er initialisert når den er definert, i stedet for første gang den er brukt.

  3. Dette er bare et leketøy eksempel. Jeg har aldri brukt dette i produksjonskode, og ikke har tenkt på.

Svarte 12/10/2012 kl. 00:13
kilden bruker

stemmer
14

Den ene gangen jeg skrev et enkelt inne Python jeg brukte en klasse hvor alle medlemsfunksjoner hadde classmethod dekoratør.

class foo:
  x = 1

  @classmethod
  def increment(cls, y = 1):
    cls.x += y
Svarte 29/08/2008 kl. 19:15
kilden bruker

stemmer
12

Den Python dokumentasjon gjør dekke dette:

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

Jeg ville trolig skrive det til å se mer ut som dette:

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

Det bør være relativt ren for å utvide dette:

class Bus(Singleton):
    def init(self, label=None, *args, **kwds):
        self.label = label
        self.channels = [Channel("system"), Channel("app")]
        ...
Svarte 17/07/2012 kl. 06:38
kilden bruker

stemmer
12

Jeg er veldig usikker på dette, men mine prosjektet bruker 'konvensjonen enkeltfødte' (ikke håndheves enkeltfødte), er at hvis jeg har en klasse som heter DataController, jeg definere dette i samme modul:

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

Det er ikke elegant, siden det er hele seks linjer. Men alle mine enkeltfødte bruke dette mønsteret, og det er i hvert fall veldig eksplisitt (som er Pytonske).

Svarte 22/08/2009 kl. 00:44
kilden bruker

stemmer
9

Opprette en singleton dekoratør (aka en merknad) er en elegant måte hvis du ønsker å dekorere (MERKNAD) klasser fremover. Da har du bare sette @singleton før klassen din definisjon.

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

@singleton
class MyClass:
    ...
Svarte 22/02/2012 kl. 22:16
kilden bruker

stemmer
9

Det er også noen interessante artikler om Googles test bloggen, diskutere hvorfor Singleton er / kan være dårlig, og er en anti-mønster:

Svarte 15/09/2008 kl. 07:47
kilden bruker

stemmer
7

Her er et eksempel fra Peter Norvig er Python IAQ Hvordan gjør jeg det Singleton Pattern i Python? (Du bør bruke søkefunksjonen i nettleseren din for å finne dette spørsmålet, er det ingen direkte kobling, sorry)

Også Bruce Eckel har et annet eksempel i sin bok Thinking i Python (igjen er det ingen direkte kobling til koden)

Svarte 28/08/2008 kl. 09:13
kilden bruker

stemmer
5

Jeg tror at å tvinge en klasse eller en forekomst å være singel er overkill. Personlig liker jeg å definere en normal en forekomst av klassen, en semi-private referanse, og en enkel fabrikk funksjon.

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

Eller hvis det er ingen problem med forekomster når modulen er først importert:

class NothingSpecial:
    pass

THE_ONE_AND_ONLY = NothingSpecial()

På den måten kan du skrive tester mot nye tilfeller uten bivirkninger, og det er ikke behov for å strø modulen med globale uttalelser, og om nødvendig kan utlede varianter i fremtiden.

Svarte 27/07/2011 kl. 12:00
kilden bruker

stemmer
3

OK, Singleton kan være gode eller onde, vet jeg. Dette er min gjennomføring, og jeg bare forlenge en klassisk tilnærming til å innføre en cache inne og produsere mange tilfeller av en annen type eller, mange tilfeller av samme type, men med forskjellige argumenter.

I kalte det Singleton_group, fordi det grupper liknende forekomster sammen og forhindre at en gjenstand av samme klasse, med samme argumenter, kunne opprettes:

# 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>}

Hvert objekt bærer av Singleton cache ... Dette kan være onde, men det fungerer bra for noen :)

Svarte 28/02/2012 kl. 20:42
kilden bruker

stemmer
3

The Singleton Pattern gjennomført med Python høflighet av Active.

Det ser ut som trikset er å sette den klassen som er ment å bare ha en forekomst på innsiden av en annen klasse.

Svarte 03/09/2008 kl. 20:54
kilden bruker

stemmer
2

Singleton halvbror

Jeg er helt enig med Ståle og jeg forlater her et eksempel på å skape en Singleton halvbror:

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

arapporterer nå å være av samme klasse som singel selv om det ikke ser ut som det. Så enkeltfødte bruker kompliserte klasser ende opp avhengig vi ikke rotet mye med dem.

Å være så, kan vi ha samme effekt og bruke enklere ting som en variabel eller en modul. Likevel, hvis vi ønsker å bruke klasser for klarhet og fordi i Python en klasse er et objekt , så vi har allerede objektet (ikke og instans, men det vil gjøre akkurat som).

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

Det har vi en fin påstand feil hvis vi prøver å opprette en forekomst, og vi kan lagre på avledninger statiske medlemmer og gjøre endringer i dem under kjøring (jeg elsker Python). Dette objektet er så god som andre om halvbrødre (du fortsatt kan lage dem hvis du ønsker det), men det har en tendens til å kjøre raskere på grunn av enkelhet.

Svarte 11/01/2013 kl. 15:10
kilden bruker

stemmer
2

Min enkel løsning som er basert på standardverdien på funksjonsparametre.

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

class Context(object):
    # Anything you want here
Svarte 28/11/2011 kl. 13:15
kilden bruker

stemmer
2
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
Svarte 17/12/2009 kl. 15:10
kilden bruker

stemmer
2

Å være relativt ny i Python Jeg er ikke sikker på hva den vanligste idiom er, men den enkleste ting jeg kan tenke på er bare å bruke en modul i stedet for en klasse. Hva ville ha vært instansmetoder på din klasse blir bare fungerer i modulen og alle data blir bare variabler i modulen i stedet for medlemmer av klassen. Jeg mistenker at dette er den Pytonske tilnærming for å løse den type problem at folk bruker enkeltfødte for.

Hvis du virkelig vil ha en singleton klasse, det er en rimelig implementering beskrevet på første treffet på Google etter "Python singel", spesielt:

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

Det ser ut til å gjøre triks.

Svarte 28/08/2008 kl. 09:09
kilden bruker

stemmer
1
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))
Svarte 15/08/2012 kl. 07:08
kilden bruker

stemmer
0

I tilfeller der du ikke vil at metaclass-basert løsning ovenfor, og du ikke liker den enkle funksjonen dekoratør basert tilnærming (for eksempel fordi i så fall statiske metoder på singleton klassen vil ikke fungere), dette kompromisset fungerer:

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)
Svarte 28/10/2011 kl. 09:52
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more