0

I happened to read the following code yesterday (I cannot guarantee this is a valid code):

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

@singleton
class MyClass:
  ...

Looks like a function singleton is designed to decorate a class MyClass. I understand the simple and standard decorator which, as a function, decorates a function. Like this:

def bold(func):
    def wrapper():
        return '<b>'+func()+'</b>'
    return wrapper

@bold
def test():
    return 'This is a test'

But I can't really get how the function-decorates-class thing works. Can anyone provide a more detailed example?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
joe wong
  • 453
  • 2
  • 9
  • 24
  • What precisely don't you understand? In the same way that the first argument to `bold` is the function object it decorates, the first argument to `singleton` is the class object it decorates. How would more detail help? – jonrsharpe Mar 22 '17 at 02:12
  • Exactly ask you have described. The important point to note here is that a decorator is shorthand for `test = bold(test)` and `MyClass = singleton(MyClass)` – Malik Brahimi Mar 22 '17 at 02:16
  • You are essentially passing the decorated construct (class or function or what have you) into a wrapper function and reassigning that very construct. – Malik Brahimi Mar 22 '17 at 02:18
  • Possible duplicate of [Python Class Decorator](http://stackoverflow.com/questions/681953/python-class-decorator) – John Coleman Mar 22 '17 at 02:23
  • @MalikBrahimi So if I type `a = MyClass()`, does it mean I'm constructing a new instance of `MyClass`, or I'm calling the function `getinstance()`? – joe wong Mar 22 '17 at 03:05
  • Whenever you call `MyClass()`, you're really calling a copy of `getinstance()` and it returns a new instance the first time, and the old instance each time afterwards. You're setting `MyClass` in your namespace to a copy of `getinstance()` that returns a `MyClass` instance. – Harvey Mar 22 '17 at 03:09
  • Related question... why is `instances` using a dict? Will/Can `getinstance()` ever be called with any value of `cls` that isn't `MyClass`? – Harvey Mar 22 '17 at 03:13
  • @Harvey So that means in this case, `a = MyClass()` calls `getinstance()` and still returns a new instance of `MyClass`? If `MyClass` wasn't decorated, `a = MyClass()` would've returned a new instance too. Is it the same result? – joe wong Mar 22 '17 at 03:22
  • It looks in the `instances` dictionary for a key with the value of the `cls`, the type of the class you decorated. If the key exists, it returns the value for that key which is a previously created instance. If not found, it creates a new MyClass instance, saves it in `instances` and then returns that. So, there's only one instance of MyClass ever created. – Harvey Mar 22 '17 at 03:26
  • @jonrsharpe Each new decoration uses a brand new dictionary which means it only ever has at most one key. – Harvey Mar 23 '17 at 12:36
  • @Harvey oh you're right, sorry. – jonrsharpe Mar 23 '17 at 12:53

0 Answers0