README improvements.

pull/15/head
Peter Hinch 2020-07-07 15:47:14 +01:00
rodzic 61d312be15
commit 2399918b38
2 zmienionych plików z 68 dodań i 19 usunięć

Wyświetl plik

@ -227,12 +227,10 @@ may readily be added as required.
Two simple class decorators for objects useful in hardware interfacing. Two simple class decorators for objects useful in hardware interfacing.
Documented [here](./functor_singleton/README.md). Documented [here](./functor_singleton/README.md).
Singletons denote classes for which only a single instance will ever occur. Singletons denote classes for which only a single instance can ever occur.
They are contentious in some circles, on the grounds that the single instance They can be useful for hardware interface classes. Their use avoids the need
guarantee may be violated in a specification change. They can be useful in for a global instance: the sole instance may be retrieved efficiently from
hardware contexts where a chip design is unlikely suddenly to change. anywhere in the code.
Singletons denoting hardware interfaces avoid globals and the need to pass
references around.
A functor is a class which is accessed via function call syntax. There is only A functor is a class which is accessed via function call syntax. There is only
one instance, like a singleton. Initial access calls the constructor, with one instance, like a singleton. Initial access calls the constructor, with

Wyświetl plik

@ -1,14 +1,23 @@
# Singletons and Functors
These closely related concepts describe classes which support only a single
instance. They share a common purpose of avoiding the need for global data by
providing a callable capable of retaining state and whose scope may be that of
the module.
In both cases implementation is via very similar class decorators.
# Singleton class decorator # Singleton class decorator
A singleton is a class which has only one instance. IT gurus debate whether A singleton is a class with only one instance. Some IT gurus argue against them
they should be used, mainly on the grounds that project aims can change: a need on the grounds that project aims can change: a need for multiple instances may
for multiple instances may arise later. I would argue that singleton classes arise later. My view is that they have merit in defining interfaces to hardware
have merit in defining interfaces to hardware objects. You can be sure that objects. You might be quite certain that your brometer will have only one
(for example) a Pyboard D will only have one RTC instance. pressure sensor.
The advantage of a singleton is that it removes the need for a global instance The advantage of a singleton is that it removes the need for a global instance
or for passing an instance between functions. The sole instance is retrieved at or for passing an instance between functions. The sole instance is efficiently
any point in the code using constructor call syntax. retrieved at any point in the code using function call syntax.
```python ```python
def singleton(cls): def singleton(cls):
@ -33,8 +42,8 @@ ms = MySingleton(42) # prints 'In __init__ 42'
x = MySingleton() # No output: assign existing instance to x x = MySingleton() # No output: assign existing instance to x
x.foo(5) # prints 'In foo 47': original state + 5 x.foo(5) # prints 'In foo 47': original state + 5
``` ```
The first instantiation sets the object's initial state. Thereafter The first call instantiates the object and sets its initial state. Subsequent
'instantiations' retrieve the original object. calls retrieve the original object.
There are other ways of achieving singletons. One is to define a (notionally There are other ways of achieving singletons. One is to define a (notionally
private) class in a module. The module API contains an access function. There private) class in a module. The module API contains an access function. There
@ -73,7 +82,49 @@ class MyFunctor:
MyFunctor(42) # prints 'In __init__ 42' MyFunctor(42) # prints 'In __init__ 42'
MyFunctor(5) # 'In __call__ 47' MyFunctor(5) # 'In __call__ 47'
``` ```
A use case is in asynchronous programming. The constructor launches a task: A use case is in asynchronous programming. The constructor launches a
this will only occur once. Subsequent calls might alter the behaviour of that continuously running task. Subsequent calls alter the behaviour of that task.
task. An example may be found The following simple example has the task waiting for a period which can be
[in the Latency class](https://github.com/peterhinch/micropython-async/blob/master/lowpower/rtc_time.py). changed at runtime:
```python
import uasyncio as asyncio
import pyb
def functor(cls):
instance = None
def getinstance(*args, **kwargs):
nonlocal instance
if instance is None:
instance = cls(*args, **kwargs)
return instance
return instance(*args, **kwargs)
return getinstance
@functor
class FooFunctor:
def __init__(self, led, interval):
self.led = led
self.interval = interval
asyncio.create_task(self._run())
def __call__(self, interval):
self.interval = interval
async def _run(self):
while True:
await asyncio.sleep_ms(self.interval)
# Do something useful here
self.led.toggle()
def go_fast(): # FooFunctor is available anywhere in this module
FooFunctor(100)
async def main():
FooFunctor(pyb.LED(1), 500)
await asyncio.sleep(3)
go_fast()
await asyncio.sleep(3)
asyncio.run(main())
```