Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #86637 > unrolled thread

suggestions for functional style (singleton pattern?)

Started byyves@zioup.com
First post2015-02-28 16:12 -0700
Last post2015-02-28 22:23 -0800
Articles 16 — 8 participants

Back to article view | Back to comp.lang.python


Contents

  suggestions for functional style (singleton pattern?) yves@zioup.com - 2015-02-28 16:12 -0700
    Re: suggestions for functional style (singleton pattern?) Michael Torrie <torriem@gmail.com> - 2015-02-28 19:19 -0700
      Re: suggestions for functional style (singleton pattern?) yves@zioup.com - 2015-02-28 21:11 -0700
        Re: suggestions for functional style (singleton pattern?) Michael Torrie <torriem@gmail.com> - 2015-02-28 22:14 -0700
    Re: suggestions for functional style (singleton pattern?) Mario Figueiredo <marfig@gmail.com> - 2015-03-01 04:45 +0100
      Re: suggestions for functional style (singleton pattern?) yves@zioup.com - 2015-02-28 21:29 -0700
        Re: suggestions for functional style (singleton pattern?) Michael Torrie <torriem@gmail.com> - 2015-02-28 22:05 -0700
          Re: suggestions for functional style (singleton pattern?) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-03-02 02:55 +1100
          Re: suggestions for functional style (singleton pattern?) Fabien <fabien.maussion@gmail.com> - 2015-03-02 11:19 +0100
            Re: suggestions for functional style (singleton pattern?) Mario Figueiredo <marfig@gmail.com> - 2015-03-02 11:31 +0100
            Re: suggestions for functional style (singleton pattern?) Michael Torrie <torriem@gmail.com> - 2015-03-02 08:59 -0700
            Re: suggestions for functional style (singleton pattern?) Ian Kelly <ian.g.kelly@gmail.com> - 2015-03-02 09:51 -0700
        Re: suggestions for functional style (singleton pattern?) Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2015-03-01 19:00 +1300
      Re: suggestions for functional style (singleton pattern?) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-03-02 02:04 +1100
        Re: suggestions for functional style (singleton pattern?) Mario Figueiredo <marfig@gmail.com> - 2015-03-01 19:20 +0100
    Re: suggestions for functional style (singleton pattern?) Paul Rubin <no.email@nospam.invalid> - 2015-02-28 22:23 -0800

#86637 — suggestions for functional style (singleton pattern?)

Fromyves@zioup.com
Date2015-02-28 16:12 -0700
Subjectsuggestions for functional style (singleton pattern?)
Message-ID<WXrIw.14066$uP4.9692@fx20.iad>
Hi,

For some scripts, I write in a a more functional way, using a lot of small
functions outside of any class. Although it makes the code clearer for
specific cases, I have found that it makes debugging and using the repl in
general difficult, as as I have to re-initialise every single objects every time.

I have now started to use some kind of state pattern to alleviate this, here's
a simplistic example:

https://github.com/dorfsmay/state_pattern_for_debugging_python/blob/master/dirstats.py

Are there better ways to address this? Any suggestion on this style?

Thanks.

--
http://yves.zioup.com
gpg: 4096R/32B0F416

[toc] | [next] | [standalone]


#86640

FromMichael Torrie <torriem@gmail.com>
Date2015-02-28 19:19 -0700
Message-ID<mailman.0.1425176390.29956.python-list@python.org>
In reply to#86637
On 02/28/2015 04:12 PM, yves@zioup.com wrote:
> For some scripts, I write in a a more functional way, using a lot of small
> functions outside of any class. Although it makes the code clearer for
> specific cases, I have found that it makes debugging and using the repl in
> general difficult, as as I have to re-initialise every single objects every time.
> 
> I have now started to use some kind of state pattern to alleviate this, here's
> a simplistic example:
> 
> https://github.com/dorfsmay/state_pattern_for_debugging_python/blob/master/dirstats.py
> 
> Are there better ways to address this? Any suggestion on this style?

You say you are trying to use a singleton pattern, but your code does
not appear to implement a singleton.  From what I can read of your code,
you really should just put all your functions as methods to the DirStat
class and call it good.  Especially if your module is going to be used
in several places potentially at the same time.

If what you want is a singleton, then that's what a module is already.
Module functions are singleton methods and module attributes maintain
state.  Just call your module dirstat and interface with it:

dirstat.opendir(**vars(args)
dirstat.print()

In this example, I really don't think the singleton is a good pattern,
though.

[toc] | [prev] | [next] | [standalone]


#86643

Fromyves@zioup.com
Date2015-02-28 21:11 -0700
Message-ID<ojwIw.1477738$W25.174147@fx02.iad>
In reply to#86640
On 2015-02-28 19:19, Michael Torrie wrote:
> You say you are trying to use a singleton pattern, but your code does
> not appear to implement a singleton.  From what I can read of your code,

I call it a singletone because I only every create one object.

I am not trying to use a singleton, I'm trying to avoid issues created by the
alternative ways of doing this (not having access to the variables in the repl
etc...).

> you really should just put all your functions as methods to the DirStat
> class and call it good.  Especially if your module is going to be used
> in several places potentially at the same time.

There are type of problems where I don't want to bind functions to data.

By the way, I have just added some more explanation in the code, trying to
clarify the problem.

--
http://yves.zioup.com
gpg: 4096R/32B0F416

[toc] | [prev] | [next] | [standalone]


#86646

FromMichael Torrie <torriem@gmail.com>
Date2015-02-28 22:14 -0700
Message-ID<mailman.3.1425186857.29956.python-list@python.org>
In reply to#86643
On 02/28/2015 09:11 PM, yves@zioup.com wrote:
> On 2015-02-28 19:19, Michael Torrie wrote:
>> You say you are trying to use a singleton pattern, but your code does
>> not appear to implement a singleton.  From what I can read of your code,
> 
> I call it a singletone because I only every create one object.
> 
> I am not trying to use a singleton, I'm trying to avoid issues created by the
> alternative ways of doing this (not having access to the variables in the repl
> etc...).
>
> There are type of problems where I don't want to bind functions to data.
>
> By the way, I have just added some more explanation in the code,
> trying to clarify the problem.

I suppose it's just a matter of style but I'd just have my utility
function return a dict of all the calculated values, rather than store
them in a utility class instance.  Then you wouldn't need an
intermediate object to hold state temporarily. The idea behind
functional programming in part is you can chain things together easily
should you need to, which your intermediate object cannot do.

But I do see kind of the pattern you are trying to employ here; I just
don't understand why it's required or what you'd use it for, given that
a function could just do all the calculations and return a list or dict
of all calculated the values.


[toc] | [prev] | [next] | [standalone]


#86642

FromMario Figueiredo <marfig@gmail.com>
Date2015-03-01 04:45 +0100
Message-ID<13v4falb4odnhtvss4qdatnn16sgiv5pgd@4ax.com>
In reply to#86637
On Sat, 28 Feb 2015 16:12:54 -0700, yves@zioup.com wrote:

>Hi,
>
>For some scripts, I write in a a more functional way, using a lot of small
>functions outside of any class. Although it makes the code clearer for
>specific cases, I have found that it makes debugging and using the repl in
>general difficult, as as I have to re-initialise every single objects every time.
>
>I have now started to use some kind of state pattern to alleviate this, here's
>a simplistic example:
>
>https://github.com/dorfsmay/state_pattern_for_debugging_python/blob/master/dirstats.py
>
>Are there better ways to address this? Any suggestion on this style?

(warning: Didn't bother prove-reading and spell chocking. Sorry...)

Problem 1:
With the exception of get_all_files(), all your functions do is to
call another standard function. This decreases performance
unnecessarily and may mislead the users of your API into thinking
these functions perform some different type of average or min/max
operations.

For a single call to your API, the performance issue isn't a problem.
But if some user wraps a DirStat instance in a loop, they may suffer
an unnecessary hit.

Meanwhile, If I see a calculate_average() or a find_smallest()
function in your API, I immediately deduce it must be doing something
different than a standard average or min/max operation. Otherwise why
would the API code bother about something I can easily do myself? And
when I go look in your code trying to understand why you are offering
me an average function, I'll become dissapointed.

Solution:
Do not create an interface for standard functions, unless you plan to
change how these functions work. Your desire to let your users know
that they can calculate an average from your API object will fall in
deaf ears because they can realize that from themselves. An API should
only expose unique methods or complex operations wrapped in one or two
methods.

If you are coding a method with a single line and that line starts
with return and is followed by a function call, you know you are
creating a redundant interface.

Problem 2:
No one likes long names. And if some user of your API is coding under
an 80 character per line limit, they'll hate you for it. Your
functions contain redundant terms that only increase the function name
size and do not offer any advantage in having a better grasp of what
the function does.

average() is better than calculate_average(), for instance.

Solution:
Always be careful with how you name your public interface elements.
These will define much of the 'personality' of your API.
calculate_average() makes for a tacky API. average() makes for a
pretty standard API.

Problem 3:
Modules are self-contained contextualized units that must contain only
code that fits in the namespace the module defines for itself. If this
module is called dirstats and is chiefed by a DirStats object that
manages a dictionary of files, a public calculate_average() function
or a min/max pair of functions simply don't fit within the module
context.

At the very least they should be moved inside the class so it becomes
clear to anyone caring that these functions really only matter in the
context of the DirStats object.

(But because of problem 1, these functions should not even exist and
instead be replaced by direct calls to the statistics module functions
inside the DirStats class.)

The get_all_files() function is an offender. It should be moved inside
the class and probably even be removed entirely. Since it apparently
has no other objective than to help the class __init__() method, the
function code just just be copied/pasted inside that method.

Solution:
Do not make a module a house of confusion. Each module should contain
only public functions and objects that belong with each other under
the module proposed context.

Also look carefully at what you are putting outside of a class. You
are making it a part of the public interface for that module. The only
reason to do so is if it can have some use outside the class
definition.

Problem 4:
You speak of a singleton. But you haven't implemented one. It is not
clear from your code if this class should be a singleton. I'm guessing
not. Singletons are in fact rare. Well, let me put it another way:
Good reasons to code a singleton are rare.

A good singleton, for instance could be the Map() object in a strategy
game, or the canonical Logging() class in most programs that implement
logging. These are really one-time objects that make sense to ever
always exist as just one instance.

Solution:
But, if you ever wish to implement a singleton, the following pattern
mostly works and is short and to the point:

    class Map():
        _instance = None

        def __new__(cls, *args, **kwargs):
            if Map._instance is None:
                Map._instance = super(Map, cls).__new__(cls)
            return Map._instance

    >>> a = Map()
    >>> b = Map()
    >>> a is b
    True

Just remember, for most singletons the truth is that what you think
you will only need 1 today, you will need 2 tomorrow.

Problem 5:
A pet peeve of mine. If you aren't going to use a variable, make that
explicit in your code by taking advatange of Python wonderful language
features.

In your get_all_files() function, 'dirs' is not used. So make that
explicit to you and anyone else reading your code, by using either the
underscore or double underscore variable naming convention.

Solution:
It goes like this:

    for root, __, files in os.walk(directory):
        for fn in files:
            abs_path = os.path.join(root, fn)
            all_files[abs_path] = os.path.getsize(abs_path)

....

So to finalize. How should your dirstats.py module probably look like?
Something like this:

import os
import inspect
import argparse
import operator
import statistics

class DirStat(object):
    def __init__(self, directory):
        self.all_files = dict()
        for root, dirs, files in os.walk(directory):
            for fn in files:
                abs_path = os.path.join(root, fn)
                self.all_files[abs_path] = os.path.getsize(abs_path)

        self.average = statistics.mean(self.all_files.values())
        self.median = statistics.median(self.all_files.values())
        self.largest = max(self.all_files, self.all_files.get)
        self.smallest = min(self.all_files, self.all_files.get)

    def print(self):
        print("mean: {:.2f}".format(self.average))
        print("median: {:.2f}".format(self.median))
        print("smallest file: {}".format(self.smallest))
        print("largest file: {}".format(self.largest))

    def __repr__(self):
        return self.__dict__.__repr__()


I also wanted to say something about your __repr__, but this post is
already too long.

[toc] | [prev] | [next] | [standalone]


#86644

Fromyves@zioup.com
Date2015-02-28 21:29 -0700
Message-ID<jAwIw.631640$rq4.16649@fx27.iad>
In reply to#86642
On 2015-02-28 20:45, Mario Figueiredo wrote:

> Problem 1:
> With the exception of get_all_files(), all your functions do is to
> call another standard function. This decreases performance
> unnecessarily and may mislead the users of your API into thinking
> these functions perform some different type of average or min/max
> operations.

I tried to create a simple example to make a point, I wouldn't create function
wrappers like this. I'll to think of a better example, this was to get the
discussion going.

> Problem 3:
> At the very least they should be moved inside the class so it becomes
> clear to anyone caring that these functions really only matter in the
> context of the DirStats object.

Trying to write in a more functional way, not OO here, the functions could be
used by more than one class. I understand OO, use it a lot, but sometimes try
to move beyond that.

> Problem 4:
> You speak of a singleton. But you haven't implemented one. It is not
> clear from your code if this class should be a singleton. I'm guessing
> not. Singletons are in fact rare. Well, let me put it another way:
> Good reasons to code a singleton are rare.

Thanks. I hadn't realise "singleton" meant a class built such that it could
not be instanciated more than once, I thought it corresponded to a pattern
where only one object is ever created from a given class.

> But, if you ever wish to implement a singleton, the following pattern
> mostly works and is short and to the point:
> 
>     class Map():
>         _instance = None
> 
>         def __new__(cls, *args, **kwargs):
>             if Map._instance is None:
>                 Map._instance = super(Map, cls).__new__(cls)
>             return Map._instance
> 
>     >>> a = Map()
>     >>> b = Map()
>     >>> a is b
>     True

Thanks.

> Problem 5:
> A pet peeve of mine. If you aren't going to use a variable, make that
> explicit in your code by taking advatange of Python wonderful language
> features.

Good point - thanks.

--
http://yves.zioup.com
gpg: 4096R/32B0F416

[toc] | [prev] | [next] | [standalone]


#86645

FromMichael Torrie <torriem@gmail.com>
Date2015-02-28 22:05 -0700
Message-ID<mailman.2.1425186322.29956.python-list@python.org>
In reply to#86644
On 02/28/2015 09:29 PM, yves@zioup.com wrote:
>> Problem 4:
>> You speak of a singleton. But you haven't implemented one. It is not
>> clear from your code if this class should be a singleton. I'm guessing
>> not. Singletons are in fact rare. Well, let me put it another way:
>> Good reasons to code a singleton are rare.
> 
> Thanks. I hadn't realise "singleton" meant a class built such that it could
> not be instanciated more than once, I thought it corresponded to a pattern
> where only one object is ever created from a given class.

Is not "only one object is ever created from a given class" the same as
"could not be [instantiated] more than once?"

To be clear, singletons are commonly used in Python every time you
import a module.  A module *is* a singleton pattern, particularly one
that maintains state.  I use sometimes use this feature for sharing
config and other data between other modules (global state when it's
required).

"import module" essentially does the one-time instantiation if it's a
new import; top-level code in the module is run once on first import,
where it acts like a constructor.  So it functions as a singleton.  As
near as I can tell there's very little reason to make a class-based
singleton in Python, since a module has the same effect and is cleaner.

[toc] | [prev] | [next] | [standalone]


#86656

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2015-03-02 02:55 +1100
Message-ID<54f33682$0$13008$c3e8da3$5496439d@news.astraweb.com>
In reply to#86645
Michael Torrie wrote:

> Is not "only one object is ever created from a given class" the same as
> "could not be [instantiated] more than once?"

Not really.

A class which you merely happen to only instantiate once is not a singleton
class. It's just a class that you instantiate N times, where so far N has
only equalled 1.

A singleton class on the other hand is a class where N *must* equal 1,
because the class itself enforces that rule.

More generally, N may equal some small value, e.g. a Boolean class might
have a fixed set of exactly 2 instances. Sometimes people try to invent
names like "doubleton" for these, but none of those names have really taken
off.

Python modules are an interesting case. Although we often call them
singletons, they're actually nothing like a singleton. Modules are
instances of ModuleType, and there is no upper limit to the number of
instances that can be created. It's not even true that Python enforces only
a single instance per module, there are ways to get around that (albeit
normally you don't want to get around it!). Here is just once way:


# Don't do this at home!
py> import sys
py> mysys = sys
py> del sys.modules['sys'], sys
py> import sys
py> sys is mysys
False


Nevertheless, modules can be used to implement similar semantics to those of
singleton classes, and in particular they provide the most important
features of singleton classes for free. But calling them singletons just
because they quack like singletons is a little weird. Next we'll be calling
the Borg pattern "singletons".




-- 
Steven

[toc] | [prev] | [next] | [standalone]


#86710

FromFabien <fabien.maussion@gmail.com>
Date2015-03-02 11:19 +0100
Message-ID<md1dep$s0t$1@speranza.aioe.org>
In reply to#86645
On 01.03.2015 06:05, Michael Torrie wrote:
>   A module*is*  a singleton pattern, particularly one
> that maintains state.  I use sometimes use this feature for sharing
> config and other data between other modules (global state when it's
> required).

I do this too, after some helping recommendations I got from this 
discussion group. I find it neat to use a module for sharing config 
states, but I always wondered: does this pattern fall into the "globals 
are devil" category?

Cheers,

Fabien

[toc] | [prev] | [next] | [standalone]


#86712

FromMario Figueiredo <marfig@gmail.com>
Date2015-03-02 11:31 +0100
Message-ID<spe8fat8dc2bsa5tki5al47ilvriil9bbl@4ax.com>
In reply to#86710
On Mon, 02 Mar 2015 11:19:06 +0100, Fabien <fabien.maussion@gmail.com>
wrote:

>On 01.03.2015 06:05, Michael Torrie wrote:
>>   A module*is*  a singleton pattern, particularly one
>> that maintains state.  I use sometimes use this feature for sharing
>> config and other data between other modules (global state when it's
>> required).
>
>I do this too, after some helping recommendations I got from this 
>discussion group. I find it neat to use a module for sharing config 
>states, but I always wondered: does this pattern fall into the "globals 
>are devil" category?
>

Global constants are conceptually different than global variables. It
is just that in Python there is no semantic diference between the two.
The former are OK and a useful pattern, whereas the latter are the
ones we call evil.

[toc] | [prev] | [next] | [standalone]


#86756

FromMichael Torrie <torriem@gmail.com>
Date2015-03-02 08:59 -0700
Message-ID<mailman.50.1425311965.13471.python-list@python.org>
In reply to#86710
On 03/02/2015 03:19 AM, Fabien wrote:
> On 01.03.2015 06:05, Michael Torrie wrote:
>>   A module*is*  a singleton pattern, particularly one
>> that maintains state.  I use sometimes use this feature for sharing
>> config and other data between other modules (global state when it's
>> required).
> 
> I do this too, after some helping recommendations I got from this 
> discussion group. I find it neat to use a module for sharing config 
> states, but I always wondered: does this pattern fall into the "globals 
> are devil" category?

In traditional languages, globals were considered evil in part because
of the ambiguity they raised (a problem of scope, really).  In a
function if you see a reference to a variable, and if globals were used,
you then have confusion over whether the variable is local or global
(could be both if one covers the other).  And globals could be set from
anywhere.  Debugging and logical patterns become a lot harder with this
ambiguity and errors are introduced.

Modules don't have the same problems when used as a store for globals
for several reasons.  First, because they have a namespace associated
with them.  You can tell at a glance which name in your function is a
reference to this "global" module.  Now if you did do a "from module
import *" then you can't tell as easily what is coming from the module
and what is locally defined.  But the cool thing here is if you rebind a
variable imported from a module in your local namespace, it actually
doesn't change the "global" variable (attribute).  Instead it just
rebinds your local name.  So if you want to change an attribute in the
module, you have to refer to it via the module.attribute path.

Secondly, one should rarely write to a module's namespace from outside
that module (though you can).  Instead treat the module in an OOP
fashion, and write business methods to alter the module's state in a
specific way.  This encapsulation protects and insulates the module's
users from implementation details that could change from time to time.
Now I don't know of any way of implementing class-style properties on a
module as you can do in a class, but if that were needed you would write
a standard class and instantiate a singleton inside the module itself
perhaps.

As with all broad statements, saying globals are evil is simply not
always true.

[toc] | [prev] | [next] | [standalone]


#86762

FromIan Kelly <ian.g.kelly@gmail.com>
Date2015-03-02 09:51 -0700
Message-ID<mailman.55.1425315154.13471.python-list@python.org>
In reply to#86710
On Mon, Mar 2, 2015 at 8:59 AM, Michael Torrie <torriem@gmail.com> wrote:
> Now I don't know of any way of implementing class-style properties on a
> module as you can do in a class, but if that were needed you would write
> a standard class and instantiate a singleton inside the module itself
> perhaps.

This works, although I'm not sure that I would do it in real code.

>>> class PropertyModule(types.ModuleType):
...     @property
...     def parrot(self):
...         return "Norwegian Blue"
...
>>> sys.modules['parrot_sketch'] = PropertyModule('parrot_sketch')
>>> import parrot_sketch
>>> parrot_sketch.parrot
'Norwegian Blue'

Note that if the user does "from parrot_sketch import parrot", then
the property is evaluated at import time and any changes won't be
reflected in the local binding, which might cause some surprises.

Also, this isn't really a singleton any longer since the user might
just create another instance of the class, but you can hide the class
and present just the module as the API.

[toc] | [prev] | [next] | [standalone]


#86647

FromGregory Ewing <greg.ewing@canterbury.ac.nz>
Date2015-03-01 19:00 +1300
Message-ID<clfo8jF8p24U1@mid.individual.net>
In reply to#86644
yves@zioup.com wrote:
> Thanks. I hadn't realise "singleton" meant a class built such that it could
> not be instanciated more than once,

It doesn't have to be, it could just be a class that you
refrain from instantiating more than once.

But as Michael Torrie said, it's rare to need to to that
in Python. Most of the time you're better off using a
module.

-- 
Greg

[toc] | [prev] | [next] | [standalone]


#86655

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2015-03-02 02:04 +1100
Message-ID<54f32a89$0$13007$c3e8da3$5496439d@news.astraweb.com>
In reply to#86642
Mario Figueiredo wrote:

> (warning: Didn't bother prove-reading and spell chocking. Sorry...)

Warning: didn't bother reading. Not sorry at all.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#86667

FromMario Figueiredo <marfig@gmail.com>
Date2015-03-01 19:20 +0100
Message-ID<kkl6fahg763n11kus42hj02pnau1h5bm11@4ax.com>
In reply to#86655
On Mon, 02 Mar 2015 02:04:40 +1100, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:

>Mario Figueiredo wrote:
>
>> (warning: Didn't bother prove-reading and spell chocking. Sorry...)
>
>Warning: didn't bother reading. Not sorry at all.

Ohh.. how hurtful of you.

It was 4:45 am when I posted that, after long hours trying to make
some code work on Erlang that needs to be ready by Monday. And only
because I wasn't seeing anyone replying to the OP request for help,
that was sitting there for quite a few hours while everyone else was
entertained discussing how many keywords are in a programming
language. After I replied, I went to bed. Exhausted and sleepy.

I'm not sorry you didn't read either, you pretentious prick. But I
find it funny you replied to say that.

[toc] | [prev] | [next] | [standalone]


#86648

FromPaul Rubin <no.email@nospam.invalid>
Date2015-02-28 22:23 -0800
Message-ID<874mq58ce1.fsf@jester.gateway.pace.com>
In reply to#86637
yves@zioup.com writes:
> Are there better ways to address this? Any suggestion on this style?

I guess I don't completely understand the question.  But one way to do a
singleton in python is put the initialization code into a separate
module.  Then the first time you import the module, the code runs, but
it doesn't re-run on subsequent imports.  

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web