Subclass of LED

The official pyboard running MicroPython.
This is the reference design and main target board for MicroPython.
You can buy one at the store.
Target audience: Users with a pyboard.
Post Reply
smhodge
Posts: 40
Joined: Tue Jan 22, 2019 2:16 am

Subclass of LED

Post by smhodge » Sun Oct 20, 2019 11:56 pm

I wish to add local functionality to the LED class, but have run into something that baffles me. Here is code reproduced from REPL with a v1.1 pyBoard, v1.11 code.
----------------------------------------------
>>> from pyb import LED
>>> class MyLed(LED):
... def __init__(self):
... self.led1_obj = LED(1)
... self.led2_obj = LED(2)
... self.led1_obj.toggle()
... self.led2_obj.toggle()
...
>>> leds = MyLed()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function takes 1 positional arguments but 0 were given
---------------------------------------------
The leds do indeed toggle when I try to create the <leds> object (the last line). Why won't it create the object? Thanks.

User avatar
jimmo
Posts: 1156
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia

Re: Subclass of LED

Post by jimmo » Mon Oct 21, 2019 12:37 am

The simple not-very-helpful answer here is that inheriting from built-in classes can lead to some surprises in MicroPython and generally it's a bad idea. The error you're seeing is that the built-in LED.__init__ is being called with no arguments.

I'll come back to a better explanation, but I'm not sure that deriving from LED really makes sense (in Python at least). It'd be different if there was an abstract/interface LED type, so a single LEDs or a group of LEDs would implement that interface, but in Python that's just done by duck typing.

The real reason for the incompatibility here is a thing in MicroPython where if a type derives from a native type, and the __init__ method of the type doesn't explicitly call super().__init__ it injects a call to the native base's __init__ with the same arguments. (Which in this case is no arguments). So the error is coming from pyb.LED's make_new function which expects an LED number.

So you can technically work around this error by putting super().__init__(1) at the end of your __init__. And that's why you see the toggle still happen, the injected call to init (with no args) happens at the end of your __init__.

User avatar
pythoncoder
Posts: 3915
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Subclass of LED

Post by pythoncoder » Mon Oct 21, 2019 8:02 am

Unless I'm missing something your code would work if you simply removed the base class. You seem already to be using composition rather than inheritance - which is usually the correct way to work around this MicroPython limitation:

Code: Select all

from pyb import LED
class MyLed:
    def __init__(self):
        self.led1_obj = LED(1)  # composition
        self.led2_obj = LED(2)
        self.led1_obj.toggle()
        self.led2_obj.toggle()
Peter Hinch

smhodge
Posts: 40
Joined: Tue Jan 22, 2019 2:16 am

Re: Subclass of LED

Post by smhodge » Fri Oct 25, 2019 2:57 am

Yes, just eliminating the base class works fine. I haven't seen any documentation on "composition", but I get it (I think). Thanks

User avatar
pythoncoder
Posts: 3915
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Composition vs Inheritance

Post by pythoncoder » Sat Oct 26, 2019 8:50 am

Composition is the term in object oriented programming which describes the case where a class constructor creates a bound variable which is an instance of another object:

Code: Select all

class Foo:
    def __init__(self):
        self.bar = Bar()
Foo "has an instance" of Bar. Contrast with inheritance:

Code: Select all

class Foo(Bar):
    def __init__(self):
        super().__init__()
Here Foo "is an instance" of Bar.

The MicroPython restriction on subclassing built-in types can usually be overcome using composition.
Peter Hinch

Post Reply