6 ms·
I blame GOF for making Python Standard Libs hard. The patterns described were for an OO system where functions were not first class. Python didn't need to be co
by teyc 15y ago
I blame GOF for making Python Standard Libs hard. The patterns described were for an OO system where functions were not first class. Python didn't need to be complicated.
If you have a look at the older libraries, most of them were written in a procedural style. Not only that, it is very amenable to testing in the REPL.
import smtplib
s=smtplib.SMTP("localhost")
s.sendmail("me@my.org",tolist,msg)
note the absence of doers like "Adapters", "Handler", "Manager", "Factory"
If you have a look at the XML library, roughly when "patterns" became popular, this style of thinking infested standard library contributions. It also coincides with a time when camelCased function names crept into the python standard library.
Here's one in xml/dom/pulldom.py:
self.documentFactory = documentFactory
Once you see this, you know you are in for some subclassing. You can no longer REPL your way to figure out how things work, and you now have to consult the manual.
Here's more pain from libraries of the same era, some of these I'd argue un-Pythonic:
#xml/sax/xmlreader.py:
def setContentHandler(self, handler):
#wsgiref/simple_server.py:
class ServerHandler(SimpleHandler):
#urllib2.py:
class HTTPDigestAuthHandler(BaseHandler,
AbstractDigestAuthHandler):
The last example is especially jarring. Abstract classes have a place in strongly typed world to declare interfaces, and help with vtable-style dispatch. In Python, where you have duck-typing and monkey patching, a class that virtually "does nothing" on its own stands out like a guy in a tux at a beach party.
Even logging is infected by the same over-patterning. logging/__init__.py:
class StreamHandler(Handler)
LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
"Managers" - what a pain when plain function handles would have done the job. Does this name even tell you what task the class performs?
#multiprocessing/managers.py:
class BaseManager(object)
If anyone remembers, Java had to do OO in a big-style with OO everywhere -- there were no alternatives.
Initially, buttons had to be subclassed just to handle click events, since functions were not first class objects. Then someone came up with a MouseListener interface, which proved too unwieldy to handle a single click. So the MouseEventAdapters came into being.
Therefore, to handle a click in a "pattern" manner involves
an anonymous class
which subclasses MouseAdapter
which implements MouseListener,
which overrides MouseClick.
Publishing how industry solves this problem of "MouseClick" over and over as a pattern [design pattern is a general reusable solution to a commonly occurring problem within a given context in software design] only gives legitimacy to an approach that has dubious wider applicability.
Heavens help the future developers who are forced to do it because it is now recognized as being industrially "good practice" and codified in a reknowned book.
It isn't!
It was a style that was forced by the constraints of a language.
This is neither pythonic nor necessary:
panel.addMouseListener
(
new MouseAdapter ()
{
public void mouseEntered (MouseEvent e) {
System.out.println (e.toString ());
}
}
);
Embracing "foolish, unschooled" thinking, this would be rendered in Python as:
def mouseEntered(event):
print event
panel.mouseEntered = mouseEntered
or for multiple event handlers
panel.mouseEntered.append(mouseEntered)
This style of API again allows effective exploration on the REPL.
- peter_l_downs 15y agoI'm making an attempt [1] at simple logging. Thoughts on the basic design so far? [1] https://github.com/peterldowns/lggr https://github.com/peterldowns/lggr
- teyc 15y agoThe acid test is to see if you can avoid terms like "Writer", "Printer", and whether someone is able to explore your API using the REPL. >>> import log >>> log.log(log.CRITICAL, 'PrintModule', 'Printer failure', sys.exc_info()) PrintModule: Printer failure >>> print log.configuration [<function printToStdErr as 0xcafebad>] >>> log.configuration.clear() >>> print log.configuration [] >>> def send_mail(level, module, message, exception): ... import smtplib ... smptlib.SMTP('mail.test.com').send('syslog@test.com', ... 'no-reply@mysystem.com', module + ' ' + message) ... >>> log.configuration.append(send_mail) >>> log.log(log.CRITICAL, 'PrintModule', 'Printer failure', sys.exc_info()) etc. Next, configuration must be straightforward. >>> import log >>> log.configuration.extend([ ... log.sendmail, ... log.syslog, ... log.rotatinglog]) ... >>> log.sendmail.to = 'admin@test.com' Notice how this can easily be captured into a file you might name as logconfig.py You might be wondering how to assign an attribute to a function, like sendmail.to? In this particular case, sendmail is actually a class that masquerades as a function. >>> class Sendmail: ... to = None ... ... def __call__(self, level, module, msg, exc): ... # et. c ... >>> sendmail = Sendmail() This way, the most common use-case, i.e. to be notified when something happens is easy to apply, while more complicated ones are only a short step away.
- peter_l_downs 15y agoThanks. I like the idea of classes masquerading as functions. And easier configuration. I had actually started writing this 2 days ago as something just for me, but after reading this decided to try something new.
- jacobolus 15y ago