Extend System Module By Subclassing

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
User avatar
devnull
Posts: 394
Joined: Sat Jan 07, 2017 1:52 am
Location: Singapore / Cornwall
Contact:

Extend System Module By Subclassing

Post by devnull » Sun Apr 14, 2019 11:45 pm

I realise that this is more of a generic python question rather than micropython specific, but how do I subclass and then add new methods ?

Is it possible to add new methods as well as replace existing ones:

Code: Select all

from network import WLAN
class wlan(WLAN):
    def __init__(self):
        super().__init__()

	def scan(self):
		print('replace original scanning method')
		super().scan()

	def xscan(self):
		print('new scan method using sub method')
		super().scan()

wlan().xscan()
wlan().scan()
Last edited by devnull on Tue Apr 16, 2019 11:44 pm, edited 1 time in total.

Online
kevinkk525
Posts: 501
Joined: Sat Feb 03, 2018 7:02 pm

Re: Extend a Module By Subclassing

Post by kevinkk525 » Mon Apr 15, 2019 5:51 am

Theoretically it is but I'd be very careful. The built-in c classes often don't like being subclassed and are not working as expected. An example might be machine.Pin which doesn't conform to a typical python class and makes subclassing kind of impossible.
Kevin Köck

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

Re: Extend a Module By Subclassing

Post by pythoncoder » Mon Apr 15, 2019 9:48 am

Yes. There is no problem subclassing a class you have created and, in the subclass, adding new methods. But as Kevin says, subclassing built-in types like int, dict or WLAN is not supported by MicroPython - see this doc.

Only subclass a built-in type if it has specifically been designed for this purpose. The only example of which I'm aware is framebuf.
Peter Hinch

User avatar
tuupola
Posts: 54
Joined: Sun Sep 17, 2017 12:10 am
Contact:

Re: Extend a Module By Subclassing

Post by tuupola » Tue Apr 16, 2019 1:33 pm

You could use composition instead of inheritance. Personally I always prefer composition.

Code: Select all

class wlan:
    def __init__(self, station):
        self._station = station

	def scan(self):
		print('replace original scanning method')
		return self._station.scan()

	def xscan(self):
		print('new scan method using sub method')
		return self._station.scan()
Which you could then use with something like:

Code: Select all

from network import WLAN, STA_IF
original = WLAN(STA_IF)
custom = wlan(original)

custom.xscan()
custom.scan()

User avatar
devnull
Posts: 394
Joined: Sat Jan 07, 2017 1:52 am
Location: Singapore / Cornwall
Contact:

Re: Extend a Module By Subclassing

Post by devnull » Tue Apr 16, 2019 11:34 pm

Hmm. interesting was not aware of the composition approach, but I guess that custom() will not contain all of the original methods and variables from WLAN() in your example, and that I would need to redefine / link them all if required ?

i.e.

Code: Select all

class wlan:
    def __init__(self, station):
        self._station = station
        self.config = station.config
        self.connect = station.connect
        self.isconnected = station.isconnected
        
	def scan(self):
		print('replace original scanning method')
		return self._station.scan()

	def xscan(self):
		print('new scan method using sub method')
		return self._station.scan()

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

Re: Extend System Module By Subclassing

Post by pythoncoder » Wed Apr 17, 2019 5:11 am

Yes, that is the reason why you might prefer inheritance over composition (if it were available). An example is the framebuf class which does allow inheritance. If you write a driver for specific display hardware such that it subclasses framebuf you get a set of fast C graphics primitives; all for zero programming effort and without runtime overhead.
Peter Hinch

User avatar
tuupola
Posts: 54
Joined: Sun Sep 17, 2017 12:10 am
Contact:

Re: Extend a Module By Subclassing

Post by tuupola » Wed Apr 17, 2019 7:57 am

devnull wrote:
Tue Apr 16, 2019 11:34 pm
Hmm. interesting was not aware of the composition approach, but I guess that custom() will not contain all of the original methods and variables from WLAN() in your example, and that I would need to redefine / link them all if required ?
You can use __getattr__ magic. It should work for both attributes and function.

Code: Select all

class wlan:
    def __init__(self, station):
        self._station = station

	def __getattr__(self, name):
            return getattr(self._station, name)
        
	def scan(self):
		print('replace original scanning method')
		return self._station.scan()

	def xscan(self):
		print('new scan method using sub method')
		return self._station.scan()

User avatar
devnull
Posts: 394
Joined: Sat Jan 07, 2017 1:52 am
Location: Singapore / Cornwall
Contact:

Re: Extend System Module By Subclassing

Post by devnull » Wed Apr 17, 2019 10:10 am

That's very neat, but this does not appear to work:

Code: Select all

from network import WLAN, STA_IF
original = WLAN(STA_IF)
custom = wlan(original)

custom.xscan()
custom.scan()
I have to call:

Code: Select all

custom._station.scan()

User avatar
tuupola
Posts: 54
Joined: Sun Sep 17, 2017 12:10 am
Contact:

Re: Extend System Module By Subclassing

Post by tuupola » Wed Apr 17, 2019 1:38 pm

Tested with real device and works for me with ESP32. Note that __getattr__ is called only when accessing an undefined attribute. Here is a bit cleaned up code:

Code: Select all

import network

class wlan:
    def __init__(self, mode):
        self._station = network.WLAN(mode)
        self._station.active(True)

    def __getattr__(self, name):
        return getattr(self._station, name)
Tested from REPL:

Code: Select all

MicroPython ESP32_LoBo_v3.2.24 - 2018-09-06 on ESP32 board with ESP32
Type "help()" for more information.
>>> import network
>>> from wlan import wlan
>>>
>>> wifi = wlan(network.STA_IF)
>>> wifi.scan
<bound_method>
>>> wifi.scan()
[(b'Manny-3BB', b'd\x12l\x00\xf1\xf0', 1, -79, 4, 'WPA_WPA2_PSK', False), (b'Myroom119819', b'\x04OLc7H', 8, -79, 4, 'WPA_WPA2_PSK', False), (b'Fcape1', b'\x03\x17\xd6\xf5s=', 6, -82, 0, 'OPEN', False),... rest removed]

Post Reply