Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #109499 > unrolled thread
| Started by | Mark Summerfield <list@qtrac.plus.com> |
|---|---|
| First post | 2016-06-04 23:55 -0700 |
| Last post | 2016-06-05 13:59 -0400 |
| Articles | 6 — 5 participants |
Back to article view | Back to comp.lang.python
Spreading a class over multiple files Mark Summerfield <list@qtrac.plus.com> - 2016-06-04 23:55 -0700
Re: Spreading a class over multiple files Gary Herron <gherron@digipen.edu> - 2016-06-05 00:18 -0700
Re: Spreading a class over multiple files Mark Summerfield <list@qtrac.plus.com> - 2016-06-05 01:14 -0700
Re: Spreading a class over multiple files Steven D'Aprano <steve@pearwood.info> - 2016-06-05 20:57 +1000
Re: Spreading a class over multiple files Peter Otten <__peter__@web.de> - 2016-06-05 09:40 +0200
Re: Spreading a class over multiple files Terry Reedy <tjreedy@udel.edu> - 2016-06-05 13:59 -0400
| From | Mark Summerfield <list@qtrac.plus.com> |
|---|---|
| Date | 2016-06-04 23:55 -0700 |
| Subject | Spreading a class over multiple files |
| Message-ID | <f8f1cc30-3274-4274-9e28-815b32b4e27c@googlegroups.com> |
Sometimes I want to spread a class over multiple files.
My primary use case is when I create a "Model" class to reflect an entire SQL database. I want a model instance to provide a single point of access to
the database, but the database has many tables each requiring its own methods since they differ in their structure, purpose, validation needs, etc.
A secondary use case is when I create "MainWindow" classes in GUI programming and have lots of methods to reflect all the actions (e.g., menu options
and toolbar actions, plus interaction with the main widget(s)).
To meet these needs I've devised an approach that I think is easy to use and understand and which doesn't use any tricky or hard to maintain code.
My question is -- are there nicer/better ways to achieve this?
Here's a summary of my approach. A fuller discussion is on my website:
https://www.qtrac.eu/pyclassmulti.html
# Lib.py
# This provides the two functions (both decorators) used to support my approach
def add_methods_from(*modules):
def decorator(Class):
for module in modules:
for method in getattr(module, "__methods__"):
setattr(Class, method.__name__, method)
return Class
return decorator
def register_method(methods): # A decorator used purely for its side-effect
def register_method(method):
methods.append(method)
return method # Unchanged and not strictly necessary
return register_method
# Model.py
# This provides my model but some methods are in separate files
import Lib
import _ModelConfig
import _ModelOutput
@Lib.add_methods_from(_ModelConfig, _ModelOutput)
class Model:
...
def small_method(self):
...
# _ModelConfig.py # _ModelOutput has the same structure so not shown
import Lib
__methods__ = [] # self is a Model
register_method = Lib.register_method(__methods__)
@register_method
def config(self):
...
So, that's the overall pattern of my solution.
Is there a nicer/better way? Could I cleanly avoid the explicit imports (e.g., import _ModelConfig), without resorting to stack frame hacks or similar?
[toc] | [next] | [standalone]
| From | Gary Herron <gherron@digipen.edu> |
|---|---|
| Date | 2016-06-05 00:18 -0700 |
| Message-ID | <mailman.4.1465111092.2306.python-list@python.org> |
| In reply to | #109499 |
On 06/04/2016 11:55 PM, Mark Summerfield wrote:
> Sometimes I want to spread a class over multiple files.
There’s and easy way to do this in Python using what's called a Mixin
class and (multiple) inheritance:
(See https://en.wikipedia.org/wiki/Mixin for more information.)
In one file, say extras.py
class ExtraMethodsMixin:
def extra_1(...):
...
def extra_2(...):
...
In the main class file:
from extras import ExtraMethodsMixin
class MainClass(ExtraMethodsMixin):
def __init__(...):
...
# and so on
The result will be essentially the same as if all three methods were
defined in MainCLass.
Gary Herron
--
Dr. Gary Herron
Professor of Computer Science
DigiPen Institute of Technology
(425) 895-4418
>
> My primary use case is when I create a "Model" class to reflect an entire SQL database. I want a model instance to provide a single point of access to
> the database, but the database has many tables each requiring its own methods since they differ in their structure, purpose, validation needs, etc.
>
> A secondary use case is when I create "MainWindow" classes in GUI programming and have lots of methods to reflect all the actions (e.g., menu options
> and toolbar actions, plus interaction with the main widget(s)).
>
> To meet these needs I've devised an approach that I think is easy to use and understand and which doesn't use any tricky or hard to maintain code.
>
> My question is -- are there nicer/better ways to achieve this?
>
> Here's a summary of my approach. A fuller discussion is on my website:
> https://www.qtrac.eu/pyclassmulti.html
>
> # Lib.py
> # This provides the two functions (both decorators) used to support my approach
> def add_methods_from(*modules):
> def decorator(Class):
> for module in modules:
> for method in getattr(module, "__methods__"):
> setattr(Class, method.__name__, method)
> return Class
> return decorator
>
> def register_method(methods): # A decorator used purely for its side-effect
> def register_method(method):
> methods.append(method)
> return method # Unchanged and not strictly necessary
> return register_method
>
> # Model.py
> # This provides my model but some methods are in separate files
> import Lib
> import _ModelConfig
> import _ModelOutput
>
> @Lib.add_methods_from(_ModelConfig, _ModelOutput)
> class Model:
> ...
> def small_method(self):
> ...
>
> # _ModelConfig.py # _ModelOutput has the same structure so not shown
> import Lib
>
> __methods__ = [] # self is a Model
> register_method = Lib.register_method(__methods__)
>
> @register_method
> def config(self):
> ...
> So, that's the overall pattern of my solution.
>
> Is there a nicer/better way? Could I cleanly avoid the explicit imports (e.g., import _ModelConfig), without resorting to stack frame hacks or similar?
[toc] | [prev] | [next] | [standalone]
| From | Mark Summerfield <list@qtrac.plus.com> |
|---|---|
| Date | 2016-06-05 01:14 -0700 |
| Message-ID | <beaa0b24-8200-4c5f-940f-51709cd40241@googlegroups.com> |
| In reply to | #109500 |
You're quite right! For some reason I have a blind-spot about mixins, but they are the perfect solution. Thanks:-)
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve@pearwood.info> |
|---|---|
| Date | 2016-06-05 20:57 +1000 |
| Message-ID | <575405ab$0$1583$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #109505 |
On Sun, 5 Jun 2016 06:14 pm, Mark Summerfield wrote:
> You're quite right! For some reason I have a blind-spot about mixins, but
> they are the perfect solution. Thanks:-)
They really aren't :-)
Just yesterday on an unrelated thread I linked to a long discussion about
multiple inheritance, mixins and traits. See links here:
https://mail.python.org/pipermail/python-list/2016-June/709808.html
To my mind, if you have to split a class over multiple files, it probably
does too much. The "God Class" that Peter referred to is an anti-pattern:
https://en.wikipedia.org/wiki/God_object
but if you still need to split your class over multiple files, this is how I
would do it:
# file a.py
class Zeus_King_Of_The_Gods: # *wink*
from b import throw_lightening
from c import turn_into_a_shower_of_gold
def turn_into_animal(self, animal='bull'):
...
# file b.py
def throw_lightening(self):
...
# file c.py
def turn_into_a_shower_of_gold(self):
...
Notice that in files b and c you define the functions with an explicit self,
even though they are at the top level.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2016-06-05 09:40 +0200 |
| Message-ID | <mailman.6.1465112705.2306.python-list@python.org> |
| In reply to | #109499 |
Mark Summerfield wrote: > Sometimes I want to spread a class over multiple files. > > My primary use case is when I create a "Model" class to reflect an entire > SQL database. I want a model instance to provide a single point of access > to > the database, but the database has many tables each requiring its own > methods since they differ in their structure, purpose, validation needs, > etc. In other words, a God Class http://c2.com/cgi/wiki?GodClass > My question is -- are there nicer/better ways to achieve this? Use composition.
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2016-06-05 13:59 -0400 |
| Message-ID | <mailman.14.1465149579.2306.python-list@python.org> |
| In reply to | #109499 |
On 6/5/2016 2:55 AM, Mark Summerfield wrote:
> Sometimes I want to spread a class over multiple files.
My experience with trying to work with two do-all classes in idlelib has
engendered a dislike for such. It is hard to find things in a
kitchen-sink class. To switch IDLE from being a multi-window
application to having multiple panes in a single window, both must be
refactored.
> My primary use case is when I create a "Model" class to reflect an
> entire SQL database. I want a model instance to provide a single
> point of access to the database, but the database has many tables
> each requiring its own methods since they differ in their structure,
> purpose, validation needs, etc.
I would consider a master database ('Model') class, a base table class,
and a subclass class for each table or group of similar tables.
> A secondary use case is when I create "MainWindow" classes in GUI
> programming and have lots of methods to reflect all the actions
> (e.g., menu options and toolbar actions, plus interaction with the
> main widget(s)).
With tk and tkinter, at least, implementation functions do not have to
be in the same file as the menu or toolbar definitions.
--
Terry Jan Reedy
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web